diff options
author | Pablo Martín <caedesv@users.sourceforge.net> | 2003-09-07 21:39:37 +0000 |
---|---|---|
committer | Pablo Martín <caedesv@users.sourceforge.net> | 2003-09-07 21:39:37 +0000 |
commit | 599a8e20c02fa48bab5102d15fab79dd6b631c95 (patch) | |
tree | 80e8760e6a8fdc7d9144370dc569b38232b2b315 | |
parent | b509942daafa671a5b5ede267b6e786619ce8173 (diff) |
Updating to last version of pdp 0.12.2
svn path=/trunk/externals/pdp/; revision=940
34 files changed, 11107 insertions, 0 deletions
diff --git a/system/X11/Makefile b/system/X11/Makefile new file mode 100644 index 0000000..0001c49 --- /dev/null +++ b/system/X11/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = pdp_xwindow.o + + +include ../../Makefile.config + +all: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/X11/pdp_xwindow.c b/system/X11/pdp_xwindow.c new file mode 100644 index 0000000..563ee21 --- /dev/null +++ b/system/X11/pdp_xwindow.c @@ -0,0 +1,529 @@ +/* + * Pure Data Packet system module. - x window glue code (fairly tied to pd and pdp) + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +// this code is fairly tied to pd and pdp. serves mainly as reusable glue code +// for pdp_xv, pdp_glx, pdp_3d_windowcontext, ... + + +#include "pdp.h" +#include "pdp_xwindow.h" + +#define D if(0) + + +static void pdp_xwindow_overrideredirect(t_pdp_xwindow *xwin, int b) +{ + XSetWindowAttributes new_attr; + new_attr.override_redirect = b ? True : False; + XChangeWindowAttributes(xwin->dpy, xwin->win, CWOverrideRedirect, &new_attr); + //XFlush(xwin->dpy); + +} + + +void pdp_xwindow_moveresize(t_pdp_xwindow *xwin, int xoffset, int yoffset, int width, int height) +{ + + D post("_pdp_xwindow_moveresize"); + if ((width > 0) && (height > 0)){ + xwin->winwidth = width; + xwin->winheight = height; + xwin->winxoffset = xoffset; + xwin->winyoffset = yoffset; + + if (xwin->initialized){ + XMoveResizeWindow(xwin->dpy, xwin->win, xoffset, yoffset, width, height); + XFlush(xwin->dpy); + } + } +} + + +void pdp_xwindow_fullscreen(t_pdp_xwindow *xwin) +{ + XWindowAttributes rootwin_attr; + + D post("pdp_xwindow_fullscreen"); + + /* hmm.. fullscreen and xlib the big puzzle.. + if it looks like a hack it is a hack. */ + + if (xwin->initialized){ + + XGetWindowAttributes(xwin->dpy, RootWindow(xwin->dpy, xwin->screen), &rootwin_attr ); + + //pdp_xwindow_overrideredirect(xwin, 0); + pdp_xwindow_moveresize(xwin, 0, 0, rootwin_attr.width, rootwin_attr.height); + //pdp_xwindow_overrideredirect(xwin, 1); + //XRaiseWindow(xwin->dpy, xwin->win); + //pdp_xwindow_moveresize(xwin, 0, 0, rootwin_attr.width, rootwin_attr.height); + //pdp_xwindow_overrideredirect(xwin, 0); + + + + + } +} + + +void pdp_xwindow_tile(t_pdp_xwindow *xwin, int x_tiles, int y_tiles, int i, int j) +{ + XWindowAttributes rootwin_attr; + XSetWindowAttributes new_attr; + + D post("pdp_xwindow_fullscreen"); + + if (xwin->initialized){ + int tile_w; + int tile_h; + XGetWindowAttributes(xwin->dpy, RootWindow(xwin->dpy, xwin->screen), &rootwin_attr ); + + tile_w = rootwin_attr.width / x_tiles; + tile_h = rootwin_attr.height / y_tiles; + + xwin->winwidth = (x_tiles-1) ? rootwin_attr.width - (x_tiles-1)*tile_w : tile_w; + xwin->winheight = (y_tiles-1) ? rootwin_attr.height - (y_tiles-1)*tile_h : tile_h; + xwin->winxoffset = i * tile_w; + xwin->winyoffset = j * tile_h; + + //new_attr.override_redirect = True; + //XChangeWindowAttributes(xwin->dpy, xwin->win, CWOverrideRedirect, &new_attr ); + XMoveResizeWindow(xwin->dpy, xwin->win, xwin->winxoffset, xwin->winyoffset, xwin->winwidth, xwin->winheight); + + } +} + +/* resize window */ +void pdp_xwindow_resize(t_pdp_xwindow *xwin, int width, int height) +{ + D post("pdp_xwindow_resize"); + if ((width > 0) && (height > 0)){ + xwin->winwidth = width; + xwin->winheight = height; + if (xwin->initialized){ + XResizeWindow(xwin->dpy, xwin->win, width, height); + XFlush(xwin->dpy); + } + } + //_pdp_xwindow_moveresize(xwin, xwin->winxoffset, xwin->winyoffset, width, height); +} + +/* move window */ +void pdp_xwindow_move(t_pdp_xwindow *xwin, int xoffset, int yoffset) +{ + D post("pdp_xwindow_move"); + pdp_xwindow_moveresize(xwin, xoffset, yoffset, xwin->winwidth, xwin->winheight); +} + +/* send events to a pd outlet (don't call this outside the pd thread) */ +void pdp_xwindow_send_events(t_pdp_xwindow *xwin, t_outlet *outlet) +{ + + unsigned int i; + XEvent e; + XConfigureEvent *ce = (XConfigureEvent *)&e; + XButtonEvent *be = (XButtonEvent *)&e; + XMotionEvent *me = (XMotionEvent *)&e; + t_symbol *s; + t_atom atom[2]; + char nextdrag[]="drag0"; + char but='0'; + float inv_x = 1.0f / (float)(xwin->winwidth); + float inv_y = 1.0f / (float)(xwin->winheight); + int nbEvents = XEventsQueued(xwin->dpy, QueuedAlready); + + + +#if 0 + while (nbEvents && XPending(xwin->dpy)){ + XNextEvent(xwin->dpy, &e); + nbEvents--; + if (e.xany.window != xwin->win) { + XPutBackEvent(xwin->dpy, &e); + continue; + } +#else + + while (XPending(xwin->dpy)){ + XNextEvent(xwin->dpy, &e); + //if (e.xany.window != xwin->win) continue; +#endif + + switch(e.type){ + case ConfigureNotify: + xwin->winwidth = ce->width; + xwin->winheight = ce->height; + inv_x = 1.0f / (float)(xwin->winwidth); + inv_y = 1.0f / (float)(xwin->winheight); + break; + + case ClientMessage: + if ((Atom)e.xclient.data.l[0] == xwin->WM_DELETE_WINDOW) { + //post("pdp_xv: button disabled, please send a \"close\" message to close the window"); + //pdp_xv_kaboom(x); + //destroy = 1; + } + break; + + case ButtonPress: + //post("pdp_xv: press %f %f", inv_x * (float)be->x, inv_y * (float)be->y); + SETFLOAT(atom+0,inv_x * (float)be->x); + SETFLOAT(atom+1,inv_y * (float)be->y); + outlet_anything(outlet, gensym("press"), 2, atom); + switch(be->button){ + case Button1: outlet_anything(outlet, gensym("press1"), 2, atom); but='1'; break; + case Button2: outlet_anything(outlet, gensym("press2"), 2, atom); but='2'; break; + case Button3: outlet_anything(outlet, gensym("press3"), 2, atom); but='3'; break; + case Button4: outlet_anything(outlet, gensym("press4"), 2, atom); but='4'; break; + case Button5: outlet_anything(outlet, gensym("press5"), 2, atom); but='5'; break; + default: break; + } + nextdrag[4]=but; + xwin->dragbutton = gensym(nextdrag); + break; + + case ButtonRelease: + //post("pdp_xv: release %f %f", inv_x * (float)be->x, inv_y * (float)be->y); + SETFLOAT(atom+0,inv_x * (float)be->x); + SETFLOAT(atom+1,inv_y * (float)be->y); + outlet_anything(outlet, gensym("release"), 2, atom); + switch(be->button){ + case Button1: outlet_anything(outlet, gensym("release1"), 2, atom); break; + case Button2: outlet_anything(outlet, gensym("release2"), 2, atom); break; + case Button3: outlet_anything(outlet, gensym("release3"), 2, atom); break; + case Button4: outlet_anything(outlet, gensym("release4"), 2, atom); break; + case Button5: outlet_anything(outlet, gensym("release5"), 2, atom); break; + default: break; + } + + break; + case MotionNotify: + //post("pdp_xv: drag %f %f", inv_x * (float)be->x, inv_y * (float)be->y); + SETFLOAT(atom+0,inv_x * (float)be->x); + SETFLOAT(atom+1,inv_y * (float)be->y); + outlet_anything(outlet, gensym("drag"), 2, atom); + outlet_anything(outlet, xwin->dragbutton, 2, atom); + break; + + default: + //post("pdp_xv: unknown event"); + break; + } + + } + +} + + + +void pdp_xwindow_cursor(t_pdp_xwindow *xwin, t_floatarg f) +{ + if (!xwin->initialized) return; + + if (f == 0) { + static char data[] = {0}; + + Cursor cursor; + Pixmap blank; + XColor dummy; + + blank = XCreateBitmapFromData(xwin->dpy, xwin->win, data, 1, 1); + cursor = XCreatePixmapCursor(xwin->dpy, blank, blank, &dummy, + &dummy, 0, 0); + XFreePixmap(xwin->dpy, blank); + XDefineCursor(xwin->dpy, xwin->win,cursor); + } + else + XUndefineCursor(xwin->dpy, xwin->win); + + xwin->cursor = f; +} + +/* create xwindow */ +int pdp_xwindow_create_on_display(t_pdp_xwindow *xwin, Display *dpy) +{ + XEvent e; + unsigned int i; + + /* check if already opened */ + if( xwin->initialized ){ + post("pdp_xwindow_create_on_display: window already created"); + goto exit; + } + + xwin->dpy = dpy; + + /* create a window */ + xwin->screen = DefaultScreen(xwin->dpy); + + + xwin->win = XCreateSimpleWindow( + xwin->dpy, + RootWindow(xwin->dpy, xwin->screen), xwin->winxoffset, xwin->winyoffset, xwin->winwidth, xwin->winheight, 0, + BlackPixel(xwin->dpy, xwin->screen), + BlackPixel(xwin->dpy, xwin->screen)); + + + /* enable handling of close window event */ + xwin->WM_DELETE_WINDOW = XInternAtom(xwin->dpy, "WM_DELETE_WINDOW", True); + (void)XSetWMProtocols(xwin->dpy, xwin->win, &xwin->WM_DELETE_WINDOW, 1); + + if(!(xwin->win)){ + /* clean up mess */ + post("pdp_xwindow_create_on_display: could not create window. closing.\n"); + //XCloseDisplay(xwin->dpy); NOT OWNER + xwin->dpy = 0; + xwin->initialized = false; + goto exit; + } + + /* select input events */ + XSelectInput(xwin->dpy, xwin->win, StructureNotifyMask | ButtonPressMask | ButtonReleaseMask | MotionNotify | ButtonMotionMask); + //XSelectInput(xwin->dpy, xwin->win, StructureNotifyMask); + + + /* set window title */ + XStoreName(xwin->dpy, xwin->win, "pdp"); + + + /* map */ + XMapWindow(xwin->dpy, xwin->win); + + /* create graphics context */ + xwin->gc = XCreateGC(xwin->dpy, xwin->win, 0, 0); + + /* catch mapnotify */ + for(;;){ + XNextEvent(xwin->dpy, &e); + if (e.type == MapNotify) break; + } + + + /* we're done initializing */ + xwin->initialized = true; + + /* disable/enable cursor */ + pdp_xwindow_cursor(xwin, xwin->cursor); + + exit: + return xwin->initialized; + +} + +void pdp_xwindow_init(t_pdp_xwindow *xwin) +{ + xwin->dpy = 0; + xwin->screen = -1; + + xwin->winwidth = 320; + xwin->winheight = 240; + xwin->winxoffset = 0; + xwin->winyoffset = 0; + + xwin->initialized = 0; + + xwin->cursor = 0; + xwin->dragbutton = gensym("drag1"); + +} + + +void pdp_xwindow_close(t_pdp_xwindow *xwin) +{ + + XEvent e; + + if (xwin->initialized){ + XFreeGC(xwin->dpy, xwin->gc); + XDestroyWindow(xwin->dpy, xwin->win); + while(XPending(xwin->dpy)) XNextEvent(xwin->dpy, &e); + xwin->dpy = 0; + xwin->initialized = false; + } + +} + +void pdp_xwindow_free(t_pdp_xwindow *x) +{ + // close win + pdp_xwindow_close(x); + + // no more dynamic data to free +} + + + +// some more x specific stuff: XVideo + +static void pdp_xvideo_create_xvimage(t_pdp_xvideo *xvid, int width, int height) +{ + int i; + long size; + + //post("pdp_xvideo_create_xvimage"); + + xvid->width = width; + xvid->height = height; + size = (xvid->width * xvid->height + (((xvid->width>>1)*(xvid->height>>1))<<1)); + //post("create xvimage %d %d", xvid->width, xvid->height); + xvid->data = (unsigned char *)pdp_alloc(size); + for (i=0; i<size; i++) xvid->data[i] = i; + xvid->xvi = XvCreateImage(xvid->dpy, xvid->xv_port, xvid->xv_format, (char *)xvid->data, xvid->width, xvid->height); + xvid->last_encoding = -1; + if ((!xvid->xvi) || (!xvid->data)) post ("ERROR CREATING XVIMAGE"); + //post("created xvimag data:%x xvi:%x",xvid->data,xvid->xvi); + +} + +static void pdp_xvideo_destroy_xvimage(t_pdp_xvideo *xvid) +{ + if(xvid->data) pdp_dealloc(xvid->data); + if (xvid->xvi) XFree(xvid->xvi); + xvid->xvi = 0; + xvid->data = 0; +} + +void pdp_xvideo_display_packet(t_pdp_xvideo *xvid, t_pdp_xwindow *xwin, int packet) +{ + t_pdp *header = pdp_packet_header(packet); + void *data = pdp_packet_data(packet); + t_bitmap * bm = pdp_packet_bitmap_info(packet); + unsigned int width, height, encoding, size, nbpixels; + + /* some checks: only display when initialized and when pacet is bitmap YV12 */ + if (!xvid->initialized) return; + if (!header) return; + if (!bm) return; + + width = bm->width; + height = bm->height; + encoding = bm->encoding; + size = (width * height + (((width>>1)*(height>>1))<<1)); + nbpixels = width * height; + + if (PDP_BITMAP != header->type) return; + if (PDP_BITMAP_YV12 != encoding) return; + + /* check if xvimage needs to be recreated */ + if ((width != xvid->width) || (height != xvid->height)){ + //post("pdp_xv: replace image"); + pdp_xvideo_destroy_xvimage(xvid); + pdp_xvideo_create_xvimage(xvid, width, height); + } + + /* copy the data to the XvImage buffer */ + memcpy(xvid->data, data, size); + + /* display */ + XvPutImage(xvid->dpy,xvid->xv_port, xwin->win,xwin->gc,xvid->xvi, + 0,0,xvid->width,xvid->height, 0,0,xwin->winwidth,xwin->winheight); + XFlush(xvid->dpy); + + + +} + + + +void pdp_xvideo_close(t_pdp_xvideo* xvid) +{ + if (xvid->initialized){ + if (xvid->xvi) pdp_xvideo_destroy_xvimage(xvid); + XvUngrabPort(xvid->dpy, xvid->xv_port, CurrentTime); + xvid->xv_port = 0; + xvid->dpy = 0; + xvid->screen = -1; + xvid->last_encoding = -1; + xvid->initialized = false; + } +} + +void pdp_xvideo_free(t_pdp_xvideo* xvid) +{ + // close xvideo port (and delete XvImage) + pdp_xvideo_close(xvid); + + // no more dynamic data to free + +} + +void pdp_xvideo_init(t_pdp_xvideo *xvid) +{ + + xvid->dpy = 0; + xvid->screen = -1; + + xvid->xv_format = FOURCC_YV12; + xvid->xv_port = 0; + + xvid->width = 320; + xvid->height = 240; + + xvid->data = 0; + xvid->xvi = 0; + + xvid->initialized = 0; + xvid->last_encoding = -1; + +} + +int pdp_xvideo_open_on_display(t_pdp_xvideo *xvid, Display *dpy) +{ + unsigned int ver, rel, req, ev, err, i, j; + unsigned int adaptors; + int formats; + XvAdaptorInfo *ai; + + if (xvid->initialized) return 1; + if (!dpy) return 0; + xvid->dpy = dpy; + + if (Success != XvQueryExtension(xvid->dpy,&ver,&rel,&req,&ev,&err)) return 0; + + /* find + lock port */ + if (Success != XvQueryAdaptors(xvid->dpy,DefaultRootWindow(xvid->dpy),&adaptors,&ai)) + return 0; + for (i = 0; i < adaptors; i++) { + if ((ai[i].type & XvInputMask) && (ai[i].type & XvImageMask)) { + for (j=0; j < ai[i].num_ports; j++){ + if (Success != XvGrabPort(xvid->dpy,ai[i].base_id+j,CurrentTime)) { + //fprintf(stderr,"INFO: Xvideo port %ld on adapter %d: is busy, skipping\n",ai[i].base_id+j, i); + } + else { + xvid->xv_port = ai[i].base_id + j; + goto breakout; + } + } + } + } + + + breakout: + + XFree(ai); + if (0 == xvid->xv_port) return 0; + post("pdp_xvideo: grabbed port %d on adaptor %d", xvid->xv_port, i); + xvid->initialized = 1; + pdp_xvideo_create_xvimage(xvid, xvid->width, xvid->height); + return 1; +} + + diff --git a/system/image/Makefile b/system/image/Makefile new file mode 100644 index 0000000..f9ed52c --- /dev/null +++ b/system/image/Makefile @@ -0,0 +1,21 @@ +include ../../Makefile.config + +all: $(PDP_TARGET) + +OBJECTS = pdp_llconv.o pdp_resample.o pdp_imageproc_common.o + +OBJECTS_MMX = pdp_imageproc_mmx.o pdp_llconv_mmx.o +OBJECTS_PORTABLE = pdp_imageproc_portable.o pdp_llconv_portable.o + + + + +linux_mmx: $(OBJECTS_MMX) $(OBJECTS) + +linux: $(OBJECTS_PORTABLE) $(OBJECTS) + +darwin: $(OBJECTS_PORTABLE) $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/image/pdp_imageproc_common.c b/system/image/pdp_imageproc_common.c new file mode 100644 index 0000000..bc34b79 --- /dev/null +++ b/system/image/pdp_imageproc_common.c @@ -0,0 +1,596 @@ +/* + * Pure Data Packet. common image processing routines. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + This file contains common code for (portable) low level image processing objects + pdp_imageproc_* methods + The rest is int pdp_imageproc_<platform>.c + + There are also highlevel dispatcher methods that operate on packets: + pdp_imageproc_dispatch_* methods + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "pdp.h" + +#define CLAMP16(x) (((x) > 0x7fff) ? 0x7fff : (((x) < -0x7fff) ? -0x7fff : (x))) + +u32 pdp_imageproc_legalwidth(int i) +{ + if (i>1024) return 1024; + if (i>0) return ((((i-1)>>3)+1)<<3); + return 8; + +} + +u32 pdp_imageproc_legalheight(int i) +{ + if (i>1024) return 1024; + if (i>0) return ((((i-1)>>3)+1)<<3); + return 8; +} +u32 pdp_imageproc_legalwidth_round_down(int i) +{ + if (i>1024) return 1024; + if (i>8) return ((i>>3)<<3); + return 8; + +} + +u32 pdp_imageproc_legalheight_round_down(int i) +{ + if (i>1024) return 1024; + if (i>8) return ((i>>3)<<3); + return 8; +} + + + +/* some operations */ + +/* logic operators */ + +void pdp_imageproc_xor_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + u32 *plane = (u32 *)image; + u32 *plane2 = (u32 *)image2; + int count = (width * height) >> 1; + int i; + + for (i=0; i<count; i++){ + plane[i] ^= plane2[i]; + } +} + +void pdp_imageproc_and_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + u32 *plane = (u32 *)image; + u32 *plane2 = (u32 *)image2; + int count = (width * height) >> 1; + int i; + + for (i=0; i<count; i++){ + plane[i] &= plane2[i]; + } +} + +void pdp_imageproc_or_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + u32 *plane = (u32 *)image; + u32 *plane2 = (u32 *)image2; + int count = (width * height) >> 1; + int i; + + for (i=0; i<count; i++){ + plane[i] |= plane2[i]; + } +} + +void pdp_imageproc_not_process(void *x, u32 width, u32 height, s16 *image) +{ + u32 *plane = (u32 *)image; + int count = (width * height) >> 1; + int i; + + for (i=0; i<count; i++){ + plane[i] ^= 0xffffffff; + } +} + +void pdp_imageproc_mask_process(void *x, u32 width, u32 height, s16 *image) +{ + u32 mask = (u32)x; + u32 *plane = (u32 *)image; + int count = (width * height) >> 1; + int i; + + mask = (mask & 0xffff) | (mask << 16); + + for (i=0; i<count; i++){ + plane[i] &= mask; + } +} + +// produce a plasma image +// note: random number generator can be platform specific +// however, it should be seeded. (same seed produces the same result) + +typedef struct +{ + u32 seed; + s32 scale; +} t_plasma; + +static inline s16 _rand_s16(void) +{ + return (s16)(random()<<0); +} + +static inline s16 _new_color(s32 one, s32 two, s32 scale) +{ + return CLAMP16((one >> 1) + (two >> 1) + ((scale * _rand_s16()) >> 16)); + //return (one >> 1) + (two >> 1); +} + +void *pdp_imageproc_plasma_new(void){return pdp_alloc(sizeof(t_plasma));} +void pdp_imageproc_plasma_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_plasma_setseed(void *x, float seed) +{ + *((float *)x) = seed; +} +void pdp_imageproc_plasma_setturbulence(void *x, float f) +{ + ((t_plasma *)x)->scale = CLAMP16(f * ((float)0x7fff)); +} + +static void _plasma_subdiv(u32 w, u32 h, u32 s, s16 *image, int calc_left, int calc_top, s32 scale) +{ + int w0 = ((w-1)>>1); // width of left segments + int h0 = ((h-1)>>1); // heigth of top segments + int w1 = w - w0; + int h1 = h - h0; + + /* conditions: w0 <= w1, h0 <= h1 */ + + /* original coordinates */ + int topleft = 0; + int topright = w-1; + int bottomleft = s * (h-1); + int bottomright = bottomleft + topright; + + /* new subdivision coordinates */ + int top = w0; + int left = s * h0; + int bottom = bottomleft + w0; + int right = topright + left; + int center = left + top; + + if (w0 && h0){ /* left-right and top-bottom subdivide */ + + /* calculate corner pixel colours */ + if (calc_top) image[top] = _new_color(image[topleft], image[topright], scale); + if (calc_left) image[left] = _new_color(image[topleft], image[bottomleft], scale); + image[right] = _new_color(image[topright], image[bottomright], scale); + image[bottom] = _new_color(image[bottomleft], image[bottomright], scale); + image[center] = (_new_color(image[top], image[bottom], scale) >> 1) + +(_new_color(image[left], image[right], scale) >> 1); + + + /* subdivide (with overlap) */ + _plasma_subdiv(w0+1, h0+1, s, &image[topleft], 1, 1, scale); + _plasma_subdiv(w1, h0+1, s, &image[top], 0, 1, scale); + _plasma_subdiv(w0+1, h1, s, &image[left], 1, 0, scale); + _plasma_subdiv(w1, h1, s, &image[center], 0, 0, scale); + + } + + + else if(h0) { /* top-bottom subdivide */ + + //post("h:%d", h); + + /* calculate corner pixel colours */ + if(calc_left) image[left] = _new_color(image[topleft], image[bottomleft], scale); + image[right] = _new_color(image[topright], image[bottomright], scale); + + /* subdivide (without overlap) */ + _plasma_subdiv(w, h0+1, s, &image[topleft], 1, 0, scale); + _plasma_subdiv(w, h1, s, &image[left], 1, 0, scale); + + } + + else if (w0){ /* left-right subdivide */ + + /* calculate corner pixel colours */ + if (calc_top) image[top] = _new_color(image[topleft], image[topright], scale); + image[bottom] = _new_color(image[bottomleft], image[bottomright],scale); + + /* subdivide with overlap */ + _plasma_subdiv(w0+1, h, s, &image[topleft], 0, 1, scale); + _plasma_subdiv(w1, h, s, &image[top], 0, 1, scale); + + } + +} + +void pdp_imageproc_plasma_process(void *x, u32 width, u32 height, s16 *image) +{ + s32 scale = (((t_plasma *)x)->scale); + srandom (((t_plasma *)x)->seed); + + /* set initial border colours */ + image[0] = _rand_s16(); + image[width-1] = _rand_s16(); + image[width * (height-1)] = _rand_s16(); + image[width * height - 1] = _rand_s16(); + + /* subdivide */ + _plasma_subdiv(width, height, width, image, 1, 1, scale); + + ((t_plasma *)x)->seed = random(); + +} + + + +void pdp_imageproc_zero_process(void *x, u32 width, u32 height, s16 *image) +{ + int bytesize = (width * height) << 1; + memset(image, 0, bytesize); +} + +void pdp_imageproc_constant_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + u32 value = (u32)x; + u32 *plane = (u32 *)image; + int wordsize = (width * height) >> 1; + value = (value & 0xffff) | (value << 16); + for (i=0; i<wordsize; i++){ + plane[i] = value; + } +} + + +/* other stateless operators */ + +/* some 2x16bit vector ops */ + +/* some bit shuffling to ensure 32 bit accesses + get the sign bit extended as a mask: - : 0xffff +: 0x0000 */ +static inline u32 _sign(s32 invec) +{ + s32 mask_top = invec; + s32 mask_bot = invec; + + mask_top &= 0x80000000; /* isolate top sign bit */ + mask_bot <<= 16; /* shift bottom word to top word */ + mask_bot &= 0x80000000; /* isolate bottom sign bit */ + mask_top >>= 15; /* shift sign bit into top word */ + mask_bot >>= 15; + ((u32)mask_bot) >>=16; /* shift top word into bottom word */ + return mask_top |mask_bot; +} + +/* clear the least significant bit of the top word + to ensure a decoupled vector add */ +static inline void _decouple(s32 *invec) +{ + *invec &= 0xfffeffff; +} + +void pdp_imageproc_abs_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + s32 *wimage = (s32 *)image; + int wsize = (width * height) >> 1; + for (i=0; i<wsize; i++){ + /* this computes c = (c >= 0) ? (c) : (~c) */ + /* not is used instead of neg to prevent overflow on 0x8000 */ + /* this maps both 0 and -1 to 0 */ + + wimage[i] ^= _sign(wimage[i]); + + } +} + +void pdp_imageproc_zthresh_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + s32 *wimage = (s32 *)image; + int wsize = (width * height) >> 1; + for (i=0; i<wsize; i++){ + /* this computes c = (c >= 0) ? (c) : (0) */ + wimage[i] &= ~_sign(wimage[i]); + } +} + +/* hard thresholding: x contains a positive unsigned short int */ +void pdp_imageproc_hardthresh_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + s32 thresh = (s32)x; + s32 sign1, isign2, a; + s32 *wimage = (s32 *)image; + int wsize = (width * height) >> 1; + thresh |= (thresh << 16); + for (i=0; i<wsize; i++){ + a = wimage[i]; + sign1 = _sign(a); + a ^= sign1; /* take abs */ + _decouple(&a); + a -= thresh; /* subtract threshold */ + isign2 = ~ _sign(a); + a &= isign2; /* zero thresh */ + _decouple(&a); + a += thresh & isign2; /* add threshold (if not zero thresholded)*/ + a ^= sign1; + wimage[i] = a; + } +} + +/* soft thresholding: x contains a positive unsigned short int */ +void pdp_imageproc_softthresh_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + s32 thresh = (s32)x; + s32 sign1, sign2, a; + s32 *wimage = (s32 *)image; + int wsize = (width * height) >> 1; + thresh |= thresh << 16; + for (i=0; i<wsize; i++){ + a = wimage[i]; + sign1 = _sign(a); + a ^= sign1; /* take abs */ + _decouple(&a); + a -= thresh; /* subtract threshold */ + sign2 = _sign(a); + a &= ~ sign2; /* zero thresh */ + _decouple(&a); + //a += thresh; /* add threshold */ + a ^= sign1; + wimage[i] = a; + + } + +} + + +/* turns an image into a positive andmask */ +void pdp_imageproc_ispositive_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + s32 *wimage = (s32 *)image; + int wsize = (width * height) >> 1; + for (i=0; i<wsize; i++){ + wimage[i] = ~_sign(wimage[i]); + } + +} + +/* get sign */ +void pdp_imageproc_sign_process(void *x, u32 width, u32 height, s16 *image) +{ + int i; + s32 *wimage = (s32 *)image; + int wsize = (width * height) >> 1; + for (i=0; i<wsize; i++){ + wimage[i] = _sign(wimage[i]) ^ 0x7fff7fff; + } + +} + +/* flip left <-> right */ +void pdp_imageproc_flip_lr_process(void *dummy, u32 width, u32 height, s16 *image) +{ + u32 y; + s16 tmp, *l, *r; + for (y=0; y<height; y++){ + l = image; + r = image + width - 1; + while (l < r){ + tmp = *l; + *l = *r; + *r = tmp; + l++; + r--; + } + image += width; + } + +} + +void pdp_llconv_flip_top_bottom(s16 *data, int width, int height, int pixelsize); + +void pdp_imageproc_flip_tb_process(void *dummy, u32 width, u32 height, s16 *image) +{ + pdp_llconv_flip_top_bottom(image, width, height, 2); +} + + +/* image processing dispatcher methods */ +/* if the first packet contains a nonzero channel mask, it will be used instead + of the one supplied as argument to the dispatcher functions. + the packet's channel mask will be reset to 0 */ + +void pdp_imageproc_dispatch_1buf(void (*process_routine)(void*, u32, u32, s16*), void *x, u32 chanmask, int packet0) +{ + t_pdp *header0; + t_image *image0; + s16 *idata0; + unsigned int w,h,d,plane_size,mask; + + /* if packet is not a valid image return without doing anything */ + if (!(pdp_packet_image_isvalid(packet0))) return; + + header0 = pdp_packet_header(packet0); + image0 = pdp_packet_image_info(packet0); + idata0 = pdp_packet_data (packet0); + + w = image0->width; + h = image0->height; + d = image0->depth; + plane_size = w*h; + + if (image0->chanmask) chanmask = image0->chanmask; + image0->chanmask = 0; + + + switch(image0->encoding){ + case PDP_IMAGE_GREY: + if (chanmask & 1) (*process_routine)(x, w, h, idata0); + break; + case PDP_IMAGE_YV12: + if (chanmask & 1) (*process_routine)(x, w, h, idata0); + idata0 += plane_size; + plane_size >>= 2; + w >>= 1; + h >>= 1; + if (chanmask & 2) (*process_routine)(x, w, h, idata0); + idata0 += plane_size; + if (chanmask & 4) (*process_routine)(x, w, h, idata0); + break; + case PDP_IMAGE_MCHP: + mask = 1; + while (d--){ + if (chanmask & mask) (*process_routine)(x, w, h, idata0); + idata0 += plane_size; + mask <<= 1; + } + break; + default: + break; + } +} + + +void pdp_imageproc_dispatch_2buf(void (*process_routine)(void*, u32, u32, s16*, s16 *), void *x, u32 chanmask, int packet0, int packet1) +{ + t_pdp *header0; + t_image *image0; + s16 *idata0, *idata1; + unsigned int w,h,d,plane_size,mask; + + /* if packets are not compatible images, return without doing anything */ + if (!(pdp_packet_image_compat(packet0, packet1))) return; + + header0 = pdp_packet_header(packet0); + image0 = pdp_packet_image_info(packet0); + idata0 = pdp_packet_data (packet0); + idata1 = pdp_packet_data (packet1); + + w = image0->width; + h = image0->height; + d = image0->depth; + plane_size = w*h; + + if (image0->chanmask) chanmask = image0->chanmask; + image0->chanmask = 0; + + switch(image0->encoding){ + case PDP_IMAGE_GREY: + if (chanmask & 1) (*process_routine)(x, w, h, idata0, idata1); + break; + case PDP_IMAGE_YV12: + if (chanmask & 1) (*process_routine)(x, w, h, idata0, idata1); + idata0 += plane_size; + idata1 += plane_size; + plane_size >>= 2; + w >>= 1; + h >>= 1; + if (chanmask & 2) (*process_routine)(x, w, h, idata0, idata1); + idata0 += plane_size; + idata1 += plane_size; + if (chanmask & 4) (*process_routine)(x, w, h, idata0, idata1); + break; + case PDP_IMAGE_MCHP: + mask = 1; + while (d--){ + if (chanmask & mask) (*process_routine)(x, w, h, idata0, idata1); + idata0 += plane_size; + idata1 += plane_size; + mask <<= 1; + } + break; + default: + break; + } +} +void pdp_imageproc_dispatch_3buf(void (*process_routine)(void*, u32, u32, s16*, s16 *, s16 *), void *x, u32 chanmask, int packet0, int packet1, int packet2) +{ + t_pdp *header0; + t_image *image0; + s16 *idata0, *idata1, *idata2; + unsigned int w,h,d,plane_size, mask; + + /* if packets are not compatible images, return without doing anything */ + if (!((pdp_packet_image_compat(packet0, packet1)) + &&(pdp_packet_image_compat(packet0, packet1)))) return; + + header0 = pdp_packet_header(packet0); + image0 = pdp_packet_image_info(packet0); + idata0 = pdp_packet_data (packet0); + idata1 = pdp_packet_data (packet1); + idata2 = pdp_packet_data (packet2); + + w = image0->width; + h = image0->height; + d = image0->depth; + plane_size = w*h; + + if (image0->chanmask) chanmask = image0->chanmask; + image0->chanmask = 0; + + switch(image0->encoding){ + case PDP_IMAGE_GREY: + if (chanmask & 1)(*process_routine)(x, w, h, idata0, idata1, idata2); + break; + case PDP_IMAGE_YV12: + if (chanmask & 1)(*process_routine)(x, w, h, idata0, idata1, idata2); + idata0 += plane_size; + idata1 += plane_size; + idata2 += plane_size; + plane_size >>= 2; + w >>= 1; + h >>= 1; + if (chanmask & 2)(*process_routine)(x, w, h, idata0, idata1, idata2); + idata0 += plane_size; + idata1 += plane_size; + idata2 += plane_size; + if (chanmask & 4)(*process_routine)(x, w, h, idata0, idata1, idata2); + break; + case PDP_IMAGE_MCHP: + mask = 1; + while (d--){ + if (chanmask & mask) (*process_routine)(x, w, h, idata0, idata1, idata2); + idata0 += plane_size; + idata1 += plane_size; + idata2 += plane_size; + mask <<= 1; + } + break; + default: + break; + } +} diff --git a/system/image/pdp_imageproc_mmx.c b/system/image/pdp_imageproc_mmx.c new file mode 100644 index 0000000..b518aa2 --- /dev/null +++ b/system/image/pdp_imageproc_mmx.c @@ -0,0 +1,590 @@ +/* + * Pure Data Packet. c wrapper for mmx image processing routines. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this is a c wrapper around platform specific (mmx) code */ +#include <stdlib.h> +#include <math.h> +#include "pdp_mmx.h" +#include "pdp_imageproc.h" +#include "m_pd.h" + +/* pdp memory alloc/dealloc prototype */ +void *pdp_alloc(int size); +void pdp_dealloc(void *); + +// utility stuff +inline static s16 float2fixed(float f) +{ + if (f > 1) f = 1; + if (f < -1) f = -1; + f *= 0x7fff; + return (s16)f; +} + +inline static void setvec(s16 *v, float f) +{ + s16 a = float2fixed(f); + v[0] = a; + v[1] = a; + v[2] = a; + v[3] = a; +} + + + +// add two images +void pdp_imageproc_add_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + unsigned int totalnbpixels = width * height; + pixel_add_s16(image, image2, totalnbpixels>>2); +} + +// mul two images +void pdp_imageproc_mul_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + unsigned int totalnbpixels = width * height; + pixel_mul_s16(image, image2, totalnbpixels>>2); +} + +// mix 2 images +void *pdp_imageproc_mix_new(void){return pdp_alloc(8*sizeof(s16));} +void pdp_imageproc_mix_delete(void *x) {pdp_dealloc (x);} +void pdp_imageproc_mix_setleftgain(void *x, float gain){setvec((s16 *)x, gain);} +void pdp_imageproc_mix_setrightgain(void *x, float gain){setvec((s16 *)x + 4, gain);} +void pdp_imageproc_mix_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + s16 *d = (s16 *)x; + unsigned int totalnbpixels = width * height; + pixel_mix_s16(image, image2, totalnbpixels>>2, d, d+4); +} + + +// random mix 2 images +void *pdp_imageproc_randmix_new(void){return pdp_alloc(8*sizeof(s16));} +void pdp_imageproc_randmix_delete(void *x) {pdp_dealloc (x);} +void pdp_imageproc_randmix_setthreshold(void *x, float threshold){setvec((s16 *)x, 2*threshold-1);} +void pdp_imageproc_randmix_setseed(void *x, float seed) +{ + s16 *d = (s16 *)x; + srandom((u32)seed); + d[4] = (s16)random(); + d[5] = (s16)random(); + d[6] = (s16)random(); + d[7] = (s16)random(); + +} +void pdp_imageproc_randmix_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + s16 *d = (s16 *)x; + unsigned int totalnbpixels = width * height; + pixel_randmix_s16(image, image2, totalnbpixels>>2, d+4, d); +} + + +// 3x1 or 1x3 in place convolution +// orientation +typedef struct +{ + s16 min1[4]; + s16 zero[4]; + s16 plus1[4]; + s16 border[4]; + u32 orientation; + u32 nbpasses; +} t_conv; +void *pdp_imageproc_conv_new(void){return(pdp_alloc(sizeof(t_conv)));} +void pdp_imageproc_conv_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_conv_setmin1(void *x, float val){setvec(((t_conv *)x)->min1, val);} +void pdp_imageproc_conv_setzero(void *x, float val){setvec(((t_conv *)x)->zero, val);} +void pdp_imageproc_conv_setplus1(void *x, float val){setvec(((t_conv *)x)->plus1, val);} +void pdp_imageproc_conv_setbordercolor(void *x, float val){setvec(((t_conv *)x)->border, val);} +void pdp_imageproc_conv_setorientation(void *x, u32 val){((t_conv *)x)->orientation = val;} +void pdp_imageproc_conv_setnbpasses(void *x, u32 val){((t_conv *)x)->nbpasses = val;} +void pdp_imageproc_conv_process(void *x, u32 width, u32 height, s16 *image) + +{ + t_conv *d = (t_conv *)x; + + u32 orientation = d->orientation; + u32 nbp = d->nbpasses; + u32 i,j; + + if (orientation == PDP_IMAGEPROC_CONV_HORIZONTAL) + { + for(i=0; i<width*height; i+=width) + for (j=0; j<nbp; j++) + pixel_conv_hor_s16(image+i, width>>2, d->border, d->min1); + } + + else + { + for (j=0; j<nbp; j++) + for(i=0; i<width; i +=4) pixel_conv_ver_s16(image+i, height, width, d->border, d->min1); + } + + + +} + +// apply a gain to an image +void *pdp_imageproc_gain_new(void){return(pdp_alloc(8*sizeof(s16)));} +void pdp_imageproc_gain_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_gain_setgain(void *x, float gain) +{ + /* convert float to s16 + shift */ + s16 *d = (s16 *)x; + s16 g; + int i; + float sign; + int shift = 0; + + sign = (gain < 0) ? -1 : 1; + gain *= sign; + + /* max shift = 16 */ + for(i=0; i<=16; i++){ + if (gain < 0x4000){ + gain *= 2; + shift++; + } + else break; + } + + gain *= sign; + g = (s16) gain; + + //g = 0x4000; + //shift = 14; + + d[0]=g; + d[1]=g; + d[2]=g; + d[3]=g; + d[4]=(s16)shift; + d[5]=0; + d[6]=0; + d[7]=0; +} +void pdp_imageproc_gain_process(void *x, u32 width, u32 height, s16 *image) +{ + s16 *d = (s16 *)x; + pixel_gain_s16(image, (width*height)>>2, d, (u64 *)(d+4)); +} + +// colour rotation for 2 colour planes +void *pdp_imageproc_crot2d_new(void){return pdp_alloc(16*sizeof(s16));} +void pdp_imageproc_crot2d_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_crot2d_setmatrix(void *x, float *matrix) +{ + s16 *d = (s16 *)x; + setvec(d, matrix[0]); + setvec(d+4, matrix[1]); + setvec(d+8, matrix[2]); + setvec(d+12, matrix[3]); +} +void pdp_imageproc_crot2d_process(void *x, s16 *image, u32 width, u32 height) +{ + s16 *d = (s16 *)x; + pixel_crot2d_s16(image, width*height >> 2, d); +} + +// biquad and biquad time +typedef struct +{ + s16 ma1[4]; + s16 ma2[4]; + s16 b0[4]; + s16 b1[4]; + s16 b2[4]; + s16 u0[4]; + s16 u1[4]; + s16 u0_save[4]; + s16 u1_save[4]; + u32 nbpasses; + u32 direction; +} t_bq; + +void *pdp_imageproc_bq_new(void){return pdp_alloc(sizeof(t_bq));} +void pdp_imageproc_bq_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_bq_setcoef(void *x, float *coef) // a0,-a1,-a2,b0,b1,b2,u0,u1 +{ + t_bq *d = (t_bq *)x; + float ia0 = 1.0f / coef[0]; + + /* all coefs are s1.14 fixed point */ + /* representing values -2 < x < 2 */ + /* so scale down before using the ordinary s0.15 float->fixed routine */ + + ia0 *= 0.5f; + + // coef + setvec(d->ma1, ia0*coef[1]); + setvec(d->ma2, ia0*coef[2]); + setvec(d->b0, ia0*coef[3]); + setvec(d->b1, ia0*coef[4]); + setvec(d->b2, ia0*coef[5]); + + // state to reset too + setvec(d->u0_save, coef[6]); + setvec(d->u1_save, coef[7]); + +} +void pdp_imageproc_bq_setnbpasses(void *x, u32 nbpasses){((t_bq *)x)->nbpasses = nbpasses;} +void pdp_imageproc_bq_setdirection(void *x, u32 direction){((t_bq *)x)->direction = direction;} +void pdp_imageproc_bq_process(void *x, u32 width, u32 height, s16* image); + + +void pdp_imageproc_bqt_process(void *x, u32 width, u32 height, s16 *image, s16 *state0, s16 *state1) +{ + s16 *d = (s16 *)x; + pixel_biquad_time_s16(image, state0, state1, d, (width*height)>>2); +} + +void pdp_imageproc_bq_process(void *x, u32 width, u32 height, s16 *image) +{ + t_bq *d = (t_bq *)x; + s16 *c = d->ma1; /* coefs */ + s16 *s = d->u0; /* state */ + u32 direction = d->direction; + u32 nbp = d->nbpasses; + unsigned int i,j; + + + + /* VERTICAL */ + + if ((direction & PDP_IMAGEPROC_BIQUAD_TOP2BOTTOM) + && (direction & PDP_IMAGEPROC_BIQUAD_BOTTOM2TOP)){ + + for(i=0; i<width; i +=4){ + for (j=0; j<nbp; j++){ + pixel_biquad_vertb_s16(image+i, height>>2, width, c, s); + pixel_biquad_verbt_s16(image+i, height>>2, width, c, s); + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_TOP2BOTTOM){ + for(i=0; i<width; i +=4){ + for (j=0; j<nbp; j++){ + pixel_biquad_vertb_s16(image+i, height>>2, width, c, s); + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_BOTTOM2TOP){ + for(i=0; i<width; i +=4){ + for (j=0; j<nbp; j++){ + pixel_biquad_verbt_s16(image+i, height>>2, width, c, s); + } + } + } + + /* HORIZONTAL */ + + if ((direction & PDP_IMAGEPROC_BIQUAD_LEFT2RIGHT) + && (direction & PDP_IMAGEPROC_BIQUAD_RIGHT2LEFT)){ + + for(i=0; i<(width*height); i +=(width<<2)){ + for (j=0; j<nbp; j++){ + pixel_biquad_horlr_s16(image+i, width>>2, width, c, s); + pixel_biquad_horrl_s16(image+i, width>>2, width, c, s); + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_LEFT2RIGHT){ + for(i=0; i<(width*height); i +=(width<<2)){ + for (j=0; j<nbp; j++){ + pixel_biquad_horlr_s16(image+i, width>>2, width, c, s); + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_RIGHT2LEFT){ + for(i=0; i<(width*height); i +=(width<<2)){ + for (j=0; j<nbp; j++){ + pixel_biquad_horrl_s16(image+i, width>>2, width, c, s); + } + } + } + +} + +// produce a random image +// note: random number generator can be platform specific +// however, it should be seeded. (same seed produces the same result) +void *pdp_imageproc_random_new(void){return pdp_alloc(4*sizeof(s16));} +void pdp_imageproc_random_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_random_setseed(void *x, float seed) +{ + s16 *d = (s16 *)x; + srandom((u32)seed); + d[0] = (s16)random(); + d[1] = (s16)random(); + d[2] = (s16)random(); + d[3] = (s16)random(); + +} +void pdp_imageproc_random_process(void *x, u32 width, u32 height, s16 *image) +{ + s16 *d = (s16 *)x; + unsigned int totalnbpixels = width * height; + pixel_rand_s16(image, totalnbpixels>>2, d); +} + + +/* resampling stuff + this is quite a zoo of data structures + the major point is this: the resampler mmx code is shared for all resampling code + it uses data specified in t_resample_cbrd (Cooked Bilinear Resampler Data) + + then the there are several feeder algorithms. one is the linear mapper. it's + data is specified in t_resample_clrd (Cooked Linear Remapper Data) + + for each feeder algorithm, there are several high level algorithms. like zoom, + rotate, ... +*/ + +typedef struct +{ + u32 lineoffset; + s16 *image; + u32 width; + u32 height; + +} t_resample_id; // Image Data + +/* initialize image meta data (dimensions + location) */ +static void pdp_imageproc_resample_init_id(t_resample_id *x, u32 offset, s16* image, u32 w, u32 h) +{ + x->lineoffset = offset; + x->image = image; + x->width = w; + x->height = h; +} + +// mmx resampling source image resampling data + coefs +typedef struct +{ + // vector data for resampling routine (resampling computation) + u8 reserved[0x60]; //internal data + s16 *address[2]; //64 bit splatted offset address + s16 twowidthm1[4]; //64 bit splatted 2*(width-1) + s16 twoheightm1[4]; //64 bit splatted 2*(height-1) + s16 lineoffset[4]; //64 bit splatted line offset in pixels + +} t_resample_cid; // Cooked Image Data + +/* convert image meta data into a cooked format used by the resampler routine */ +static void pdp_imageproc_resample_init_cid(t_resample_cid *r, t_resample_id *i) +{ + u32 twowm1 = (i->width-1)<<1; + u32 twohm1 = (i->height-1)<<1; + r->address[0] = i->image; + r->address[1] = i->image; + r->twowidthm1[0] = twowm1; + r->twowidthm1[1] = twowm1; + r->twowidthm1[2] = twowm1; + r->twowidthm1[3] = twowm1; + r->twoheightm1[0] = twohm1; + r->twoheightm1[1] = twohm1; + r->twoheightm1[2] = twohm1; + r->twoheightm1[3] = twohm1; + r->lineoffset[0] = i->lineoffset; + r->lineoffset[1] = i->lineoffset; + r->lineoffset[2] = i->lineoffset; + r->lineoffset[3] = i->lineoffset; +} + +// linear mapping data struct (zoom, scale, rotate, shear, ...) +typedef struct +{ + s32 rowstatex[2]; // row state x coord + s32 rowstatey[2]; // row state y coord + s32 colstatex[2]; // column state x coord + s32 colstatey[2]; // column state y coord + s32 rowincx[2]; // row inc vector x coord + s32 rowincy[2]; // row inc vector y coord + s32 colincx[2]; // column inc vector x coord + s32 colincy[2]; // column inc vector y coord +} t_resample_clmd; // Cooked Linear Mapping Data + +/* convert incremental linear remapping vectors to internal cooked format */ +static void pdp_imageproc_resample_cookedlinmap_init(t_resample_clmd *l, s32 sx, s32 sy, s32 rix, s32 riy, s32 cix, s32 ciy) +{ + l->colstatex[0] = l->rowstatex[0] = sx; + l->colstatex[1] = l->rowstatex[1] = sx + rix; + l->colstatey[0] = l->rowstatey[0] = sy; + l->colstatey[1] = l->rowstatey[1] = sy + riy; + l->rowincx[0] = rix << 1; + l->rowincx[1] = rix << 1; + l->rowincy[0] = riy << 1; + l->rowincy[1] = riy << 1; + l->colincx[0] = cix; + l->colincx[1] = cix; + l->colincy[0] = ciy; + l->colincy[1] = ciy; +} + + +/* this struct contains all the data necessary for + bilin interpolation from src -> dst image + (src can be == dst) */ +typedef struct +{ + t_resample_cid csrc; //cooked src image meta data for bilinear interpolator + t_resample_id src; //src image meta + t_resample_id dst; //dst image meta +} t_resample_cbrd; //Bilinear Resampler Data + + +/* this struct contains high level zoom parameters, + all image relative */ +typedef struct +{ + float centerx; + float centery; + float zoomx; + float zoomy; + float angle; +} t_resample_zrd; + + +/* convert floating point center and zoom data to incremental linear remapping vectors */ +static void pdp_imageproc_resample_clmd_init_from_id_zrd(t_resample_clmd *l, t_resample_id *i, t_resample_zrd *z) +{ + double izx = 1.0f / (z->zoomx); + double izy = 1.0f / (z->zoomy); + double scale = (double)0xffffffff; + double scalew = scale / ((double)(i->width - 1)); + double scaleh = scale / ((double)(i->height - 1)); + double cx = ((double)z->centerx) * ((double)(i->width - 1)); + double cy = ((double)z->centery) * ((double)(i->height - 1)); + double angle = z->angle * (-M_PI / 180.0); + double c = cos(angle); + double s = sin(angle); + + /* affine x, y mappings in screen coordinates */ + double mapx(double x, double y){return cx + izx * ( c * (x-cx) + s * (y-cy));} + double mapy(double x, double y){return cy + izy * (-s * (x-cx) + c * (y-cy));} + + u32 tl_x = (u32)(scalew * mapx(0,0)); + u32 tl_y = (u32)(scaleh * mapy(0,0)); + + + u32 row_inc_x = (u32)(scalew * (mapx(1,0)-mapx(0,0))); + u32 row_inc_y = (u32)(scaleh * (mapy(1,0)-mapy(0,0))); + u32 col_inc_x = (u32)(scalew * (mapx(0,1)-mapx(0,0))); + u32 col_inc_y = (u32)(scaleh * (mapy(0,1)-mapy(0,0))); + + + pdp_imageproc_resample_cookedlinmap_init(l, tl_x, tl_y, row_inc_x, row_inc_y, col_inc_x, col_inc_y); +} + +/* this struct contains all data for the zoom object */ +typedef struct +{ + t_resample_cbrd cbrd; // Bilinear Resampler Data + t_resample_clmd clmd; // Cooked Linear Mapping data + t_resample_zrd zrd; // Zoom / Rotate Data +} t_resample_zoom_rotate; + +// zoom + rotate +void *pdp_imageproc_resample_affinemap_new(void) +{ + t_resample_zoom_rotate *z = (t_resample_zoom_rotate *)pdp_alloc(sizeof(t_resample_zoom_rotate)); + z->zrd.centerx = 0.5; + z->zrd.centery = 0.5; + z->zrd.zoomx = 1.0; + z->zrd.zoomy = 1.0; + z->zrd.angle = 0.0f; + return (void *)z; +} +void pdp_imageproc_resample_affinemap_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_resample_affinemap_setcenterx(void *x, float f){((t_resample_zoom_rotate *)x)->zrd.centerx = f;} +void pdp_imageproc_resample_affinemap_setcentery(void *x, float f){((t_resample_zoom_rotate *)x)->zrd.centery = f;} +void pdp_imageproc_resample_affinemap_setzoomx(void *x, float f){((t_resample_zoom_rotate *)x)->zrd.zoomx = f;} +void pdp_imageproc_resample_affinemap_setzoomy(void *x, float f){((t_resample_zoom_rotate *)x)->zrd.zoomy = f;} +void pdp_imageproc_resample_affinemap_setangle(void *x, float f){((t_resample_zoom_rotate *)x)->zrd.angle = f;} +void pdp_imageproc_resample_affinemap_process(void *x, u32 width, u32 height, s16 *srcimage, s16 *dstimage) +{ + t_resample_zoom_rotate *z = (t_resample_zoom_rotate *)x; + + /* setup resampler image meta data */ + pdp_imageproc_resample_init_id(&(z->cbrd.src), width, srcimage, width, height); + pdp_imageproc_resample_init_id(&(z->cbrd.dst), width, dstimage, width, height); + pdp_imageproc_resample_init_cid(&(z->cbrd.csrc),&(z->cbrd.src)); + + /* setup linmap data from zoom_rotate parameters */ + pdp_imageproc_resample_clmd_init_from_id_zrd(&(z->clmd), &(z->cbrd.src), &(z->zrd)); + + + /* call assembler routine */ + pixel_resample_linmap_s16(z); +} + + + +// polynomials + + +typedef struct +{ + u32 order; + u32 nbpasses; + s16 coefs[0]; +} t_cheby; + +void *pdp_imageproc_cheby_new(int order) +{ + t_cheby *z; + int i; + if (order < 2) order = 2; + z = (t_cheby *)pdp_alloc(sizeof(t_cheby) + (order + 1) * sizeof(s16[4])); + z->order = order; + setvec(z->coefs + 0*4, 0); + setvec(z->coefs + 1*4, 0.25); + for (i=2; i<=order; i++) setvec(z->coefs + i*4, 0); + + return z; +} +void pdp_imageproc_cheby_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_cheby_setcoef(void *x, u32 n, float f) +{ + t_cheby *z = (t_cheby *)x; + if (n <= z->order){ + setvec(z->coefs + n*4, f * 0.25); // coefs are in s2.13 format + } +} +void pdp_imageproc_cheby_setnbpasses(void *x, u32 n){((t_cheby *)x)->nbpasses = n;} + +void pdp_imageproc_cheby_process(void *x, u32 width, u32 height, s16 *image) +{ + t_cheby *z = (t_cheby *)x; + u32 iterations = z->nbpasses; + u32 i,j; + for (j=0; j < (height*width); j += width) + for (i=0; i<iterations; i++) + pixel_cheby_s16_3plus(image+j, width>>2, z->order+1, z->coefs); + + //pixel_cheby_s16_3plus(image, (width*height)>>2, z->order+1, z->coefs); +} diff --git a/system/image/pdp_imageproc_portable.c b/system/image/pdp_imageproc_portable.c new file mode 100644 index 0000000..53f195f --- /dev/null +++ b/system/image/pdp_imageproc_portable.c @@ -0,0 +1,679 @@ +/* + * Pure Data Packet. portable image processing routines. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include "pdp_imageproc.h" + +/* pdp memory alloc/dealloc prototype */ +void *pdp_alloc(int size); +void pdp_dealloc(void *); + + +// utility stuff +inline static s32 float2fixed(float f) +{ + if (f > 1) f = 1; + if (f < -1) f = -1; + f *= 0x7fff; + return (s32)f; +} + + + +#define CLAMP16(x) (((x) > 0x7fff) ? 0x7fff : (((x) < -0x7fff) ? -0x7fff : (x))) + +// add two images +void pdp_imageproc_add_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + int a, b; + unsigned int i; + for (i=0; i<width*height; i++){ + a = (int)image[i]; + b = (int)image2[i]; + image[i] = (s16)(CLAMP16(a+b)); + } + +} + +// mul two images +void pdp_imageproc_mul_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + int a, b; + unsigned int i; + for (i=0; i<width*height; i++){ + a = (int)image[i]; + b = (int)image2[i]; + image[i] = (s16)((a*b)>>15); + } + +} + +// mix 2 images +void *pdp_imageproc_mix_new(void){return pdp_alloc(2*sizeof(s32));} +void pdp_imageproc_mix_delete(void *x) {pdp_dealloc (x);} +void pdp_imageproc_mix_setleftgain(void *x, float gain) +{ + s32 *d = (s32 *)x; + d[0] = float2fixed(gain); +} +void pdp_imageproc_mix_setrightgain(void *x, float gain) +{ + s32 *d = (s32 *)x; + d[1] = float2fixed(gain); +} +void pdp_imageproc_mix_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + s32 *d = (s32 *)x; + u32 i; + s32 a,b; + + for(i=0; i<width*height; i++){ + a = (s32)image[i]; + b = (s32)image2[i]; + a = (a*d[0] + b*d[1]) >> 15; + image[i] = (s16)CLAMP16(a); + } + +} + + +// random mix 2 images +void *pdp_imageproc_randmix_new(void){return pdp_alloc(2*sizeof(s32));;} +void pdp_imageproc_randmix_delete(void *x) {pdp_dealloc(x);} +void pdp_imageproc_randmix_setthreshold(void *x, float threshold) +{ + s32 *d = (s32 *)x; + if (threshold > 1.0f) threshold = 1.0f; + if (threshold < 0.0f) threshold = 0.0f; + d[0] = float2fixed(threshold); +} +void pdp_imageproc_randmix_setseed(void *x, float seed) +{ + s32 *d = (s32 *)x; + d[1] = float2fixed(seed); +} +void pdp_imageproc_randmix_process(void *x, u32 width, u32 height, s16 *image, s16 *image2) +{ + s32 *d = (s32 *)x; + u32 i; + s16 r; + srandom((u32)d[1]); + + + for(i=0; i<width*height; i++){ + // get a random val between 0 and 0x7fff + r = (s16)(random() & 0x7fff); + if (r < d[0]) image[i] = image2[i]; + } +} + + +// 3x1 or 1x3 in place convolution +// orientation +void *pdp_imageproc_conv_new(void){return(pdp_alloc(6*sizeof(s32)));} +void pdp_imageproc_conv_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_conv_setmin1(void *x, float val) +{ + s32 *d = (s32 *)x; + d[0] = float2fixed(val); +} +void pdp_imageproc_conv_setzero(void *x, float val) +{ + s32 *d = (s32 *)x; + d[1] = float2fixed(val); +} +void pdp_imageproc_conv_setplus1(void *x, float val) +{ + s32 *d = (s32 *)x; + d[2] = float2fixed(val); +} +void pdp_imageproc_conv_setbordercolor(void *x, float val) +{ + s32 *d = (s32 *)x; + d[3] = float2fixed(val); +} +void pdp_imageproc_conv_setorientation(void *x, u32 val){((u32 *)x)[4] = val;} +void pdp_imageproc_conv_setnbpasses(void *x, u32 val){((u32 *)x)[5] = val;} + +static inline void pdp_imageproc_conv_scanline(void *x, s16 *data, u32 count, s32 stride) +{ + s32 *d = (s32 *)x; + s32 a,b,c,r; + u32 i; + + a = d[3]; //border + b = data[0]; + c = data[stride]; + + for(i = 0; i < count-2; i++){ + r = a*d[0] + b*d[1] + c*d[2]; + a = data[0]; + b = data[stride]; + c = data[stride<<1]; + data[0] = (s16)CLAMP16(r>>15); + data += stride; + } + r = a*d[0] + b*d[1] + c*d[2]; + a = data[0]; + b = data[stride]; + c = d[3]; //border + data[0] = (s16)CLAMP16(r>>15); + r = a*d[0] + b*d[1] + c*d[2]; + data[stride] = (s16)CLAMP16(r>>15); + +} + +void pdp_imageproc_conv_process(void *x, u32 width, u32 height, s16 *image) +{ + s32 *d = (s32 *)x; + u32 i, j; + u32 orientation = d[4]; + u32 nbp = d[5]; + if (orientation == PDP_IMAGEPROC_CONV_HORIZONTAL){ + for(i=0; i<width*height; i+=width) + for(j=0; j<nbp; j++) + pdp_imageproc_conv_scanline(x, image+i, width, 1); + + } + + if (orientation == PDP_IMAGEPROC_CONV_VERTICAL){ + for(i=0; i<width; i++) + for(j=0; j<nbp; j++) + pdp_imageproc_conv_scanline(x, image+i, height, width); + + } + + + + +} + +// apply a gain to an image +void *pdp_imageproc_gain_new(void){return(pdp_alloc(2*sizeof(s32)));} +void pdp_imageproc_gain_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_gain_setgain(void *x, float gain) +{ + /* convert float to s16 + shift */ + s32 *d = (s32 *)x; + s32 g; + int i; + float sign; + s32 shift = 0; + + sign = (gain < 0) ? -1 : 1; + gain *= sign; + + /* max shift = 16 */ + for(i=0; i<=16; i++){ + if (gain < 0x4000){ + gain *= 2; + shift++; + } + else break; + } + + gain *= sign; + g = (s32) gain; + + //g = 0x4000; + //shift = 14; + + d[0]=g; + d[1]=shift; +} +void pdp_imageproc_gain_process(void *x, u32 width, u32 height, s16 *image) +{ + s32 *d = (s32 *)x; + s32 a; + u32 i; + for (i=0; i<width*height; i++){ + a = (s32)image[i]; + image[i] = (s16)(CLAMP16((a * d[0]) >> d[1])); + } +} + +// colour rotation for 2 colour planes +void *pdp_imageproc_crot2d_new(void){return pdp_alloc(4*sizeof(s32));} +void pdp_imageproc_crot2d_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_crot2d_setmatrix(void *x, float *matrix) +{ + s32 *d = (s32 *)x; + d[0] = float2fixed(matrix[0]); + d[1] = float2fixed(matrix[1]); + d[2] = float2fixed(matrix[2]); + d[3] = float2fixed(matrix[3]); + +} +void pdp_imageproc_crot2d_process(void *x, s16 *image, u32 width, u32 height) +{ + s32 *d = (s32 *)x; + u32 i,j; + s32 a1,a2,c1,c2; + + for(i=0, j=width*height; i<width*height; i++, j++){ + c1 = (s32)image[i]; + c2 = (s32)image[j]; + + a1 = d[0] * c1; + a2 = d[1] * c1; + a1+= d[2] * c2; + a2+= d[3] * c2; + + a1 >>= 15; + a2 >>= 15; + + image[i] = (s16)CLAMP16(a1); + image[j] = (s16)CLAMP16(a2); + } +} + +// biquad and biquad time +typedef struct +{ + s32 ma1; + s32 ma2; + s32 b0; + s32 b1; + s32 b2; + + s32 u0; + s32 u1; + + s32 u0_save; + s32 u1_save; + + u32 nbpasses; + u32 direction; +} t_bq; +void *pdp_imageproc_bq_new(void){return pdp_alloc(sizeof(t_bq));} +void pdp_imageproc_bq_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_bq_setnbpasses(void *x, u32 i){((t_bq *)x)->nbpasses = i;} +void pdp_imageproc_bq_setdirection(void *x, u32 i){((t_bq *)x)->direction = i;} +void pdp_imageproc_bq_setcoef(void *x, float *coef) // a0,-a1,-a2,b0,b1,b2,u0,u1 +{ + s32 *d = (s32 *)x; + float ia0 = 1.0f / coef[0]; + + /* all coefs are s1.14 fixed point */ + /* representing values -2 < x < 2 */ + /* so scale down before using the ordinary s0.15 float->fixed routine */ + + ia0 *= 0.5f; + + // coef + d[0] = float2fixed(ia0*coef[1]); // -a1 + d[1] = float2fixed(ia0*coef[2]); // -a2 + d[2] = float2fixed(ia0*coef[3]); // b0 + d[3] = float2fixed(ia0*coef[4]); // b1 + d[4] = float2fixed(ia0*coef[5]); // b2 + + + // state to reset too + d[5] = float2fixed(coef[6]); + d[6] = float2fixed(coef[7]); + +} + +#define A1 d[0] +#define A2 d[1] +#define B0 d[2] +#define B1 d[3] +#define B2 d[4] +/* + # DIRECT FORM II BIQUAD (from pixel_biquad_s16.s) + # + # y[k] = b0 * x[k] + u1[k-1] + # u1[k] = b1 * x[k] + u2[k-1] - a1 * y[k] + # u2[k] = b2 * x[k] - a2 * y[k] +*/ + +/* remark A1 and A2 are already negated) */ + + +static inline void pdp_imageproc_bq_scanline(void *x, s16 *data, u32 count, s32 stride) +{ + + s32 *d = (s32 *)x; + s32 u1,u2, xx, yy; + + u32 i; + + u1 = d[7]; + u2 = d[8]; + + for(i = 0; i < count; i++){ + + xx = (s32)data[0]; + + yy = ((B0 * xx)>>14) + u1; + u1 = ((B1 * xx)>>14) + u2 + ((A1 * yy)>>14); + u2 = ((B2 * xx)>>14) + ((A2 * yy)>>14); + + data[0] = (s16)CLAMP16(yy); + + data += stride; + + } + + d[7] = u1; + d[8] = u2; + +} + +void pdp_imageproc_bqt_process(void *x, u32 width, u32 height, s16 *image, s16 *state1, s16 *state2) +{ + s32 *d = (s32 *)x; + u32 i; + s32 u1, u2, xx, yy; + + for (i=0; i<width*height; i++){ + + xx = (s32)image[i]; + u1 = (s32)state1[i]; + u2 = (s32)state2[i]; + + yy = ((B0 * xx)>>14) + u1; + u1 = ((B1 * xx)>>14) + u2 + ((A1 * yy)>>14); + u2 = ((B2 * xx)>>14) + ((A2 * yy)>>14); + + image[i] = (s16)CLAMP16(yy); + state1[i] = (s16)CLAMP16(u1); + state2[i] = (s16)CLAMP16(u2); + } + + +} + +void pdp_imageproc_bq_process(void *x, u32 width, u32 height, s16 *data) +{ + s32 *d = (s32 *)x; + + u32 nbp = d[9]; + u32 direction = d[10]; + unsigned int i,j, offset; + + + /* VERTICAL */ + offset = (height-1)*width; + + if ((direction & PDP_IMAGEPROC_BIQUAD_TOP2BOTTOM) + && (direction & PDP_IMAGEPROC_BIQUAD_BOTTOM2TOP)){ + + for(i=0; i<width; i++){ + for (j=0; j<nbp; j++){ + pdp_imageproc_bq_scanline(x, data+i, height, width); //T->B + pdp_imageproc_bq_scanline(x, data+offset+i, height, -width); //B->T + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_TOP2BOTTOM){ + for(i=0; i<width; i++){ + for (j=0; j<nbp; j++){ + pdp_imageproc_bq_scanline(x, data+i, height, width); //T->B + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_BOTTOM2TOP){ + for(i=0; i<width; i++){ + for (j=0; j<nbp; j++){ + pdp_imageproc_bq_scanline(x, data+offset+i, height, -width); //B->T + } + } + } + + /* HORIZONTAL */ + + offset = width-1; + if ((direction & PDP_IMAGEPROC_BIQUAD_LEFT2RIGHT) + && (direction & PDP_IMAGEPROC_BIQUAD_RIGHT2LEFT)){ + + for(i=0; i<(width*height); i += width){ + for (j=0; j<nbp; j++){ + pdp_imageproc_bq_scanline(x, data+i, width, 1); //L->R + pdp_imageproc_bq_scanline(x, data+offset+i, width, -1); //R->L + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_LEFT2RIGHT){ + for(i=0; i<(width*height); i += width){ + for (j=0; j<nbp; j++){ + pdp_imageproc_bq_scanline(x, data+i, width, 1); //L->R + } + } + } + + else if (direction & PDP_IMAGEPROC_BIQUAD_RIGHT2LEFT){ + for(i=0; i<(width*height); i += width){ + for (j=0; j<nbp; j++){ + pdp_imageproc_bq_scanline(x, data+offset+i, width, -1); //R->L + + } + } + } + +} + +// produce a random image +// note: random number generator can be platform specific +// however, it should be seeded. (same seed produces the same result) +void *pdp_imageproc_random_new(void){return pdp_alloc(sizeof(s32));} +void pdp_imageproc_random_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_random_setseed(void *x, float seed) +{ + float *f = (float *)x; + u32 *d = (u32 *)x; + f[0] = seed; + srandom(d[0]); +} + +void pdp_imageproc_random_process(void *x, u32 width, u32 height, short int *image) +{ + s32 *d = (u32 *)x; + u32 i; + s32 r; + srandom(d[0]); + for (i=0; i<(width*height); i++) { + r = random(); + image[i] = r; + } + d[0] = random(); +} + + + +/* resampling code */ +// zoom + rotate + +/* bilinear resampling core routine */ +/* virtual coordinates are the lowest 16 bits in virt_x and virt_y*/ +static inline s32 pdp_resample_bilin(s16 *image, s32 width, s32 height, s32 virt_x, s32 virt_y) +{ + + s32 fp_x, fp_y, frac_x, frac_y, f, offset, r_1, r_2; + + //virt_x &= 0xffff; + //virt_y &= 0xffff; + + fp_x = virt_x * (width - 1); + fp_y = virt_y * (height - 1); + + frac_x = fp_x & (0xffff); + frac_y = fp_y & (0xffff); + + offset = (fp_x >> 16) + (fp_y >> 16) * width; + image += offset; + + f = 0x10000 - frac_x; + + r_1 = ((f * (s32)(image[0]) + frac_x * (s32)(image[1])))>>16; + + image += width; + + r_2 = ((f * (s32)(image[0]) + frac_x * (s32)(image[1])))>>16; + + f = 0x10000 - frac_y; + + return ((f * r_1 + frac_y * r_2)>>16); + +} + +typedef struct +{ + float centerx; + float centery; + float zoomx; + float zoomy; + float angle; +} t_affine_map; + + +void *pdp_imageproc_resample_affinemap_new(void) +{ + + t_affine_map *a = (t_affine_map *)pdp_alloc(sizeof(t_affine_map)); + a->centerx = 0.5; + a->centery = 0.5; + a->zoomx = 1.0; + a->zoomy = 1.0; + a->angle = 0.0f; + return (void *)a; +} + +void pdp_imageproc_resample_affinemap_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_resample_affinemap_setcenterx(void *x, float f){((t_affine_map *)x)->centerx = f;} +void pdp_imageproc_resample_affinemap_setcentery(void *x, float f){((t_affine_map *)x)->centery = f;} +void pdp_imageproc_resample_affinemap_setzoomx(void *x, float f){((t_affine_map *)x)->zoomx = f;} +void pdp_imageproc_resample_affinemap_setzoomy(void *x, float f){((t_affine_map *)x)->zoomy = f;} +void pdp_imageproc_resample_affinemap_setangle(void *x, float f){((t_affine_map *)x)->angle = f;} +void pdp_imageproc_resample_affinemap_process(void *x, u32 width, u32 height, s16 *srcimage, s16 *dstimage) +{ + t_affine_map *a = (t_affine_map *)x; + double izx = 1.0f / (a->zoomx); + double izy = 1.0f / (a->zoomy); + double scale = (double)0xffffffff; + double scalew = scale / ((double)(width - 1)); + double scaleh = scale / ((double)(height - 1)); + double cx = ((double)a->centerx) * ((double)(width - 1)); + double cy = ((double)a->centery) * ((double)(height - 1)); + double angle = a->angle * (-M_PI / 180.0); + double c = cos(angle); + double s = sin(angle); + + /* affine x, y mappings in screen coordinates */ + double mapx(double x, double y){return cx + izx * ( c * (x-cx) + s * (y-cy));} + double mapy(double x, double y){return cy + izy * (-s * (x-cx) + c * (y-cy));} + + u32 colstate_x = (u32)(scalew * mapx(0,0)); + u32 colstate_y = (u32)(scaleh * mapy(0,0)); + u32 rowstate_x = colstate_x; + u32 rowstate_y = colstate_y; + + u32 row_inc_x = (u32)(scalew * (mapx(1,0)-mapx(0,0))); + u32 row_inc_y = (u32)(scaleh * (mapy(1,0)-mapy(0,0))); + u32 col_inc_x = (u32)(scalew * (mapx(0,1)-mapx(0,0))); + u32 col_inc_y = (u32)(scaleh * (mapy(0,1)-mapy(0,0))); + + u32 i,j; + + for (j=0; j<height; j++){ + for (i=0; i<width; i++){ + *dstimage++ = pdp_resample_bilin(srcimage, width, height, rowstate_x>>16, rowstate_y>>16); + rowstate_x += row_inc_x; + rowstate_y += row_inc_y; + } + colstate_x += col_inc_x; + colstate_y += col_inc_y; + rowstate_x = colstate_x; + rowstate_y = colstate_y; + } + +} + + + + + +// polynomials + + + + +typedef struct +{ + u32 order; + u32 nbpasses; + s32 coefs[0]; +} t_cheby; + +void *pdp_imageproc_cheby_new(int order) +{ + t_cheby *z; + int i; + if (order < 2) order = 2; + z = (t_cheby *)pdp_alloc(sizeof(t_cheby) + (order + 1) * sizeof(s32)); + z->order = order; + z->coefs[0] = 0; + z->coefs[1] = 0x7fff; + for (i=2; i<=order; i++) z->coefs[i] = 0; + return z; +} +void pdp_imageproc_cheby_delete(void *x){pdp_dealloc(x);} +void pdp_imageproc_cheby_setnbpasses(void *x, u32 n){((t_cheby *)x)->nbpasses = n;} +void pdp_imageproc_cheby_setcoef(void *x, u32 n, float f) +{ + + t_cheby *z = (t_cheby *)x; + if (n <= z->order){ + z->coefs[n] = (s32)(f * 32767.0f); // coefs are in s16.15 format + } + +} +void pdp_imageproc_cheby_process(void *x, u32 width, u32 height, s16 *image) +{ + + t_cheby *z = (t_cheby *)x; + u32 iterations = z->nbpasses; + u32 i,j,k; + s32 *c = z->coefs; + for (j=0; j < (height*width); j++){ + s32 acc = (s32)image[j]; + for (i=0; i<iterations; i++){ + s32 T2 = 0x7fff; /* 1 */ + s32 T1 = acc; + s32 t; + s32 in = acc; + acc = c[0] + ((in*c[1])>>15); + for (k=2; k<=z->order; k++){ + t = ((T1*in)>>14) - T2; /* T_n = 2 x T_n-1 - T_n-2 */ + T2 = T1; + T1 = t; + acc += ((c[k] * t)>>15); + } + } + image[j] = (s16)(CLAMP16(acc)); + } +} diff --git a/system/image/pdp_llconv.c b/system/image/pdp_llconv.c new file mode 100644 index 0000000..7fb17de --- /dev/null +++ b/system/image/pdp_llconv.c @@ -0,0 +1,596 @@ +/* + * Pure Data Packet system implementation. : low level format conversion code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this file contains low level image conversion code + nominated as "the ugliest part of pdp" + some code is mmx, most is not. */ + +#include "pdp_llconv.h" +#include "pdp_mmx.h" + + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define CLAMP8(x) (((x)<0) ? 0 : ((x>255)? 255 : (x))) +#define CLAMP16(x) (((x)<-0x7fff) ? -0x7fff : ((x>0x7fff) ? 0x7fff : (x))) +#define FP(x) ((int)(((float)(x)) * 256.0f)) + +#define CLAMP CLAMP8 + +/* some prototypes for functions defined elsewhere */ +void llconv_yvu_planar_s16u8(short int *src, unsigned char *dst, unsigned int nbpixels); +void llconv_yuv_planar_u8s16(unsigned char* source, short int *dest, int nbpixels); +void llconv_grey_s16u8(short int *src, unsigned char *dst, unsigned int nbpixels); +void llconv_yvu_planar_u8s16(unsigned char* source, short int *dest, int nbpixels); + + +static inline int rgb2y(int r, int g, int b){return (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16);} +static inline int rgb2v(int r, int g, int b){return (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b) + FP(128);} +static inline int rgb2u(int r, int g, int b){return -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b) + FP(128);} + + +/* swap top to bottom */ +static inline void _exchange_row(char *row1, char *row2, int size) +{ + int mask = ~(sizeof(int)-1); + int *irow1 = (int *)row1; + int *irow2 = (int *)row2; + + /* transfer words */ + while (size & mask){ + int tmp = *irow1; + *irow1++ = *irow2; + *irow2++ = tmp; + size -= sizeof(int); + } + + row1 = (char *)irow1; + row2 = (char *)irow2; + + /* transfer rest bytes */ + while (size){ + int tmp = *row1; + *row1++ = *row2; + *row2++ = tmp; + size--; + } +} + +void pdp_llconv_flip_top_bottom(char *data, int width, int height, int pixelsize) +{ + int linesize = width * pixelsize; + int i; + char *row1 = data; + char *row2 = data + linesize * (height-1); + + if (height <= 1) return; + if (width <= 0) return; + + while (row1 < row2){ + _exchange_row(row1, row2, linesize); + row1 += linesize; + row2 -= linesize; + } +} + +/* "standard" 8 bit conversion routine */ +static void llconv_rgb2yvu(unsigned char* src, unsigned char* dst, int nbpixels) +{ + int r,g,b,y,v,u,i; + for (i=0; i<nbpixels; i++){ + r = src[0]; + g = src[1]; + b = src[2]; + + y = rgb2y(r,g,b); + v = rgb2v(r,g,b); + u = rgb2u(r,g,b); + + dst[0] = CLAMP(y>>8); + dst[1] = CLAMP(v>>8); + dst[2] = CLAMP(u>>8); + + src += 3; + dst += 3; + } +} + +static void llconv_yvu16planar2rgbpacked(short int *src, unsigned char *dst, int w, int h) +{ + +/* +B = 1.164(Y - 16) + 2.018(U - 128) +G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) +R = 1.164(Y - 16) + 1.596(V - 128)} +*/ + + int r,g,b,y,u,v,b1,g1,r1,y1,xoff,yoff; + int size = w*h; + int voffset = size; + int uoffset = size + (size>>2); + int rgboff; + int rgbw = w*3; + int lumoff = 0; + int chromoff = 0; + + for(yoff=0; yoff<w*h; yoff+=2*w){ + for(xoff=0; xoff<w; xoff+=2){ + + /* calculate offsets */ + rgboff = 3 * (xoff + yoff); + lumoff = xoff + yoff; + chromoff = (xoff >> 1) + (yoff >> 2); + + /* get uv values */ + v = src[voffset + chromoff]; + u = src[uoffset + chromoff]; + + /* calculate chroma contrib for 2x2 pixblock */ + b1 = FP(2.018) * u; + g1 = FP(-0.813) * v + FP(-0.391) * u; + r1 = FP(1.596) * v; + + /* TOP LEFT */ + + /* top left luma contrib */ + y = src[lumoff] << 1; + y1 = FP(1.164) * y; + y1 -= FP(16*256); + + b = (b1 + y1)>>16; + g = (g1 + y1)>>16; + r = (r1 + y1)>>16; + + /* store top left rgb pixel */ + dst[rgboff+0] = CLAMP8(r); + dst[rgboff+1] = CLAMP8(g); + dst[rgboff+2] = CLAMP8(b); + + /* TOP RIGHT */ + + /* top right luma contrib */ + y = src[lumoff + 1] << 1; + y1 = FP(1.164) * y; + y1 -= FP(16*256); + + b = (b1 + y1)>>16; + g = (g1 + y1)>>16; + r = (r1 + y1)>>16; + + /* store top right rgb pixel */ + dst[rgboff+3] = CLAMP8(r); + dst[rgboff+4] = CLAMP8(g); + dst[rgboff+5] = CLAMP8(b); + + + /* BOTTOM LEFT */ + + /* bottom left luma contrib */ + y = src[lumoff+w] << 1; + y1 = FP(1.164) * y; + y1 -= FP(16*256); + + b = (b1 + y1)>>16; + g = (g1 + y1)>>16; + r = (r1 + y1)>>16; + + /* store bottom left rgb pixel */ + dst[rgboff+rgbw+0] = CLAMP8(r); + dst[rgboff+rgbw+1] = CLAMP8(g); + dst[rgboff+rgbw+2] = CLAMP8(b); + + /* BOTTOM RIGHT */ + + /* bottom right luma contrib */ + y = src[lumoff + w + 1] << 1; + y1 = FP(1.164) * y; + y1 -= FP(16*256); + + b = (b1 + y1)>>16; + g = (g1 + y1)>>16; + r = (r1 + y1)>>16; + + /* store bottom right rgb pixel */ + dst[rgboff+rgbw+3] = CLAMP8(r); + dst[rgboff+rgbw+4] = CLAMP8(g); + dst[rgboff+rgbw+5] = CLAMP8(b); + + } + + } + +} + + + +/* 8 bit rgb to 16 bit planar subsampled yvu */ +static void llconv_rgb2yvu_planar16sub(unsigned char* src, short int* dst, int w, int h) +{ + int r,g,b,y,v,u,i,j,k; + int size = w*h; + + int voffset = size; + int uoffset = size + (size>>2); + + + int loffset = w * 3; + + k=0; + for (j=0; j<w*h; j+=(w<<1)){ + k = 3 * j; + for (i=0; i<w; i+=2){ + + + // well, this seems to work... strange though + r = src[k]; + g = src[k+1]; + b = src[k+2]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v = (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u = -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[i+j] = CLAMP16(y >> 1); + + r = src[k+3]; + g = src[k+4]; + b = src[k+5]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v += (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u += -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[i+j+1] = CLAMP16(y >> 1); + + + + r = src[loffset + k]; + g = src[loffset + k+1]; + b = src[loffset + k+2]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v = (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u = -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[w+i+j] = CLAMP16(y >> 1); + + r = src[loffset + k+3]; + g = src[loffset + k+4]; + b = src[loffset + k+5]; + + k += 6; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v += (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u += -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[w+i+j+1] = CLAMP16(y >> 1); + + dst[uoffset+ (i>>1) + (j>>2)] = (CLAMP16(u >> 1)); + dst[voffset+ (i>>1) + (j>>2)] = (CLAMP16(v >> 1)); + } + } +} + + +/* 8 bit rgb to 8 bit planar subsampled yvu */ +static void llconv_rgb2yvu_planar8sub(unsigned char* src, unsigned char *dst, int w, int h) +{ + int r,g,b,y,v,u,i,j,k; + int size = w*h; + + int voffset = size; + int uoffset = size + (size>>2); + + + int loffset = w * 3; + + k=0; + for (j=0; j<w*h; j+=(w<<1)){ + k = 3 * j; + for (i=0; i<w; i+=2){ + + + // well, this seems to work... strange though + r = src[k]; + g = src[k+1]; + b = src[k+2]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v = (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u = -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[i+j] = CLAMP8(y >> 8); + + r = src[k+3]; + g = src[k+4]; + b = src[k+5]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v += (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u += -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[i+j+1] = CLAMP8(y >> 8); + + + + r = src[loffset + k]; + g = src[loffset + k+1]; + b = src[loffset + k+2]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v = (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u = -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[w+i+j] = CLAMP8(y >> 8); + + r = src[loffset + k+3]; + g = src[loffset + k+4]; + b = src[loffset + k+5]; + + k += 6; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v += (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u += -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[w+i+j+1] = CLAMP8(y >> 8); + + dst[uoffset+ (i>>1) + (j>>2)] = (CLAMP8((u >> 9)+128)); + dst[voffset+ (i>>1) + (j>>2)] = (CLAMP8((v >> 9)+128)); + } + } +} + + +/* 8 bit bgr to 16 bit planar subsampled yvu */ +static void llconv_bgr2yvu_planar16sub(unsigned char* src, short int* dst, int w, int h) +{ + int r,g,b,y,v,u,i,j,k; + int size = w*h; + + int voffset = size; + int uoffset = size + (size>>2); + + + int loffset = w * 3; + + k=0; + for (j=0; j<w*h; j+=(w<<1)){ + k = 3 * j; + for (i=0; i<w; i+=2){ + + + // well, this seems to work... strange though + b = src[k]; + g = src[k+1]; + r = src[k+2]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v = (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u = -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[i+j] = CLAMP16(y >> 1); + + b = src[k+3]; + g = src[k+4]; + r = src[k+5]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v += (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u += -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[i+j+1] = CLAMP16(y >> 1); + + + + b = src[loffset + k]; + g = src[loffset + k+1]; + r = src[loffset + k+2]; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v = (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u = -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[w+i+j] = CLAMP16(y >> 1); + + b = src[loffset + k+3]; + g = src[loffset + k+4]; + r = src[loffset + k+5]; + + k += 6; + + y = (FP(0.257) * r) + (FP(0.504) * g) + (FP(0.098) * b) + FP(16); + v += (FP(0.439) * r) - (FP(0.368) * g) - (FP(0.071) * b); + u += -(FP(0.148) * r) - (FP(0.291) * g) + (FP(0.439) * b); + + dst[w+i+j+1] = CLAMP16(y >> 1); + + dst[uoffset+ (i>>1) + (j>>2)] = (CLAMP16(u >> 1)); + dst[voffset+ (i>>1) + (j>>2)] = (CLAMP16(v >> 1)); + } + } +} + +/* these seem to be pretty slow */ + +static void llconv_yvu2rgb(unsigned char* src, unsigned char* dst, int nbpixels) +{ + int r,g,b,y,v,u,i; + for (i=0; i<nbpixels; i++){ + y = src[0]; + v = src[1]; + u = src[2]; + + + b = FP(1.164) * (y - 16) + FP(2.018) * (u - 128); + g = FP(1.164) * (y - 16) - FP(0.813) * (v - 128) - FP(0.391) * (u - 128); + r = FP(1.164) * (y - 16) + FP(1.596) * (v - 128); + + dst[0] = CLAMP(r>>8); + dst[1] = CLAMP(g>>8); + dst[2] = CLAMP(b>>8); + + src += 3; + dst += 3; + } +} + + + +/* convert yvu to yuyv */ +static void llconv_yvu2yuyv(unsigned char *src, unsigned char *dst, unsigned int nbpixels) +{ + unsigned int y1, y2, u, v, i; + + for (i = 0; i < nbpixels/2; i++){ + + y1 = src[0]; + y2 = src[3]; + v = (src[1] + src[4]) >> 1; + u = (src[2] + src[5]) >> 1; + dst[0] = y1; + dst[1] = u; + dst[2] = y2; + dst[3] = v; + + src += 6; + dst += 4; + + } + +} + + + +/* convert yuvu packed 8 bit unsigned to yv12 planar 16bit signed */ +static void llconv_yuyv_packed_u8s16(unsigned char* ucsource, short int *sidest, unsigned int w, unsigned int h) +{ + unsigned int i, j; + unsigned int *source = (unsigned int *)ucsource; + + unsigned int *dest = (unsigned int *)sidest; + unsigned int uoffset = (w*h)>>1; + unsigned int voffset = (w*h + ((w*h) >> 2)) >> 1; + + for(j=0; j < (h*w)>>1; j +=(w)){ + for(i=0; i< (w>>1); i+=2){ + unsigned int y,u,v; + unsigned int v00, v01, v10, v11; + v00 = source[i+j]; + v01 = source[i+j+1]; + v10 = source[i+j+(w>>1)]; + v11 = source[i+j+(w>>1)+1]; + + // save luma + dest[i+j] = ((v00 & 0x00ff00ff) << 7); + dest[i+j+1] = ((v01 & 0x00ff00ff) << 7); + dest[i+j+(w>>1)] = ((v10 & 0x00ff00ff) << 7); + dest[i+j+(w>>1)+1] = ((v11 & 0x00ff00ff) << 7); + + // compute chroma + + // mask out luma & shift right + v00 = (v00 & 0xff00ff00)>>1; + v01 = (v01 & 0xff00ff00)>>1; + v10 = (v10 & 0xff00ff00)>>1; + v11 = (v11 & 0xff00ff00)>>1; + + // average 2 scan lines + v00 += v10; + v01 += v11; + + // combine + v = (v01 << 16) | (v00 & 0x0000ffff); + u = (v01 & 0xffff0000) | (v00 >> 16); + + // flip sign bits for u,v + u ^= 0x80008000; + v ^= 0x80008000; + + // save chroma + dest[uoffset + (i>>1) + (j>>2)] = u; + dest[voffset + (i>>1) + (j>>2)] = v; + } + } + + +} + +#define CONVERT(x,y) ((x) + ((y)<<16)) + +void pdp_llconv(void *src, int stype, void *dst, int dtype, int w, int h) +{ + int conversion = CONVERT(stype, dtype); + void *tmpbuf; + + switch(CONVERT(stype, dtype)){ + + case CONVERT( RIF_YVU__P411_U8, RIF_YVU__P411_S16 ): + llconv_yvu_planar_u8s16((unsigned char*)src, (short int *)dst, w*h); + break; + + case CONVERT( RIF_YUV__P411_U8, RIF_YVU__P411_S16 ): + llconv_yuv_planar_u8s16((unsigned char*)src, (short int *)dst, w*h); + break; + + case CONVERT( RIF_YUYV_P____U8, RIF_YVU__P411_S16 ): + llconv_yuyv_packed_u8s16((unsigned char*)src, (short int *)dst, w, h); + break; + + case CONVERT( RIF_RGB__P____U8, RIF_YVU__P411_U8 ): + llconv_rgb2yvu_planar8sub((unsigned char*) src, (unsigned char*) dst, w, h); + break; + + case CONVERT( RIF_RGB__P____U8, RIF_YVU__P411_S16 ): + llconv_rgb2yvu_planar16sub((unsigned char*) src, (short int*) dst, w, h); + break; + + case CONVERT( RIF_BGR__P____U8, RIF_YVU__P411_S16 ): + llconv_bgr2yvu_planar16sub((unsigned char*) src, (short int*) dst, w, h); + break; + + case CONVERT( RIF_YVU__P411_S16, RIF_RGB__P____U8 ): + llconv_yvu16planar2rgbpacked((short int*) src, (unsigned char*) dst, w, h); + break; + + case CONVERT( RIF_YVU__P411_S16, RIF_YVU__P411_U8 ): + llconv_yvu_planar_s16u8((short int*)src, (unsigned char*)dst, w*h); + break; + + case CONVERT( RIF_GREY______S16, RIF_GREY______U8 ): + llconv_grey_s16u8((short int*)src, (unsigned char*)dst, w*h); + break; + default: + post("pdp_llconv: WARNING: no conversion routine defined for (%d)->(%d)", stype, dtype); + + } + +} + + +#ifdef __cplusplus +} +#endif diff --git a/system/image/pdp_llconv_mmx.c b/system/image/pdp_llconv_mmx.c new file mode 100644 index 0000000..8070bac --- /dev/null +++ b/system/image/pdp_llconv_mmx.c @@ -0,0 +1,55 @@ + +/* + * Pure Data Packet system implementation. : wrapper for mmx low level format conversion code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "pdp_mmx.h" + + + +/* convert greyscale 8 bit unsigned to 16bit signed */ +void llconv_grey_s16u8(short int *src, unsigned char *dst, unsigned int nbpixels) +{ + pixel_pack_s16u8_y(src, dst, nbpixels>>3); +} + +/* convert yvu planar 411 16 bit signed to 8 bit unsigned */ +void llconv_yvu_planar_s16u8(short int *src, unsigned char *dst, unsigned int nbpixels) +{ + pixel_pack_s16u8_y(src, dst, nbpixels>>3); + pixel_pack_s16u8_uv(src + nbpixels, dst + nbpixels, nbpixels>>4); +} + + +/* convert yvu planar 411 8 bit unsigned to yv12 planar 16bit signed */ +void llconv_yvu_planar_u8s16(unsigned char* source, short int *dest, int nbpixels) +{ + pixel_unpack_u8s16_y(source, dest, nbpixels>>3); + pixel_unpack_u8s16_uv(&source[nbpixels], &dest[nbpixels], nbpixels>>4); +} + +/* convert yuv planar 411 8 bit unsigned to yv12 planar 16bit signed */ +void llconv_yuv_planar_u8s16(unsigned char* source, short int *dest, int nbpixels) +{ + pixel_unpack_u8s16_y(source, dest, nbpixels>>3); + pixel_unpack_u8s16_uv(&source[nbpixels], &dest[nbpixels + (nbpixels>>2)], nbpixels>>5); + pixel_unpack_u8s16_uv(&source[nbpixels + (nbpixels>>2)], &dest[nbpixels], nbpixels>>5); +} + diff --git a/system/image/pdp_llconv_portable.c b/system/image/pdp_llconv_portable.c new file mode 100644 index 0000000..f6d5a44 --- /dev/null +++ b/system/image/pdp_llconv_portable.c @@ -0,0 +1,82 @@ + +/* + * Pure Data Packet system implementation. : portable low level format conversion code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define CLAMP(x) (((x)<0) ? 0 : ((x>255)? 255 : (x))) +#define FP(x) ((int)(((float)(x)) * 256.0f)) + +void pixel_unpack_portable_u8s16_y(unsigned char *src ,short int *dst, unsigned int nbpixels) +{ + unsigned int i; + for (i=0; i<nbpixels; i++) dst[i] = ((short int)(src[i])) << 7; +} + +void pixel_unpack_portable_u8s16_uv(unsigned char *src ,short int *dst, unsigned int nbpixels) +{ + unsigned int i; + for (i=0; i<nbpixels; i++) dst[i] = (((short int)(src[i])) << 8) ^ 0x8000; +} + + +void pixel_pack_portable_s16u8_y(short int *src, unsigned char *dst, unsigned int nbpixels) +{ + unsigned int i; + for (i=0; i<nbpixels; i++) dst[i] = (unsigned char)(CLAMP(src[i]>>7)); +} + +void pixel_pack_portable_s16u8_uv(short int *src, unsigned char *dst, unsigned int nbpixels) +{ + unsigned int i; + unsigned short *usrc = (unsigned short *)src; + for (i=0; i<nbpixels; i++) dst[i] = ((usrc[i]^0x8000)>>8); +} + + +/* convert greyscale 8 bit unsigned to 16bit signed */ +void llconv_grey_s16u8(short int *src, unsigned char *dst, unsigned int nbpixels) +{ + pixel_pack_portable_s16u8_y(src, dst, nbpixels); +} + +/* convert yvu planar 411 16 bit signed to 8 bit unsigned */ +void llconv_yvu_planar_s16u8(short int *src, unsigned char *dst, unsigned int nbpixels) +{ + pixel_pack_portable_s16u8_y(src, dst, nbpixels); + pixel_pack_portable_s16u8_uv(src + nbpixels, dst + nbpixels, nbpixels>>1); + +} + + +/* convert yvu planar 411 8 bit unsigned to yv12 planar 16bit signed */ +void llconv_yvu_planar_u8s16(unsigned char* source, short int *dest, int nbpixels) +{ + pixel_unpack_portable_u8s16_y(source, dest, nbpixels); + pixel_unpack_portable_u8s16_uv(&source[nbpixels], &dest[nbpixels], nbpixels>>1); +} + +/* convert yuv planar 411 8 bit unsigned to yv12 planar 16bit signed */ +void llconv_yuv_planar_u8s16(unsigned char* source, short int *dest, int nbpixels) +{ + pixel_unpack_portable_u8s16_y(source, dest, nbpixels); + pixel_unpack_portable_u8s16_uv(&source[nbpixels], &dest[nbpixels + (nbpixels>>2)], nbpixels>>2); + pixel_unpack_portable_u8s16_uv(&source[nbpixels + (nbpixels>>2)], &dest[nbpixels], nbpixels>>2); +} + + diff --git a/system/image/pdp_resample.c b/system/image/pdp_resample.c new file mode 100644 index 0000000..d2ffbcd --- /dev/null +++ b/system/image/pdp_resample.c @@ -0,0 +1,204 @@ +/* + * Pure Data Packet system file. - image resampling routines + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "pdp_resample.h" +#include "pdp.h" +#include <string.h> + +/* + +efficient bilinear resampling ?? +performance: how to eliminate divides? -> virtual coordinates 2^k x 2^k (conf. opengl) + +i.e. 16 bit virtual coordinates: easy modular addressing + +*/ + + +/* code in this file should go out to be replaced by code in pdp_imageproc */ + +static s32 pdp_resample_bilin(s16 *image, s32 width, s32 height, s32 virt_x, s32 virt_y) +{ + + s32 fp_x, fp_y, frac_x, frac_y, f, offset, r_1, r_2; + + virt_x &= 0xffff; + virt_y &= 0xffff; + + fp_x = virt_x * (width - 1); + fp_y = virt_y * (height - 1); + + frac_x = fp_x & (0xffff); + frac_y = fp_y & (0xffff); + + offset = (fp_x >> 16) + (fp_y >> 16) * width; + image += offset; + + f = 0x10000 - frac_x; + + r_1 = ((f * (s32)(image[0]) + frac_x * (s32)(image[1])))>>16; + + image += width; + + r_2 = ((f * (s32)(image[0]) + frac_x * (s32)(image[1])))>>16; + + f = 0x10000 - frac_y; + + return ((f * r_1 + frac_y * r_2)>>16); + +} + + +void pdp_resample_scale_bilin(s16 *src_image, s16 *dst_image, s32 src_w, s32 src_h, s32 dst_w, s32 dst_h) +{ + s32 i,j; + s32 virt_x=0; + s32 virt_y=0; /* virtual coordinates in 30 bit */ + s32 scale_x = 0x40000000 / dst_w; + s32 scale_y = 0x40000000 / dst_h; + + for (j=0; j<dst_h; j++){ + for (i=0; i<dst_w; i++){ + *dst_image++ = pdp_resample_bilin(src_image, src_w, src_h, virt_x>>14, virt_y>>14); + virt_x += scale_x; + } + virt_x = 0; + virt_y += scale_y; + } + +} + +void pdp_resample_scale_nn(s16 *src_image, s16 *dst_image, s32 src_w, s32 src_h, s32 dst_w, s32 dst_h) +{ + s32 i,j; + s32 x=0; + s32 y=0; + s32 frac_x=0; + s32 frac_y=0; + s32 scale_x = (src_w << 20 ) / dst_w; + s32 scale_y = (src_h << 20 ) / dst_h; + + for (j=0; j<dst_h; j++){ + for (i=0; i<dst_w; i++){ + *dst_image++ = src_image[x+y]; + frac_x += scale_x; + x = frac_x >> 20; + } + x = 0; + frac_x = 0; + frac_y += scale_y; + y = (frac_y >> 20) * src_w; + } + +} + +/* USE pdp_resample_affinemap +void pdp_resample_zoom_tiled_bilin(s16 *src_image, s16 *dst_image, s32 w, s32 h, + float zoom_x, float zoom_y, float center_x_relative, float center_y_relative) +{ + float izx = 1.0f / zoom_x; + float izy = 1.0f / zoom_y; + s32 scale_x = (s32)((float)0x100000 * izx / (float)w); + s32 scale_y = (s32)((float)0x100000 * izy / (float)h); + + s32 top_virt_x = (s32)((1.0f - izx) * (float)0x100000 * center_x_relative); + s32 top_virt_y = (s32)((1.0f - izy) * (float)0x100000 * center_y_relative); + + s32 virt_x = top_virt_x; + s32 virt_y = top_virt_y; + + s32 i,j; + + for (j=0; j<h; j++){ + for (i=0; i<w; i++){ + *dst_image++ = pdp_resample_bilin(src_image, w, h, virt_x>>4, virt_y>>4); + virt_x += scale_x; + } + virt_x = top_virt_x; + virt_y += scale_y; + } + +} +*/ + +void pdp_resample_halve(s16 *src_image, s16 *dst_image, s32 src_w, s32 src_h) +{ + + int dst_x,dst_y; + int src_x = 0; + int src_y = 0; + int dst_w = src_w >> 1; + int dst_h = src_h >> 1; + s32 tmp1,tmp2,tmp3,tmp4; + + //post("%x %x %d %d\n", src_image, dst_image, src_w, src_h); + + for(dst_y = 0; dst_y < dst_h * dst_w; dst_y += dst_w){ + for (dst_x = 0; dst_x < dst_w; dst_x++){ + + tmp1 = (s32)src_image[src_y + src_x]; + tmp2 = (s32)src_image[src_y + src_x + 1]; + tmp3 = (s32)src_image[src_y + src_x + src_w]; + tmp4 = (s32)src_image[src_y + src_x + src_w + 1]; + + tmp1 += tmp2; + tmp3 += tmp4; + + src_x += 2; + + dst_image[dst_x+dst_y] = (s16)((tmp1 + tmp3)>>2); + } + src_y += src_w << 1; + src_x = 0; + } +} + +void pdp_resample_double(s16 *src_image, s16 *dst_image, s32 src_w, s32 src_h) +{ + int src_x = 0; + int src_y = 0; + int dst = 0; + int dst_w = src_w << 1; + + s16 tmp; + + for(src_y = 0; src_y < src_h * src_w; src_y += src_w){ + for (src_x = 0; src_x < src_w; src_x++){ + + tmp = *src_image++; + dst = (src_y << 2) + (src_x << 1); + dst_image[dst] = tmp; + dst_image[dst+1] = tmp; + dst+=dst_w; + dst_image[dst] = tmp; + dst_image[dst+1] = tmp; + } + } +} + +/* $$$TODO: finish this */ +void pdp_resample_padcrop(s16 *src_image, s16 *dst_image, s32 src_w, s32 src_h, s32 dst_w, s32 dst_h) +{ + + int shift_x = (dst_w - src_w) / 2; + int shift_y = (dst_h - src_h) / 2; +} + diff --git a/system/kernel/Makefile b/system/kernel/Makefile new file mode 100644 index 0000000..f1d82a9 --- /dev/null +++ b/system/kernel/Makefile @@ -0,0 +1,16 @@ + +OBJECTS = pdp.o pdp_ut.o pdp_packet.o pdp_queue.o pdp_comm.o \ + pdp_control.o pdp_compat.o pdp_type.o pdp_dpd_command.o \ + pdp_list.o pdp_forth.o + + +include ../../Makefile.config + +all: pdp_main_clean $(OBJECTS) + +pdp_main_clean: + rm -f pdp.o + +clean: + rm -f *~ + rm -f *.o diff --git a/system/kernel/pdp.c b/system/kernel/pdp.c new file mode 100644 index 0000000..d71e655 --- /dev/null +++ b/system/kernel/pdp.c @@ -0,0 +1,198 @@ +/* + * Pure Data Packet system implementation: setup code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "pdp_config.h" +#include "pdp.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* module setup declarations (all C-style) */ + +/* pdp system / internal stuff */ +void pdp_list_setup(void); +void pdp_sym_setup(void); +void pdp_forth_setup(void); +void pdp_type_setup(void); +void pdp_packet_setup(void); +void pdp_ut_setup(void); +void pdp_queue_setup(void); +void pdp_control_setup(void); +void pdp_image_setup(void); +void pdp_bitmap_setup(void); +void pdp_matrix_setup(void); + + +/* pdp modules */ +void pdp_xv_setup(void); +void pdp_add_setup(void); +void pdp_mul_setup(void); +void pdp_mix_setup(void); +void pdp_randmix_setup(void); +void pdp_qt_setup(void); +void pdp_v4l_setup(void); +void pdp_reg_setup(void); +void pdp_conv_setup(void); +void pdp_bq_setup(void); +void pdp_del_setup(void); +void pdp_snap_setup(void); +void pdp_trigger_setup(void); +void pdp_route_setup(void); +void pdp_noise_setup(void); +void pdp_gain_setup(void); +void pdp_chrot_setup(void); +void pdp_scope_setup(void); +void pdp_scale_setup(void); +void pdp_zoom_setup(void); +void pdp_scan_setup(void); +void pdp_scanxy_setup(void); +void pdp_sdl_setup(void); +void pdp_cheby_setup(void); +void pdp_grey2mask_setup(void); +void pdp_constant_setup(void); +void pdp_logic_setup(void); +void pdp_glx_setup(void); +void pdp_loop_setup(void); +void pdp_description_setup(void); +void pdp_convert_setup(void); +void pdp_stateless_setup(void); +void pdp_mat_mul_setup(void); +void pdp_mat_lu_setup(void); +void pdp_mat_vec_setup(void); +void pdp_plasma_setup(void); +void pdp_cog_setup(void); +void pdp_histo_setup(void); +void pdp_array_setup(void); + +/* hacks */ +void pdp_inspect_setup(void); +void pdp_slice_cut_setup(void); +void pdp_slice_glue_setup(void); + +/* testing */ +void pdp_dpd_test_setup(void); +void pdp_forthproc_setup(void); + + + +/* library setup routine */ +void pdp_setup(void){ + + /* babble */ + post ("PDP: pure data packet"); + +#ifdef PDP_VERSION + fprintf(stderr, "PDP: version " PDP_VERSION "\n"); +#endif + + + /* setup pdp system */ + pdp_list_setup(); + pdp_type_setup(); + pdp_sym_setup(); + pdp_packet_setup(); + pdp_forth_setup(); + pdp_control_setup(); + + pdp_image_setup(); + pdp_bitmap_setup(); + pdp_matrix_setup(); + + pdp_queue_setup(); + + /* setup utility toolkit */ + pdp_ut_setup(); + + /* setup pdp modules*/ + pdp_add_setup(); + pdp_mul_setup(); + pdp_mix_setup(); + pdp_randmix_setup(); + pdp_reg_setup(); + pdp_conv_setup(); + pdp_bq_setup(); + pdp_del_setup(); + pdp_snap_setup(); + pdp_trigger_setup(); + pdp_route_setup(); + pdp_noise_setup(); + pdp_plasma_setup(); + pdp_gain_setup(); + pdp_chrot_setup(); + pdp_scope_setup(); + pdp_scale_setup(); + pdp_zoom_setup(); + pdp_scan_setup(); + pdp_scanxy_setup(); + pdp_cheby_setup(); + pdp_grey2mask_setup(); + pdp_constant_setup(); + pdp_logic_setup(); + pdp_loop_setup(); + pdp_description_setup(); + pdp_convert_setup(); + pdp_stateless_setup(); + pdp_mat_mul_setup(); + pdp_mat_lu_setup(); + pdp_mat_vec_setup(); + pdp_cog_setup(); + pdp_histo_setup(); + pdp_array_setup(); + + /* experimental stuff */ + pdp_slice_cut_setup(); + pdp_slice_glue_setup(); + pdp_inspect_setup(); + + /* testing */ + //pdp_dpd_test_setup(); + pdp_forthproc_setup(); + + /* optional modules */ + +#ifdef HAVE_PDP_QT + pdp_qt_setup(); +#endif + +#ifdef HAVE_PDP_XV + pdp_xv_setup(); +#endif + +#ifdef HAVE_PDP_SDL + pdp_sdl_setup(); +#endif + +#ifdef HAVE_PDP_V4L + pdp_v4l_setup(); +#endif + +#ifdef HAVE_PDP_GLX + pdp_glx_setup(); +#endif + +} + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_comm.c b/system/kernel/pdp_comm.c new file mode 100644 index 0000000..bcc0e3a --- /dev/null +++ b/system/kernel/pdp_comm.c @@ -0,0 +1,198 @@ +/* + * Pure Data Packet system implementation. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this file contains misc communication (packet) methods for pd */ + + +#include "pdp.h" +#include "pdp_internals.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + +t_symbol *_pdp_sym_pdp; +t_symbol *_pdp_sym_rro; +t_symbol *_pdp_sym_rrw; +t_symbol *_pdp_sym_prc; +t_symbol *_pdp_sym_dpd; +t_symbol *_pdp_sym_ins; +t_symbol *_pdp_sym_acc; + +t_symbol *pdp_sym_pdp() {return _pdp_sym_pdp;} +t_symbol *pdp_sym_rro() {return _pdp_sym_rro;} +t_symbol *pdp_sym_rrw() {return _pdp_sym_rrw;} +t_symbol *pdp_sym_prc() {return _pdp_sym_prc;} +t_symbol *pdp_sym_dpd() {return _pdp_sym_dpd;} +t_symbol *pdp_sym_ins() {return _pdp_sym_ins;} +t_symbol *pdp_sym_acc() {return _pdp_sym_acc;} + +/************** packet management and communication convenience functions ************/ + +/* send a packet to an outlet */ +void outlet_pdp(t_outlet *out, int packetid) +{ + t_atom atom[2]; + + SETFLOAT(atom+1, (float)packetid); + + SETSYMBOL(atom+0, pdp_sym_rro()); + outlet_anything(out, pdp_sym_pdp(), 2, atom); + + SETSYMBOL(atom+0, pdp_sym_rrw()); + outlet_anything(out, pdp_sym_pdp(), 2, atom); + + SETSYMBOL(atom+0, pdp_sym_prc()); + outlet_anything(out, pdp_sym_pdp(), 2, atom); + +} + +/* send an accumulation packet to an outlet */ +void outlet_dpd(t_outlet *out, int packetid) +{ + t_atom atom[2]; + + SETFLOAT(atom+1, (float)packetid); + + SETSYMBOL(atom+0, pdp_sym_ins()); + outlet_anything(out, pdp_sym_dpd(), 2, atom); + + SETSYMBOL(atom+0, pdp_sym_acc()); + outlet_anything(out, pdp_sym_dpd(), 2, atom); + +} + +/* unregister a packet and send it to an outlet */ +void + +pdp_packet_pass_if_valid(t_outlet *outlet, int *packet_ptr) +{ + t_pdp *header = pdp_packet_header(*packet_ptr); + //if (-1 != *packet){ + if (header){ + /* if packet is exclusively owned, mark as passing */ + if (1 == header->users) pdp_packet_mark_passing(packet_ptr); + + /* send */ + outlet_pdp(outlet, *packet_ptr); + + /* if the packet is still here (was passing and not registered, + or it was a shared copy): unregister it */ + if (-1 != *packet_ptr){ + pdp_packet_unmark_passing(*packet_ptr); + pdp_packet_mark_unused(*packet_ptr); + *packet_ptr = -1; + } + } +} + +void +pdp_packet_replace_if_valid(int *dpacket, int *spacket) +{ + if (-1 != *spacket){ + pdp_packet_mark_unused(*dpacket); + *dpacket = *spacket; + *spacket = -1; + } + +} + + +int +pdp_packet_copy_ro_or_drop(int *dpacket, int spacket) +{ + int drop = 0; + if (*dpacket == -1) *dpacket = pdp_packet_copy_ro(spacket); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + + +int +pdp_packet_copy_rw_or_drop(int *dpacket, int spacket) +{ + int drop = 0; + if (*dpacket == -1) *dpacket = pdp_packet_copy_rw(spacket); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + +int +pdp_packet_convert_ro_or_drop(int *dpacket, int spacket, t_pdp_symbol *template) +{ + int drop = 0; + + if (!template) return pdp_packet_copy_ro_or_drop(dpacket, spacket); + + if (*dpacket == -1) *dpacket = pdp_packet_convert_ro(spacket, template); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + + +int +pdp_packet_convert_rw_or_drop(int *dpacket, int spacket, t_pdp_symbol *template) +{ + int drop = 0; + + if (!template) return pdp_packet_copy_rw_or_drop(dpacket, spacket); + + if (*dpacket == -1) *dpacket = pdp_packet_convert_rw(spacket, template); + else { + /* send a notification there is a dropped packet */ + pdp_control_notify_drop(spacket); + drop = 1; + } + return drop; +} + + +void +pdp_sym_setup(void) +{ + _pdp_sym_pdp = gensym("pdp"); + _pdp_sym_rro = gensym("register_ro"); + _pdp_sym_rrw = gensym("register_rw"); + _pdp_sym_prc = gensym("process"); + _pdp_sym_dpd = gensym("dpd"); + _pdp_sym_ins = gensym("inspect"); + _pdp_sym_acc = gensym("accumulate"); +} + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_compat.c b/system/kernel/pdp_compat.c new file mode 100644 index 0000000..4bcf00d --- /dev/null +++ b/system/kernel/pdp_compat.c @@ -0,0 +1,56 @@ +/* + * Pure Data Packet system implementation. Compatibility routines. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this file contains misc communication methods */ + + +#include "pdp.h" +#include "pdp_internals.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void +pdp_pass_if_valid(t_outlet *outlet, int *packet) +{ + pdp_packet_pass_if_valid(outlet, packet); +} + +void +pdp_replace_if_valid(int *dpacket, int *spacket) +{ + pdp_packet_replace_if_valid(dpacket, spacket); + +} + + + + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_control.c b/system/kernel/pdp_control.c new file mode 100644 index 0000000..2cba7b7 --- /dev/null +++ b/system/kernel/pdp_control.c @@ -0,0 +1,186 @@ +/* + * Pure Data Packet system implementation: control object + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this is an actual pd class that is used for communication with the + pdp framework */ + +#include "pdp.h" +#include "pdp_internals.h" +#include "pdp_control.h" +#include <stdio.h> + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + + +static long dropped_packets; + +static t_class* pdp_control_class; + + +/* pdp control instance data */ + +struct _pdp_control; +typedef struct _pdp_control +{ + t_object x_obj; + t_outlet *x_outlet0; + struct _pdp_control *x_next; + +} t_pdp_control; + + + +static t_pdp_control *pdp_control_list; + +static void pdp_control_info(t_pdp_control *x) +{ +} + +static void pdp_control_collectgarbage(t_pdp_control *x) +{ + int nb_packets_freed = pdp_pool_collect_garbage(); + post("pdp_control: freed %d packets", nb_packets_freed); + +} + +static void pdp_control_set_mem_limit(t_pdp_control *x, t_floatarg f) +{ + int limit = (int)f; + if (limit < 0) limit = 0; + pdp_pool_set_max_mem_usage(limit); + if (limit) post("pdp_control: set memory limit to %d bytes", limit); + else post("pdp_control: disabled memory limit"); + +} + +static void pdp_control_thread(t_pdp_control *x, t_floatarg f) +{ + int t = (int)f; + + if (t){ + post("pdp_control: pdp is now using its own processing thread"); + pdp_queue_use_thread(1); + } + else { + post("pdp_control: pdp is now using the main pd thread"); + pdp_queue_use_thread(0); + } +} + + +static void pdp_control_send_drop_message(t_pdp_control *x) +{ + t_atom atom[1]; + t_symbol *s = gensym("pdp_drop"); + + SETFLOAT(atom+0, (float)dropped_packets); + outlet_anything(x->x_outlet0, s, 1, atom); +} + + +static void pdp_control_free(t_pdp_control *x) +{ + /* remove from linked list */ + t_pdp_control *curr = pdp_control_list; + if (pdp_control_list == x) pdp_control_list = x->x_next; + else while (curr){ + if (curr->x_next == x) { + curr->x_next = x->x_next; + break; + } + else { + curr = curr->x_next; + } + + } +} + + +static void *pdp_control_new(void) +{ + t_pdp_control *x = (t_pdp_control *)pd_new(pdp_control_class); + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + /* add to list */ + x->x_next = pdp_control_list; + pdp_control_list = x; + return x; +} + +/************************* class methods ***************************************/ + + +void pdp_control_addmethod(t_method m, t_symbol *s) +{ + class_addmethod(pdp_control_class, m, s, A_GIMME, A_NULL); +} + +void pdp_control_setup(void) +{ + + pdp_control_list = 0; + dropped_packets = 0; + + /* setup pd class data */ + pdp_control_class = class_new(gensym("pdp_control"), (t_newmethod)pdp_control_new, + (t_method)pdp_control_free, sizeof(t_pdp_control), 0, A_NULL); + + + class_addmethod(pdp_control_class, (t_method)pdp_control_info, gensym("info"), A_NULL); + class_addmethod(pdp_control_class, (t_method)pdp_control_thread, gensym("thread"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_control_class, (t_method)pdp_control_collectgarbage, gensym("collectgarbage"), A_NULL); + class_addmethod(pdp_control_class, (t_method)pdp_control_set_mem_limit, gensym("memlimit"), A_FLOAT, A_NULL); +} + + + +void pdp_control_notify_broadcast(t_pdp_control_method_notify *notify) +{ + t_pdp_control *curr = pdp_control_list; + while (curr){ + (*notify)(curr); + curr = curr->x_next; + } +} + + + +/************************* notify class methods *************************/ + +void pdp_control_notify_drop(int packet) +{ + dropped_packets++; + + /* send drop notify to controller class instances */ + pdp_control_notify_broadcast(pdp_control_send_drop_message); + //post("dropped packet"); +} + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_dpd_command.c b/system/kernel/pdp_dpd_command.c new file mode 100644 index 0000000..0d3ecf1 --- /dev/null +++ b/system/kernel/pdp_dpd_command.c @@ -0,0 +1,87 @@ + +/* + * Pure Data Packet header file. DPD command class + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* this object implements a dpd command queue and command object */ + +#include "pdp.h" +#include "pdp_dpd_command.h" + +void pdp_dpd_commandfactory_init(t_pdp_dpd_commandfactory *x, u32 size) +{ + x->nb_commands = 0; + x->command_size = size; + x->command = 0; +} + +void _pdp_dpd_commandfactory_free(t_pdp_dpd_command *x) +{ + if (x) _pdp_dpd_commandfactory_free(x->next); + pdp_dealloc(x); +} + +void pdp_dpd_commandfactory_free(t_pdp_dpd_commandfactory *x) +{ + _pdp_dpd_commandfactory_free(x->command); + x->command = 0; + x->nb_commands = 0; +} + + +/* factory method */ +t_pdp_dpd_command *pdp_dpd_commandfactory_get_new_command(t_pdp_dpd_commandfactory *x) +{ + + t_pdp_dpd_command *c = x->command; + t_pdp_dpd_command *oldhead = c; + + /* check if we can reuse */ + while (c){ + if (!c->used){ + c->used = 1; + //post("reusing command %x", c, c->used); + return c; + } + //post("command %x is used %d", c, c->used); + c = c->next; + } + + /* create a new command */ + x->command = (t_pdp_dpd_command *)pdp_alloc(x->command_size); + x->command->next = oldhead; + x->command->used = 1; + x->nb_commands++; + //post("created command %x, nbcommands: %d", x->command, x->nb_commands); + return x->command; + +} + + +/* (self)destructor */ +void pdp_dpd_command_suicide(void *x) +{ + t_pdp_dpd_command *c = (t_pdp_dpd_command *)x; + c->used = 0; + //post("command %x committed suicide %d", c, c->used); +} + + + + diff --git a/system/kernel/pdp_forth.c b/system/kernel/pdp_forth.c new file mode 100644 index 0000000..1764004 --- /dev/null +++ b/system/kernel/pdp_forth.c @@ -0,0 +1,541 @@ +/* + * Pure Data Packet header file. Packet processor system + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdlib.h> +#include <math.h> +#include "pdp.h" +#include "pdp_forth.h" + +#define D if (0) + + + +t_pdp_stack *pdp_stack_new(void) {return pdp_list_new(0);} + +void pdp_stack_free(t_pdp_stack *s) { + pdp_tree_strip_packets(s); + pdp_list_free(s); +} + + +/* some stack manips */ +t_pdp_word_error pdp_stack_dup(t_pdp_stack *s) +{ + if (!s->first) return e_underflow; + pdp_list_add(s, s->first->t, s->first->w); + + /* copy it properly if its a packet */ + if (s->first->t == a_packet){ + s->first->w.w_packet = pdp_packet_copy_ro(s->first->w.w_packet); + } + return e_ok; +} + +t_pdp_word_error pdp_stack_drop(t_pdp_stack *s) +{ + if (!s->first) return e_underflow; + + /* delete it properly if its a packet */ + if (s->first->t == a_packet){ + pdp_packet_mark_unused(s->first->w.w_packet); + } + pdp_list_pop(s); + + return e_ok; +} + +t_pdp_word_error pdp_stack_over(t_pdp_stack *s) +{ + if (s->elements < 2) return e_underflow; + pdp_list_add(s, s->first->next->t, s->first->next->w); + + /* copy it properly if its a packet */ + if (s->first->t == a_packet){ + s->first->w.w_packet = pdp_packet_copy_ro(s->first->w.w_packet); + } + + return e_ok; +} + +t_pdp_word_error pdp_stack_swap(t_pdp_stack *s) +{ + t_pdp_word w; + t_pdp_word_type t; + if (s->elements < 2) return e_underflow; + w = s->first->w; + t = s->first->t; + s->first->w = s->first->next->w; + s->first->t = s->first->next->t; + s->first->next->w = w; + s->first->next->t = t; + return e_ok; + +} + +/* pushing and popping the stack */ + +t_pdp_word_error pdp_stack_push_float(t_pdp_stack *s, float f) {pdp_list_add(s, a_float, (t_pdp_word)f); return e_ok;} +t_pdp_word_error pdp_stack_push_int(t_pdp_stack *s, int i) {pdp_list_add(s, a_int, (t_pdp_word)i); return e_ok;} +t_pdp_word_error pdp_stack_push_pointer(t_pdp_stack *s, void *x) {pdp_list_add(s, a_pointer, (t_pdp_word)x); return e_ok;} +t_pdp_word_error pdp_stack_push_symbol(t_pdp_stack *s, t_pdp_symbol *x) {pdp_list_add(s, a_symbol, (t_pdp_word)x); return e_ok;} + +/* note: packets pushed stack are owned by the stack. if a caller wants to keep a packet that + will be deleted by the word, it should make a copy before transferring it to the stack. + if a stack processor wants to write to a packet, it should replace it with a writable copy first */ + +t_pdp_word_error pdp_stack_push_packet(t_pdp_stack *s, int p) {pdp_list_add(s, a_packet, (t_pdp_word)p); return e_ok;} + + + + + +t_pdp_word_error pdp_stack_pop_float(t_pdp_stack *s, float *f) +{ + if (!s->first) return e_underflow; + + if (s->first->t == a_float) *f = s->first->w.w_float; + else if (s->first->t == a_int) *f = (float)s->first->w.w_int; + else *f = 0.0f; + pdp_stack_drop(s); + return e_ok; +} + +t_pdp_word_error pdp_stack_pop_int(t_pdp_stack *s, int *i) +{ + if (!s->first) return e_underflow; + if (s->first->t == a_int) *i = s->first->w.w_int; + else if (s->first->t == a_float) *i = (int)s->first->w.w_float; + else *i = 0; + pdp_stack_drop(s); + return e_ok; +} + +t_pdp_word_error pdp_stack_pop_pointer(t_pdp_stack *s, void **x) +{ + if (!s->first) return e_underflow; + *x = (s->first->t == a_pointer) ? s->first->w.w_pointer : 0; + pdp_stack_drop(s); + return e_ok; +} + +t_pdp_word_error pdp_stack_pop_symbol(t_pdp_stack *s, t_pdp_symbol **x) +{ + if (!s->first) return e_underflow; + *x = (s->first->t == a_symbol) ? s->first->w.w_symbol : pdp_gensym("invalid"); + pdp_stack_drop(s); + return e_ok; +} + +/* packets popped from the stack are owned by the caller */ + +t_pdp_word_error pdp_stack_pop_packet(t_pdp_stack *s, int *p) +{ + if (!s->first) return e_underflow; + *p = (s->first->t == a_packet) ? s->first->w.w_packet : -1; + pdp_list_pop(s); //ownership is transferred to receiver, drop kills the packet + return e_ok; +} + + +t_pdp_word_error pdp_stack_mov(t_pdp_stack *s) +{ + int position; + t_pdp_atom *a, *a_before; + if (s->elements < 2) return e_underflow; + if (s->first->t != a_int) return e_type; + + pdp_stack_pop_int(s, &position); // get insert point + if (position < 1) return e_ok; // < 0 : invalid; do nothing, 0 : nop (= insert at start, but already at start) + if ((s->elements-1) < position) return e_underflow; + + a = s->first; // get first atom + s->first = a->next; + + if (s->elements-1 == position){ //insert at end + s->last->next = a; + a->next = 0; + s->last = a; + } + else { //insert somewhere in the middle + a_before = s->first; + while (--position) a_before = a_before->next; + a->next = a_before->next; + a_before->next = a; + } + return e_ok; +} + +/* rotate stack down (tos -> bottom) */ +t_pdp_word_error pdp_stack_rdown(t_pdp_stack *s) +{ + t_pdp_word_type t = s->first->t; + t_pdp_word w = s->first->w; + pdp_list_pop(s); + pdp_list_add_back(s, t, w); + return e_ok; +} + + +/* convert to int */ +t_pdp_word_error pdp_stack_int(t_pdp_stack *s) +{ + int i; + pdp_stack_pop_int(s, &i); + pdp_stack_push_int(s, i); + return e_ok; +} + +/* convert to float */ +t_pdp_word_error pdp_stack_float(t_pdp_stack *s) +{ + float f; + pdp_stack_pop_float(s, &f); + pdp_stack_push_float(s, f); + return e_ok; +} + + +#define OP1(name, type, op) \ +t_pdp_word_error pdp_stack_##name##_##type (t_pdp_stack *s) \ +{ \ + type x0; \ + pdp_stack_pop_##type (s, &(x0)); \ + pdp_stack_push_##type (s, op (x0)); \ + return e_ok; \ +} + +#define OP2(name, type, op) \ +t_pdp_word_error pdp_stack_##name##_##type (t_pdp_stack *s) \ +{ \ + type x0, x1; \ + pdp_stack_pop_##type (s, &(x0)); \ + pdp_stack_pop_##type (s, &(x1)); \ + pdp_stack_push_##type (s, x1 op x0); \ + return e_ok; \ +} + +/* some floating point and integer stuff */ + +OP2(add, float, +); +OP2(sub, float, -); +OP2(mul, float, *); +OP2(div, float, /); + +OP1(sin, float, sin); +OP1(cos, float, cos); + +OP2(add, int, +); +OP2(sub, int, -); +OP2(mul, int, *); +OP2(div, int, /); +OP2(mod, int, %); + +OP2(and, int, &); +OP2(or, int, |); +OP2(xor, int, ^); + + +/* some integer stuff */ + +t_pdp_word_error pdp_stack_push_invalid_packet(t_pdp_stack *s) +{ + pdp_stack_push_packet(s, -1); + return e_ok; +} + +/* dictionary manipulation */ + +void pdp_forth_word_print_debug(t_pdp_symbol *s) +{ + t_pdp_atom *a; + if (!s->s_word_spec){ + post("%s is not a forth word", s->s_name); + } + else{ + post(""); + post("forth word %s", s->s_name); + post("\tinput: %d", s->s_word_spec->input_size); + post("\toutput: %d", s->s_word_spec->output_size); + post("\ttype index: %d", s->s_word_spec->type_index); + + post("\nimplementations:"); + for(a=s->s_word_spec->implementations->first; a; a=a->next){ + t_pdp_forthword_imp *i = a->w.w_pointer; + startpost("\t%s\t", i->type ? i->type->s_name : "anything"); + pdp_list_print(i->def); + + } + post(""); + } +} + +/* add a definition (list of high level words (symbols) or primitive routines) */ +void pdp_forthdict_add_word(t_pdp_symbol *name, t_pdp_list *def, int input_size, int output_size, + int type_index, t_pdp_symbol *type) +{ + t_pdp_forthword_spec *spec = 0; + t_pdp_forthword_imp *imp = 0; + t_pdp_forthword_imp *old_imp = 0; + t_pdp_atom *a; + /* check if the word complies to a previously defined word spec with the same name */ + if (spec = name->s_word_spec){ + if ((spec->input_size != input_size) + ||(spec->output_size != output_size) + ||(spec->type_index != type_index)){ + post("ERROR: pdp_forthdict_add_word: new implementation of [%s] does not comply to old spec", + name->s_name); + return; + } + + } + /* no previous word spec with this name, so create a new spec */ + else{ + spec = name->s_word_spec = (t_pdp_forthword_spec *)pdp_alloc(sizeof(t_pdp_forthword_spec)); + spec->name = name; + spec->input_size = input_size; + spec->output_size = output_size; + spec->type_index = type_index; + spec->implementations = pdp_list_new(0); + } + + /* create the new implementation and add it */ + imp = (t_pdp_forthword_imp *)pdp_alloc(sizeof(t_pdp_forthword_imp)); + imp->name = name; + imp->def = def; + imp->type = type; + + /* can't delete old implemetations because of thread safety */ + pdp_list_add_pointer(spec->implementations, imp); + +} + +/* add a primitive */ +void pdp_forthdict_add_primitive(t_pdp_symbol *name, t_pdp_forthword w, int input_size, int output_size, + int type_index, t_pdp_symbol *type) +{ + t_pdp_list *def = pdp_list_new(1); + def->first->t = a_pointer; + def->first->w.w_pointer = w; + pdp_forthdict_add_word(name, def, input_size, output_size, type_index, type); +} + +/* parse a new definition from a null terminated string */ +t_pdp_list *pdp_forth_compile_def(char *chardef) +{ + t_pdp_list *l; + char *c; + + if (!(l = pdp_list_from_cstring(chardef, &c))){ + post ("ERROR: pdp_forth_compile_def: parse error parsing: %s", chardef); + if (*c) post ("ERROR: remaining input: %s", c); + } + if (*c){ + post ("WARNING: pdp_forth_compile_def: parsing: %s", chardef); + if (*c) post ("garbage at end of string: %s", c); + } + + return l; + +} + +void pdp_forthdict_compile_word(t_pdp_symbol *name, char *chardef, int input_size, int output_size, + int type_index, t_pdp_symbol *type) +{ + /* add the definition list to the dictionary */ + t_pdp_list *def; + + if (def = pdp_forth_compile_def (chardef)) + pdp_forthdict_add_word(name, def, input_size, output_size, type_index, type); + + +} + + + +/* execute a definition list + a def list is a list of primitives, immediates or symbolic words */ +t_pdp_word_error pdp_forth_execute_def(t_pdp_stack *stack, t_pdp_list *def) +{ + t_pdp_atom *a; + t_pdp_word_error e; + t_pdp_forthword w; + float f; + int i,p; + + D post("pdp_forth_execute_def %x %x", stack, def); + D pdp_list_print(def); + + for (a = def->first; a; a=a->next){ + switch(a->t){ + case a_float: // an immidiate float + f = a->w.w_float; + D post("pushing %f onto the stack", f); + pdp_stack_push_float(stack, f); + break; + case a_int: // an immidiate int + i = a->w.w_int; + D post("pushing %d onto the stack", i); + pdp_stack_push_int(stack, i); + break; + case a_packet: // an immidiate int + p = a->w.w_packet; + D post("pushing packet %d onto the stack", p); + pdp_stack_push_packet(stack, pdp_packet_copy_ro(p)); + break; + case a_symbol: // a high level word or an immediate symbol + D post("interpeting symbol %s", a->w.w_symbol->s_name); + if (e = pdp_forth_execute_word(stack, a->w.w_symbol)) return e; + break; + case a_pointer: // a primitive + w = a->w.w_pointer; + D post("exec primitive %x", w); + if (e = (w(stack))) return e; + break; + default: + return e_internal; + + } + } + return e_ok; +} + +/* execute a symbol (a high level word or an immediate) + this routine does the type based word multiplexing and stack checking */ +t_pdp_word_error pdp_forth_execute_word(t_pdp_stack *stack, t_pdp_symbol *word) +{ + t_pdp_symbol *type = 0; + t_pdp_atom *a; + t_pdp_forthword_spec *spec; + t_pdp_forthword_imp *imp = 0; + int i; + + D post("pdp_forth_execute_word %x %x", stack, word); + + /* first check if the word is defined. if not, the symbol will be loaded + onto the stack as an immidiate symbol */ + + if (!(spec = word->s_word_spec)){ + D post ("pushing symbol %s on the stack", word->s_name); + pdp_stack_push_symbol(stack, word); + return e_ok; + } + + D post("exec high level word [%s]", word->s_name); + + /* it is a word. check the stack size */ + if (stack->elements < spec->input_size){ + D post ("error executing [%s]: stack underflow", word->s_name); + return e_underflow; + } + + /* if the word is type oblivious, symply execute the first (only) + implementation in the list */ + if (spec->type_index < 0){ + D post("exec type oblivious word [%s]", word->s_name); + imp = spec->implementations->first->w.w_pointer; + return pdp_forth_execute_def(stack , imp->def); + } + + /* if it is not type oblivious, find the type template + to determine the correct implementation */ + + for(i=spec->type_index,a=stack->first; i--; a=a->next); + switch (a->t){ + /* get type description from first item on*/ + case a_packet: + type = pdp_packet_get_description(a->w.w_packet); break; + case a_symbol: + type = a->w.w_symbol; break; + case a_float: + type = pdp_gensym("float"); break; + case a_int: + type = pdp_gensym("int"); break; + case a_pointer: + type = pdp_gensym("pointer"); break; + default: + /* no type description found on top of stack. */ + type = pdp_gensym("unknown"); + break; + } + + /* scan the implementation list until a definition with matching type is found + if the type spec for a word is NULL, it counts as a match (for generic words) */ + for (a = spec->implementations->first; a; a = a->next){ + imp = a->w.w_pointer; + if ((!imp->type) || pdp_type_description_match(type, imp->type)){ + return pdp_forth_execute_def(stack , imp->def); + } + } + D post("ERROR: pdp_forth_execute_word: type error executing [%s] (2). stack:",word->s_name); + D pdp_list_print(stack); + + return e_type; // type error + +} + + +static void _add_2op(char *name, t_pdp_forthword w, char *type){ + pdp_forthdict_add_primitive(pdp_gensym(name), w, 2, 1, 0, pdp_gensym(type)); +} + +static void _add_1op(char *name, t_pdp_forthword w, char *type){ + pdp_forthdict_add_primitive(pdp_gensym(name), w, 1, 1, 0, pdp_gensym(type)); +} + + +void pdp_forth_setup(void) +{ + + /* add type oblivious (type_index = -1, type = NULL) stack manip primitives */ + pdp_forthdict_add_primitive(pdp_gensym("dup"), (t_pdp_forthword)pdp_stack_dup, 1, 2, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("swap"), (t_pdp_forthword)pdp_stack_swap, 2, 2, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("drop"), (t_pdp_forthword)pdp_stack_drop, 1, 0, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("over"), (t_pdp_forthword)pdp_stack_over, 2, 3, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("mov"), (t_pdp_forthword)pdp_stack_mov, 2, 1, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("down"), (t_pdp_forthword)pdp_stack_rdown, 1, 1, -1, 0); + + /* type converters (casts) */ + pdp_forthdict_add_primitive(pdp_gensym("int"), (t_pdp_forthword)pdp_stack_int, 1, 1, -1, 0); + pdp_forthdict_add_primitive(pdp_gensym("float"), (t_pdp_forthword)pdp_stack_float, 1, 1, -1, 0); + + /* add floating point ops */ + _add_2op("add", (t_pdp_forthword)pdp_stack_add_float, "float"); + _add_2op("sub", (t_pdp_forthword)pdp_stack_sub_float, "float"); + _add_2op("mul", (t_pdp_forthword)pdp_stack_mul_float, "float"); + _add_2op("div", (t_pdp_forthword)pdp_stack_div_float, "float"); + + _add_1op("sin", (t_pdp_forthword)pdp_stack_sin_float, "float"); + _add_1op("cos", (t_pdp_forthword)pdp_stack_cos_float, "float"); + + /* add integer ops */ + _add_2op("add", (t_pdp_forthword)pdp_stack_add_int, "int"); + _add_2op("sub", (t_pdp_forthword)pdp_stack_sub_int, "int"); + _add_2op("mul", (t_pdp_forthword)pdp_stack_mul_int, "int"); + _add_2op("div", (t_pdp_forthword)pdp_stack_div_int, "int"); + _add_2op("mod", (t_pdp_forthword)pdp_stack_mod_int, "int"); + + _add_2op("and", (t_pdp_forthword)pdp_stack_and_int, "int"); + _add_2op("or", (t_pdp_forthword)pdp_stack_or_int, "int"); + _add_2op("xor", (t_pdp_forthword)pdp_stack_xor_int, "int"); + + /* some immidiates */ + pdp_forthdict_add_primitive(pdp_gensym("ip"), (t_pdp_forthword)pdp_stack_push_invalid_packet, 0, 1, -1, 0); + +} diff --git a/system/kernel/pdp_list.c b/system/kernel/pdp_list.c new file mode 100644 index 0000000..ed2ad76 --- /dev/null +++ b/system/kernel/pdp_list.c @@ -0,0 +1,663 @@ + +/* + * Pure Data Packet header file. List class + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* who can live without a list, hmm? */ + +/* rethink the list of list thing: + should free and copy really copy the lists, or should + references to lists be allowed too? + + maybe references to lists as generic pointers? */ + +#include "pdp_list.h" +#include "pdp_types.h" +#include "pdp.h" +#include <pthread.h> +#include <stdlib.h> + +#define D if (0) + +#define PDP_LIST_BLOCKSIZE 4096 + +#define PDP_ATOM_ALLOC _pdp_atom_reuse +#define PDP_ATOM_DEALLOC _pdp_atom_save +//#define PDP_ATOM_ALLOC _pdp_atom_alloc +//#define PDP_ATOM_DEALLOC _pdp_atom_dealloc + +static t_pdp_atom *freelist; +static t_pdp_list *blocklist; +static pthread_mutex_t mut; + +/* memory allocation (for ease of wrapping) */ +static inline t_pdp_list* _pdp_list_alloc(void){return (t_pdp_list *)pdp_alloc(sizeof(t_pdp_list));} +static inline void _pdp_list_dealloc(t_pdp_list *list){pdp_dealloc(list);} +static inline t_pdp_atom* _pdp_atom_alloc(void){return (t_pdp_atom *)pdp_alloc(sizeof(t_pdp_atom));} +static inline void _pdp_atom_dealloc(t_pdp_atom *a){pdp_dealloc(a);} + + + +/* some private helper methods */ + +/* list pool setup */ +void pdp_list_setup(void) +{ + freelist = 0; + blocklist = _pdp_list_alloc(); + blocklist->elements = 0; + blocklist->first = 0; + pthread_mutex_init(&mut, NULL); + +#if 0 + { + char *c = "(test 1 2 (23 4)) (een (zes (ze)ven ()))) (())"; + while (*c){ + t_pdp_list *l = pdp_list_from_char(c, &c); + if (l) pdp_list_print(l); + else{ + post("parse error: remaining input: %s", c); break; + } + } + } +#endif + +} + +/* allocate a block */ +static t_pdp_atom* _pdp_atom_block_alloc(int size) +{ + int i = size; + t_pdp_atom *atom_block, *atom; + atom_block = (t_pdp_atom *)pdp_alloc(sizeof(t_pdp_atom) * size); + atom = atom_block; + while(i--){ + atom->next = atom + 1; + atom->t = a_undef; + atom->w.w_int = 0; + atom++; + } + atom_block[size-1].next = 0; + return atom_block; +} + +static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a); + +static void _pdp_atom_refill_freelist(void) +{ + t_pdp_atom *atom; + + if (freelist != 0) post("ERROR:_pdp_atom_do_reuse: freelist != 0"); + + /* get a new block */ + freelist = _pdp_atom_block_alloc(PDP_LIST_BLOCKSIZE); + //post("new block"); + //_pdp_atomlist_fprint(stderr, freelist); + + /* take the first element from the block to serve as a blocklist element */ + atom = freelist; + atom->t = a_pointer; + atom->w.w_pointer = freelist; + freelist = freelist->next; + atom->next = blocklist->first; + blocklist->first = atom; + blocklist->elements++; + +} + + +/* reuse an old element from the freelist, or allocate a new block */ +static inline t_pdp_atom* _pdp_atom_reuse(void) +{ + t_pdp_atom *atom; + + pthread_mutex_lock(&mut); + + while (!(atom = freelist)){ + _pdp_atom_refill_freelist(); + } + /* delete the element from the freelist */ + freelist = freelist->next; + atom->next = 0; + + pthread_mutex_unlock(&mut); + + return atom; +} + +static inline void _pdp_atom_save(t_pdp_atom *atom) +{ + pthread_mutex_lock(&mut); + + atom->next = freelist; + freelist = atom; + + pthread_mutex_unlock(&mut); +} + + + + + + +/* create a list */ +t_pdp_list* pdp_list_new(int elements) +{ + t_pdp_atom *a = 0; + t_pdp_list *l = _pdp_list_alloc(); + l->elements = 0; + if (elements){ + a = PDP_ATOM_ALLOC(); + l->elements++; + a->t = a_undef; + a->w.w_int = 0; + a->next = 0; + elements--; + } + l->first = a; + l->last = a; + + while (elements--){ + a = PDP_ATOM_ALLOC(); + l->elements++; + a->t = a_undef; + a->w.w_int = 0; + a->next = l->first; + l->first = a; + } + return l; +} + +/* clear a list */ +void pdp_list_clear(t_pdp_list *l) +{ + t_pdp_atom *a = l->first; + t_pdp_atom *next_a; + + while(a){ + next_a = a->next; + PDP_ATOM_DEALLOC(a); + a = next_a; + } + l->first = 0; + l->last = 0; + l->elements = 0; +} + +/* destroy a list */ +void pdp_list_free(t_pdp_list *l) +{ + if (l){ + pdp_list_clear(l); + _pdp_list_dealloc(l); + } +} + + +/* destroy a (sub)tree */ +void pdp_tree_free(t_pdp_list *l) +{ + if (l) { + pdp_tree_clear(l); + _pdp_list_dealloc(l); + } +} + +/* clear a tree */ +void pdp_tree_clear(t_pdp_list *l) +{ + t_pdp_atom *a = l->first; + t_pdp_atom *next_a; + + while(a){ + if (a->t == a_list) pdp_tree_free(a->w.w_list); + next_a = a->next; + PDP_ATOM_DEALLOC(a); + a = next_a; + } + l->first = 0; + l->last = 0; + l->elements = 0; +} + + +static inline int _is_whitespace(char c){return (c == ' ' || c == '\n' || c == '\t');} +static inline void _skip_whitespace(char **c){while (_is_whitespace(**c)) (*c)++;} +static inline int _is_left_separator(char c) {return (c == '(');} +static inline int _is_right_separator(char c) {return (c == ')');} +static inline int _is_separator(char c) {return (_is_left_separator(c) || _is_right_separator(c) || _is_whitespace(c));} + + +static inline void _parse_atom(t_pdp_word_type *t, t_pdp_word *w, char *c, int n) +{ + char tmp[n+1]; + //post("_parse_atom(%d, %x, %s, %d)", *t, w, c, n); + strncpy(tmp, c, n); + tmp[n] = 0; + + + /* check if it's a number */ + if (tmp[0] >= '0' && tmp[0] <= '9'){ + int is_float = 0; + char *t2; + for(t2 = tmp; *t2; t2++){ + if (*t2 == '.') { + is_float = 1; + break; + } + } + /* it's a float */ + if (is_float){ + float f = strtod(tmp, 0); + D post("adding float %f", f); + *t = a_float; + *w = (t_pdp_word)f; + } + + /* it's an int */ + else { + int i = strtol(tmp, 0, 0); + D post("adding int %d", i); + *t = a_int; + *w = (t_pdp_word)i; + } + + } + /* it's a symbol */ + else { + D post("adding symbol %s", tmp); + *t = a_symbol; + *w = (t_pdp_word)pdp_gensym(tmp); + } + + D post("done parsing atom"); + +} + + +/* create a list from a character string */ +t_pdp_list *pdp_list_from_cstring(char *chardef, char **nextchar) +{ + t_pdp_list *l = pdp_list_new(0); + char *c = chardef; + char *lastname = c; + int n = 0; + + D post ("creating list from char: %s", chardef); + + /* find opening parenthesis and skip it*/ + _skip_whitespace(&c); + if (!_is_left_separator(*c)) goto error; else c++; + + /* start counting at the first non-whitespace + character after opening parenthesis */ + _skip_whitespace(&c); + lastname = c; + n = 0; + + while(*c){ + if (!_is_separator(*c)){ + /* item not terminated: keep counting */ + c++; + n++; + } + else { + /* separator encountered whitespace or parenthesis */ + + if (n){ + /* if there was something between this and the previous + separator, we've found and atom and parse it */ + pdp_list_add_back(l, a_undef, (t_pdp_word)0); + _parse_atom(&l->last->t, &l->last->w, lastname, n); + + /* skip the whitespace after the parsed word, if any */ + _skip_whitespace(&c); + } + + /* if we're at a right separator, we're done */ + if (_is_right_separator(*c)) {c++; goto done;} + + /* if it's a left separater, we have a sublist */ + if (_is_left_separator(*c)){ + t_pdp_list *sublist = pdp_list_from_cstring(c, &c); + if (!sublist) goto error; //sublist had parse error + pdp_list_add_back(l, a_list, (t_pdp_word)sublist); + } + + /* prepare for next atom */ + lastname = c; + n = 0; + } + + } + + error: + /* end of string encountered: parse error */ + D post("parse error: %s", c); + if (nextchar) *nextchar = c; + pdp_tree_free(l); //this will free all sublists too + return 0; + + + done: + _skip_whitespace(&c); + if (nextchar) *nextchar = c; + return l; + + + +} + + +/* traversal */ +void pdp_list_apply(t_pdp_list *l, t_pdp_atom_method m) +{ + t_pdp_atom *a; + if (!l) return; + for (a=l->first; a; a=a->next) m(a); +} + +void pdp_tree_apply(t_pdp_list *l, t_pdp_atom_method m) +{ + t_pdp_atom *a; + if (!l) return; + for (a=l->first; a; a=a->next){ + if (a->t == a_list) pdp_tree_apply(a->w.w_list, m); + else m(a); + } +} + +void pdp_list_apply_word_method(t_pdp_list *l, + t_pdp_word_type type, t_pdp_word_method wm) +{ + t_pdp_atom *a; + if (!l) return; + for (a=l->first; a; a=a->next){ + if (a->t == type) wm(a->w); + } +} +void pdp_list_apply_pword_method(t_pdp_list *l, + t_pdp_word_type type, t_pdp_pword_method pwm) +{ + t_pdp_atom *a; + if (!l) return; + for (a=l->first; a; a=a->next){ + if (a->t == type) pwm(&a->w); + } +} + +void pdp_tree_apply_word_method(t_pdp_list *l, + t_pdp_word_type type, t_pdp_word_method wm) +{ + t_pdp_atom *a; + if (!l) return; + for (a=l->first; a; a=a->next){ + if (a->t == a_list) pdp_tree_apply_word_method(a->w.w_list, type, wm); + else if (a->t == type) wm(a->w); + } +} +void pdp_tree_apply_pword_method(t_pdp_list *l, + t_pdp_word_type type, t_pdp_pword_method pwm) +{ + t_pdp_atom *a; + if (!l) return; + for (a=l->first; a; a=a->next){ + if (a->t == a_list) pdp_tree_apply_pword_method(a->w.w_list, type ,pwm); + else if (a->t == type) pwm(&a->w); + } +} + +static void _atom_packet_mark_unused(t_pdp_atom *a) +{ + if (a->t == a_packet){ + pdp_packet_mark_unused(a->w.w_packet); + a->t = a_undef; + a->w.w_int = 0; + } +} + +void pdp_tree_strip_packets (t_pdp_list *l) +{ + pdp_tree_apply(l, _atom_packet_mark_unused); +} + + +/* debug */ +static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a) +{ + fprintf(f, "("); + while (a){ + switch(a->t){ + case a_symbol: fprintf(f, "%s",a->w.w_symbol->s_name); break; + case a_float: fprintf(f, "%f",a->w.w_float); break; + case a_int: fprintf(f, "%d",a->w.w_int); break; + case a_packet: fprintf(f, "#<pdp %d %s>",a->w.w_packet, + pdp_packet_get_description(a->w.w_packet)->s_name); break; + case a_pointer: fprintf(f, "#<0x%08x>",(unsigned int)a->w.w_pointer); break; + case a_list: _pdp_atomlist_fprint(f, a->w.w_list->first); break; + case a_undef: fprintf(f, "undef"); break; + + default: fprintf(f, "unknown"); break; + } + a = a->next; + if (a) fprintf(f, " "); + } + fprintf(f, ")"); +} + +void _pdp_list_fprint(FILE* f, t_pdp_list *l) +{ + _pdp_atomlist_fprint(f, l->first); + fprintf(f, "\n"); +} + +void pdp_list_print(t_pdp_list *l) +{ + _pdp_list_fprint(stderr, l); +} + + +/* public list operations */ + + +/* add a word to the start of the list */ +void pdp_list_add(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *new_a = PDP_ATOM_ALLOC(); + l->elements++; + new_a->next = l->first; + new_a->w = w; + new_a->t = t; + l->first = new_a; + if (!l->last) l->last = new_a; +} + + +/* add a word to the end of the list */ +void pdp_list_add_back(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *new_a = PDP_ATOM_ALLOC(); + l->elements++; + new_a->next = 0; + new_a->w = w; + new_a->t = t; + if (l->last){ + l->last->next = new_a; + } + else{ + l->first = new_a; + } + l->last = new_a; +} + +/* get list size */ +int pdp_list_size(t_pdp_list *l) +{ + return l->elements; +} + + + + +/* pop: return first item and remove */ +t_pdp_word pdp_list_pop(t_pdp_list *l) +{ + t_pdp_atom *a = l->first; + t_pdp_word w; + + w = a->w; + l->first = a->next; + PDP_ATOM_DEALLOC(a); + l->elements--; + if (!l->first) l->last = 0; + + return w; +} + + +/* return element at index */ +t_pdp_word pdp_list_index(t_pdp_list *l, int index) +{ + t_pdp_atom *a; + for (a = l->first; index--; a = a->next); + return a->w; +} + + + + + +/* remove an element from a list */ +void pdp_list_remove(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom head; + t_pdp_atom *a; + t_pdp_atom *kill_a; + head.next = l->first; + + for(a = &head; a->next; a = a->next){ + if (a->next->w.w_int == w.w_int && a->next->t == t){ + kill_a = a->next; // element to be killed + a->next = a->next->next; // remove link + PDP_ATOM_DEALLOC(kill_a); + l->elements--; + l->first = head.next; // restore the start pointer + if (l->last == kill_a) { // restore the end pointer + l->last = (a != &head) ? a : 0; + } + + break; + } + } + +} + + + + + +/* copy a list */ +t_pdp_list* pdp_tree_copy_reverse(t_pdp_list *list) +{ + t_pdp_list *newlist = pdp_list_new(0); + t_pdp_atom *a; + for (a = list->first; a; a = a->next) + if (a->t == a_list){ + pdp_list_add(newlist, a->t, + (t_pdp_word)pdp_tree_copy_reverse(a->w.w_list)); + } + else{ + pdp_list_add(newlist, a->t, a->w); + } + return newlist; +} +t_pdp_list* pdp_list_copy_reverse(t_pdp_list *list) +{ + t_pdp_list *newlist = pdp_list_new(0); + t_pdp_atom *a; + for (a = list->first; a; a = a->next) + pdp_list_add(newlist, a->t, a->w); + return newlist; +} + +t_pdp_list* pdp_tree_copy(t_pdp_list *list) +{ + t_pdp_list *newlist = pdp_list_new(list->elements); + t_pdp_atom *a_src = list->first; + t_pdp_atom *a_dst = newlist->first; + + while(a_src){ + a_dst->t = a_src->t; + if (a_dst->t == a_list){ //recursively copy sublists (tree copy) + a_dst->w.w_list = pdp_tree_copy(a_src->w.w_list); + } + else{ + a_dst->w = a_src->w; + } + a_src = a_src->next; + a_dst = a_dst->next; + } + + return newlist; +} +t_pdp_list* pdp_list_copy(t_pdp_list *list) +{ + t_pdp_list *newlist = pdp_list_new(list->elements); + t_pdp_atom *a_src = list->first; + t_pdp_atom *a_dst = newlist->first; + + while(a_src){ + a_dst->t = a_src->t; + a_dst->w = a_src->w; + a_src = a_src->next; + a_dst = a_dst->next; + } + return newlist; +} + +void pdp_list_cat (t_pdp_list *l, t_pdp_list *tail) +{ + t_pdp_list *tmp = pdp_list_copy(tail); + l->elements += tmp->elements; + l->last->next = tmp->first; + l->last = tmp->last; + _pdp_list_dealloc(tmp); //delete the list stub + +} + + +/* check if a list contains an element */ +int pdp_list_contains(t_pdp_list *list, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *a; + for(a = list->first; a; a=a->next){ + if (a->w.w_int == w.w_int && a->t == t) return 1; + } + return 0; +} + +/* add a thing to the start of the list if it's not in there already */ +void pdp_list_add_to_set(t_pdp_list *list, t_pdp_word_type t, t_pdp_word w) +{ + if (!pdp_list_contains(list, t, w)) + pdp_list_add(list, t, w); +} + + + diff --git a/system/kernel/pdp_packet.c b/system/kernel/pdp_packet.c new file mode 100644 index 0000000..a811381 --- /dev/null +++ b/system/kernel/pdp_packet.c @@ -0,0 +1,957 @@ +/* + * Pure Data Packet system implementation: Packet Manager + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "pdp.h" +#include <stdio.h> +#include <pthread.h> +#include <unistd.h> +#include <string.h> + + +/* TODO: + implement an indexing system to the packet pool (array) to speed up searches. + -> a list of lists of recycled packes, arranged by packet type. + -> a list of unused slots +*/ + + + +#define D if (0) + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* pdp specific constants */ +#define PDP_ALIGN 8 + +/* this needs to be able to grow dynamically, think about it later */ +/* for ordinary work, this is enough and can help finding memory leaks */ + +#define PDP_INITIAL_POOL_SIZE 1 +#define PDP_PACKET_MAX_MEM_USAGE -1 + +/* the pool */ +static unsigned int pdp_packet_mem_usage; +static unsigned int pdp_packet_max_mem_usage; +static int pdp_pool_size; +static t_pdp** pdp_pool; + +/* this is for detecting memory leaks */ +static int pdp_packet_count; + +/* some global vars */ +static t_symbol* pdp_sym_register_rw; +static t_symbol* pdp_sym_register_ro; +static t_symbol* pdp_sym_process; + +/* mutex */ +static pthread_mutex_t pdp_pool_mutex; + +/* the list of classes */ +static t_pdp_list *class_list; + +/* debug */ +void +pdp_packet_print_debug(int packet) +{ + t_pdp *h = pdp_packet_header(packet); + post("debug info for packet %d", packet); + if (!h){ + post("invalid packet"); + } + else{ + post ("\ttype: %d", h->type); + post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown"); + post ("\tsize: %d", h->size); + post ("\tflags: %x", h->flags); + post ("\tusers: %d", h->users); + post ("\trefloc: %x", h->refloc); + post ("\tclass: %x", h->theclass); + } +} + + + +/* setup methods */ + +void +pdp_packet_setup(void) +{ + + pdp_pool_size = PDP_INITIAL_POOL_SIZE; + pdp_packet_count = 0; + pdp_packet_mem_usage = 0; + pdp_packet_max_mem_usage = PDP_PACKET_MAX_MEM_USAGE; + pdp_pool = (t_pdp **)malloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *)); + bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *)); + pdp_sym_register_rw = gensym("register_rw"); + pdp_sym_register_ro = gensym("register_ro"); + pdp_sym_process = gensym("process"); + pdp_packet_count = 0; + class_list = pdp_list_new(0); + + pthread_mutex_init(&pdp_pool_mutex, NULL); +} + +/* class methods */ +t_pdp_class *pdp_class_new(t_pdp_symbol *type, t_pdp_factory_method create){ + t_pdp_class *c = (t_pdp_class *)pdp_alloc(sizeof(t_pdp_class)); + memset(c, 0, sizeof(t_pdp_class)); + c->create = create; + c->type = type; // set type + //c->attributes = pdp_list_new(0); // create an empty attribute list + pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c)); + //post("added class %s %x", c->type->s_name, c->create); + + return c; +} + +#if 0 +void pdp_class_addmethod(t_pdp_class *c, t_pdp_symbol *name, t_pdp_attribute_method method, + t_pdp_list *in_spec, t_pdp_list *out_spec) +{ + t_pdp_attribute *attr = (t_pdp_attribute *)pdp_alloc(sizeof(t_pdp_attribute)); + attr->name = name; + attr->method = method; + attr->in_spec = in_spec; + attr->out_spec = out_spec; + pdp_list_add_pointer(c->attributes, attr); + +} +#endif + +/* the packet factory */ +int pdp_factory_newpacket(t_pdp_symbol *type) +{ + t_pdp_class *c; + t_pdp_atom *a = class_list->first; + while(a){ + c = (t_pdp_class *)(a->w.w_pointer); + if (c->type && pdp_type_description_match(type, c->type)){ + //post("method %x, type %s", c->create, type->s_name); + return (c->create) ? (*c->create)(type) : -1; + } + a = a->next; + } + return -1; +} + +#if 0 +/* generic methods. actually a forth word operation on a stack. + first item on stack must be a packet and it's type will + determine the place to look for the operator */ +int pdp_packet_op(t_pdp_symbol *operation, struct _pdp_list *stack) +{ + int packet = stack->first->w.w_packet; + t_pdp *h = pdp_packet_header(packet); + t_pdp_atom *i; + t_pdp_attribute *attr; + + if (!(h && h->theclass)) goto exit; + + PDP_POINTER_IN(h->theclass->attributes, i, attr){ + //post("attribute:%s", attr->name->s_name); + if (attr->name == operation){ + + /* FOUND -> exec (check arguments first ???) */ + return attr->method (stack); + } + + } + exit: + // method not found + post ("WARNING: pdp_packet_op: packet %d from class %s has no operation %s", + packet, (h && h->theclass) ? h->theclass->type->s_name : "UNKNOWN", operation->s_name); + return 0; +} + +#endif + +static void +_pdp_pool_expand(void){ + int i; + + /* double the size */ + int new_pool_size = pdp_pool_size << 1; + t_pdp **new_pool = (t_pdp **)malloc(new_pool_size * sizeof(t_pdp *)); + bzero(new_pool, new_pool_size * sizeof(t_pdp *)); + memcpy(new_pool, pdp_pool, pdp_pool_size * sizeof(t_pdp *)); + free(pdp_pool); + pdp_pool = new_pool; + pdp_pool_size = new_pool_size; + + D post("DEBUG: _pdp_pool_expand: resized pool to contain %d packets", pdp_pool_size); +} + + + + +/* private _pdp_packet methods */ + +/* packets can only be created and destroyed using these 2 methods */ +/* it updates the mem usage and total packet count */ + +static void +_pdp_packet_dealloc(t_pdp *p) +{ + unsigned int size = p->size; + if (p->theclass && p->theclass->cleanup) + (*p->theclass->cleanup)(p); + + free (p); + pdp_packet_mem_usage -= size; + pdp_packet_count--; + D post("DEBUG: _pdp_packet_new_dealloc: freed packet. pool contains %d packets", pdp_packet_count); +} + +static t_pdp* +_pdp_packet_alloc(unsigned int datatype, unsigned int datasize) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + unsigned int align; + t_pdp *p = 0; + + /* check if there is a memory usage limit */ + if (pdp_packet_max_mem_usage){ + /* if it would exceed the limit, fail */ + if (pdp_packet_mem_usage + totalsize > pdp_packet_max_mem_usage){ + D post("DEBUG: _pdp_packet_new_alloc: memory usage limit exceeded"); + return 0; + } + } + p = (t_pdp *)malloc(totalsize); + if (p){ + align = ((unsigned int)p) & (PDP_ALIGN - 1); + if (align) post("WARNING: _pdp_packet_alloc: data misaligned by %x", align); + memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0 + p->type = datatype; + p->size = totalsize; + p->users = 1; + pdp_packet_mem_usage += totalsize; + pdp_packet_count++; + D post("DEBUG: _pdp_packet_new_alloc: allocated new packet. pool contains %d packets, using %d bytes", + pdp_packet_count, pdp_packet_mem_usage); + } + + return p; +} + + +void +pdp_packet_destroy(void) +{ + int i = 0; + /* dealloc all the data in object stack */ + post("DEBUG: pdp_packet_destroy: clearing object pool."); + while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc(pdp_pool[i++]); +} + + + +/* try to find a packet based on main type and datasize */ +static int +_pdp_packet_reuse_type_size(unsigned int datatype, unsigned int datasize) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + int i = 0; + int return_packet = -1; + t_pdp* p; + + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + /* check if we can reuse this one if it is already allocated */ + if (p) { + /* search for unused packets of equal size & type */ + if ((p->users == 0) && (p->size == totalsize) && (p->type == datatype)){ + D post("DEBUG: _pdp_packet_reuse_type_size: can reuse %d", i); + + /* if possible, a packet will be reused and reinitialized + i haven't found a use for this, so it's only used for discriminating + between pure and not-so-pure packets */ + if (p->theclass && p->theclass->reinit){ + (*p->theclass->reinit)(p); + } + /* if no re-init method is found, the header will be reset to all 0 + this ensures the header is in a predictable state */ + else { + memset(p, 0, PDP_HEADER_SIZE); + p->type = datatype; + p->size = totalsize; + } + + p->users = 1; + return_packet = i; + goto exit; + } + else{ + D post("DEBUG _pdp_packet_reuse_type_size: can't reuse %d, (%d users)", i, p->users); + } + } + } + + D post("DEBUG: _pdp_packet_reuse_type_size: no reusable packet found"); + + exit: + return return_packet; +} + + +/* create a new packet in an empty slot. + if this fails, the garbage collector needs to be called */ +static int +_pdp_packet_create_in_empty_slot(unsigned int datatype, unsigned int datasize /*without header*/) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + int i = 0; + int return_packet = -1; + int out_of_mem = 0; + t_pdp* p; + + + /* no reusable packets found, try to find an empty slot */ + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + if (!p) { + p = _pdp_packet_alloc(datatype, datasize); + + if (!p) { + D post("DEBUG: _pdp_packet_create_in_empty_slot: out of memory (malloc returned NULL)"); + return_packet = -1; + goto exit; + } + + pdp_pool[i] = p; + return_packet = i; + goto exit; + } + } + + /* if we got here the pool is full: resize the pool and try again */ + _pdp_pool_expand(); + return_packet = _pdp_packet_create_in_empty_slot(datatype, datasize); + + exit: + return return_packet; + +} + +/* find an unused packet, free it and create a new packet. + if this fails, something is seriously wrong */ +static int +_pdp_packet_create_in_unused_slot(unsigned int datatype, unsigned int datasize /*without header*/) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + int i = 0; + int return_packet = -1; + int out_of_mem = 0; + t_pdp* p; + + D post("DEBUG: _pdp_packet_create_in_unused_slot: collecting garbage"); + + /* search backwards */ + for (i=pdp_pool_size-1; i >= 0; i--){ + p = pdp_pool[i]; + if (p){ + if (p->users == 0){ + _pdp_packet_dealloc(p); + p = _pdp_packet_alloc(datatype, datasize); + pdp_pool[i] = p; + + /* alloc succeeded, return */ + if (p) { + post("DEBUG _pdp_packet_create_in_unused_slot: garbage collect succesful"); + return_packet = i; + goto exit; + } + + /* alloc failed, continue collecting garbage */ + D post("DEBUG _pdp_packet_create_in_unused_slot: freed one packet, still out of memory (malloc returned NULL)"); + out_of_mem = 1; + + } + } + } + + /* if we got here, we ran out of memory */ + D post("DEBUG: _pdp_packet_create_in_unused_slot: out of memory after collecting garbage"); + return_packet = -1; + +exit: + return return_packet; +} + +/* warning: for "not so pure" packets, this method will only return an initialized + packet if it can reuse a privious one. that is, if it finds a reinit method + in the packet. use the pdp_packet_new_<type> constructor if possible */ + +static int +_pdp_packet_brandnew(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int return_packet = -1; + + /* try to create a new packet in an empty slot */ + return_packet = _pdp_packet_create_in_empty_slot(datatype, datasize); + if (return_packet != -1) goto exit; + + /* if we still don't have a packet, we need to call the garbage collector until we can allocate */ + return_packet = _pdp_packet_create_in_unused_slot(datatype, datasize); + + exit: + return return_packet; +} + + +static int +_pdp_packet_new(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int return_packet = -1; + + /* try to reuse a packet based on main type and datasize */ + return_packet = _pdp_packet_reuse_type_size(datatype, datasize); + if (return_packet != -1) goto exit; + + /* create a brandnew packet */ + return_packet = _pdp_packet_brandnew(datatype, datasize); + if (return_packet != -1) goto exit; + + post("WARNING: maximum packet memory usage limit (%d bytes) reached after garbage collect.", pdp_packet_max_mem_usage); + post("WARNING: increase memory limit or decrease packet usage (i.e. pdp_loop, pdp_delay)."); + post("WARNING: pool contains %d packets, using %d bytes.", pdp_packet_count, pdp_packet_mem_usage); + + exit: + return return_packet; + +} + + + +/* public pool operations: have to be thread safe so each entry point + locks the mutex */ + +/* reuse an old packet based on high level description */ +int +pdp_packet_reuse(t_pdp_symbol *description) +{ + int i; + int return_packet = -1; + t_pdp *p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + /* check if we can reuse this one if it is already allocated */ + if ((p) && (p->users == 0) && (p->desc == description)){ + /* mark it as used */ + p->users = 1; + return_packet = i; + goto gotit; + } + } + + gotit: + /* LOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return return_packet; + + +} + +/* create a new packet, or reuse an old one based on main type and size */ +int +pdp_packet_new(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int packet; + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + packet = _pdp_packet_new(datatype, datasize); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + return packet; +} + + +/* create a brand new packet, don't reuse an old one */ +int +pdp_packet_brandnew(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int packet; + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + packet = _pdp_packet_brandnew(datatype, datasize); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + return packet; +} + + +/* this returns a copy of a packet for read only access. */ +int +pdp_packet_copy_ro(int handle) +{ + int out_handle = -1; + t_pdp* p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if ((handle >= 0) + && (handle < pdp_pool_size) + && (p = pdp_pool[handle])){ + + /* it is an error to copy a packet without an owner */ + if (!p->users){ + post("pdp_packet_copy_ro: ERROR: request to copy packet %d which has 0 users", handle); + out_handle = -1; + } + + /* if it's a passing packet, reset the reference location + and turn it into a normal packet */ + else if (p->refloc){ + *p->refloc = -1; + p->refloc = 0; + out_handle = handle; + } + /* if it's a normal packet, increase the number of users */ + else { + p->users++; + out_handle = handle; + } + } + else out_handle = -1; + + //post("pdp_copy_ro: outhandle:%d", out_handle); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return out_handle; +} + +/* copy a packet. if the packet is marked passing, it will be aquired. + otherwize a new packet will be created with a copy of the contents. */ + +int +pdp_packet_copy_rw(int handle) +{ + int out_handle = -1; + t_pdp* p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if ((handle >= 0) + && (handle < pdp_pool_size) + && (p = pdp_pool[handle])){ + /* if there are other users, copy the object otherwize return the same handle */ + + /* it is an error to copy a packet without an owner */ + if (!p->users){ + post("pdp_packet_copy_rw: ERROR: request to copy packet %d which has 0 users", handle); + out_handle = -1; + } + + /* if it is a passing packet, remove the owner's reference to it */ + else if (p->refloc){ + *p->refloc = -1; + p->refloc = 0; + out_handle = handle; + } + + /* check if packet supports copy (for fanout) */ + else if(p->flags & PDP_FLAG_DONOTCOPY) out_handle = -1; + + + /* copy the packet, since it already has 1 or more users */ + else{ + int new_handle = _pdp_packet_new(p->type, p->size - PDP_HEADER_SIZE); + t_pdp* new_p = pdp_packet_header(new_handle); + + /* check if valid */ + if (!new_p) out_handle = -1; + + else { + /* if a copy constructor is found, it will be called */ + if (p->theclass && p->theclass->copy){ + (*p->theclass->copy)(new_p, p); + } + /* if not, the entire packet will be copied, assuming a pure data packet */ + else { + memcpy(new_p, p, p->size); + } + new_p->users = 1; + out_handle = new_handle; + } + } + + //post("pdp_copy_rw: inhandle:%d outhandle:%d", handle, out_handle); + + } + else out_handle = -1; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + return out_handle; +} + +/* create a new packet, copying the header data of another + packet, without copying the data */ +int +pdp_packet_clone_rw(int handle) +{ + int out_handle; + t_pdp* p; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if ((handle >= 0) + && (handle < pdp_pool_size) + && (p = pdp_pool[handle])){ + + /* clone the packet header, don't copy the data */ + int new_handle = _pdp_packet_new(p->type, p->size - PDP_HEADER_SIZE); + t_pdp* new_p = pdp_packet_header(new_handle); + + /* if a clone initializer is found, it will be called */ + if (p->theclass && p->theclass->clone){ + (*p->theclass->clone)(new_p, p); + } + /* if not, just the header will be copied, assuming a pure data packet */ + else { + memcpy(new_p, p, PDP_HEADER_SIZE); + } + new_p->users = 1; /* the new packet has 1 user */ + new_p->refloc = 0; /* it is not a passing packet, even if the template was */ + out_handle = new_handle; + } + + else out_handle = -1; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return out_handle; +} + +void +_pdp_packet_mark_unused_nolock(int handle) +{ + t_pdp* p; + + if ((handle >= 0) && (handle < pdp_pool_size)){ + if (p = pdp_pool[handle]) { + /* mark_unused on a passing packet has no effect + this is to support automatic conversion for passing packets + so in order to delete a passing packet, it should be marked normal first */ + if (p->refloc){ + post("DEBUG: pdp_mark_unused called on a passing packet. ignored."); + return; + } + + /* decrease the refcount */ + if (p->users) { + p->users--; + //post("pdp_mark_unused: handle %d, users left %d", handle, p->users); + } + else { + post("pdp_mark_unused: ERROR: handle %d has zero users (duplicate pdp_mark_unused ?)", handle); + } + } + else { + post("pdp_mark_unused: ERROR: invalid handle %d: no associated object", handle); + } + } + + else { + /* -1 is the only invalid handle that doesn't trigger a warning */ + if (handle != -1) post("pdp_mark_unused: WARNING: invalid handle %d: out of bound", handle); + } + +} + +/* mark a packet as unused, decreasing the reference count. + if the reference count reaches zero, the packet is ready to be recycled + by a new packet allocation. it is illegal to reference a packet with + reference count == zero. if the reference count is not == 1, only readonly + access is permitted. */ + +void +pdp_packet_mark_unused(int handle) +{ + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + _pdp_packet_mark_unused_nolock(handle); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); +} + +void +pdp_packet_mark_unused_atomic(int *handle) +{ + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + _pdp_packet_mark_unused_nolock(*handle); + *handle = -1; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); +} + +/* delete a packet. this is more than mark_unused: + it actually removes any reference. can be used for + some packet types that have "expensive" resources. + usually this is up to the garbage collector to call. */ + +void +pdp_packet_delete(int handle) +{ + t_pdp *header = pdp_packet_header(handle); + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + + /* check if it's a valid handle */ + if ((handle >= 0) && (handle < pdp_pool_size) && pdp_pool[handle]){ + + /* mark it unused */ + _pdp_packet_mark_unused_nolock(handle); + + /* if no users, dealloc */ + if (!header->users){ + _pdp_packet_dealloc(header); + pdp_pool[handle] = NULL; + } + + /* print a warning if failed */ + else{ + post("WARNING: pdp_packet_delete: packet %d was not deleted. %d users remaining", + handle, header->users); + } + } + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + +} + + +/* this turns any packet into a normal (non-passing) packet */ + +void +pdp_packet_unmark_passing(int packet) +{ + t_pdp* header = pdp_packet_header(packet); + if (!header) return; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + header->refloc = 0; + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + +} + + +/* this marks a packet as passing. this means it changes + owner on first copy_ro or copy_rw. the previous owner is + notified by setting the handler to -1. */ + +void +pdp_packet_mark_passing(int *phandle) +{ + t_pdp* header = pdp_packet_header(*phandle); + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + if (header){ + if (header->refloc){ + post("pdp_packet_mark_passing: ERROR: duplicate mark_passing on packet %d", *phandle); + } + else if (1 != header->users){ + post("pdp_packet_mark_passing: ERROR: packet %d is not exclusively owned by caller, it has %d users", *phandle, header->users); + } + else { + header->refloc = phandle; + } + } + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + +} + + +/* public data access methods */ + +t_pdp* +pdp_packet_header(int handle) +{ + if ((handle >= 0) && (handle < pdp_pool_size)) return pdp_pool[handle]; + else return 0; +} + +void* +pdp_packet_subheader(int handle) +{ + t_pdp* header = pdp_packet_header(handle); + if (!header) return 0; + return (void *)(&header->info.raw); +} + +void* +pdp_packet_data(int handle) +{ + t_pdp *h; + if ((handle >= 0) && (handle < pdp_pool_size)) + { + h = pdp_pool[handle]; + if (!h) return 0; + return (char *)(h) + PDP_HEADER_SIZE; + } + else return 0; +} + + +/* OBSOLETE: use packet description */ +/* check if two packets are allocated and of the same type */ +bool pdp_packet_compat(int packet0, int packet1) +{ + + t_pdp *header0 = pdp_packet_header(packet0); + t_pdp *header1 = pdp_packet_header(packet1); + + if (!(header1)){ + //post("pdp_type_compat: invalid header packet1"); + return 0; + } + if (!(header0)){ + //post("pdp_type_compat: invalid header packet 0"); + return 0; + } + if (header0->type != header1->type){ + //post("pdp_type_compat: types do not match"); + return 0; + } + + return 1; +} + +int pdp_packet_writable(int packet) /* returns true if packet is writable */ +{ + t_pdp *h = pdp_packet_header(packet); + if (!h) return 0; + return (h->users == 1); +} + +void pdp_packet_replace_with_writable(int *packet) /* replaces a packet with a writable copy */ +{ + int new_p; + if (!pdp_packet_writable(*packet)){ + new_p = pdp_packet_copy_rw(*packet); + pdp_packet_mark_unused(*packet); + *packet = new_p; + } + +} + +/* pool stuff */ + +int +pdp_pool_collect_garbage(void) +{ + t_pdp *p; + int i; + int nbpackets = pdp_packet_count; + + /* LOCK */ + pthread_mutex_lock(&pdp_pool_mutex); + + for (i=0; i < pdp_pool_size; i++){ + p = pdp_pool[i]; + if(p && !p->users) { + _pdp_packet_dealloc(p); + pdp_pool[i] = 0; + } + } + nbpackets -= pdp_packet_count; + //post("pdp_pool_collect_garbage: deleted %d unused packets", nbpackets); + + /* UNLOCK */ + pthread_mutex_unlock(&pdp_pool_mutex); + + return nbpackets; +} + +void +pdp_pool_set_max_mem_usage(int max) +{ + if (max < 0) max = 0; + pdp_packet_max_mem_usage = max; + +} + + + + + + +/* malloc wrapper that calls garbage collector */ +void *pdp_alloc(int size) +{ + void *ptr = malloc(size); + + //post ("malloc called for %d bytes", size); + + if (ptr) return ptr; + + post ("malloc failed in a pdp module: running garbage collector."); + + pdp_pool_collect_garbage(); + return malloc(size); +} + + +void pdp_dealloc(void *stuff) +{ + free (stuff); +} + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_queue.c b/system/kernel/pdp_queue.c new file mode 100644 index 0000000..665a236 --- /dev/null +++ b/system/kernel/pdp_queue.c @@ -0,0 +1,347 @@ +/* + * Pure Data Packet - processor queue module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +/* + this is a the processor queue pdp system module + it receives tasks from objects that are schedules to + be computed in another thread. the object is signalled back + when the task is completed. + + this is not a standard pd class. it is a sigleton class + using a standard pd clock to poll for compleded methods on + every scheduler run. this is a hack to do thread synchronization + in a thread unsafe pd. + + the queue object can be reused. the pdp system however only + has one instance (one pdp queue. pdp remains a serial program.) + + */ + +#include "pdp.h" +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> + + +#define D if (0) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define PDP_QUEUE_LOGSIZE 10 +#define PDP_QUEUE_DELTIME 10.0f + + + + +/********************* general purpose pd process queue class *********************/ + +void pdp_procqueue_wait(t_pdp_procqueue *q) +{ + D post("pdp_procqueue_wait(%x): waiting for pdp_queue_thread to finish processing", q); + pthread_mutex_lock(&q->mut); + while(((q->curr - q->head) & q->mask) != 0){ + + pthread_cond_wait(&q->cond_processingdone, &q->mut); + } + pthread_mutex_unlock(&q->mut); + D post("pdp_procqueue_wait(%x): pdp_procqueue_thread has finished processing", q); + +} +void pdp_procqueue_finish(t_pdp_procqueue *q, int index) +{ + + if (-1 == index) { + //post("pdp_pq_remove: index == -1"); + return; + } + /* wait for processing thread to finish*/ + pdp_procqueue_wait(q); + + /* invalidate callback at index */ + q->q[index & q->mask].x_callback = 0; + q->q[index & q->mask].x_queue_id = 0; + +} + +static void pdp_procqueue_signal_processor(t_pdp_procqueue *q) +{ + + //NOTE: uncommenting these post statements causes a libc crash + //in mutex lock in putc + //D post("pdp_procqueue_signal_processor(%x): signalling process thread", q); + pthread_mutex_lock(&q->mut); + pthread_cond_signal(&q->cond_dataready); + pthread_mutex_unlock(&q->mut); + //D post("pdp_procqueue_signal_processor(%x): signalling done", q); + + +} + +static void pdp_procqueue_wait_for_feeder(t_pdp_procqueue *q) +{ + + + /* only use locking when there is no data */ + if(((q->curr - q->head) & q->mask) == 0){ + + /* signal processing done */ + D post("pdp_procqueue_wait_for_feeder(%x): signalling processing is done", q); + pthread_mutex_lock(&q->mut); + pthread_cond_signal(&q->cond_processingdone); + + /* wait until there is an item in the queue */ + while(((q->curr - q->head) & q->mask) == 0){ + pthread_cond_wait(&q->cond_dataready, &q->mut); + } + + pthread_mutex_unlock(&q->mut); + D post("pdp_procqueue_wait_for_feeder(%x): waiting done", q); + + } +} + +void pdp_procqueue_add(t_pdp_procqueue *q, void *owner, void *process, void *callback, int *queue_id) +{ + int i; + + /* if processing is in not in thread, just call the funcs */ + if (!q->use_thread){ + D post("pdp_procqueue_add(%q): calling processing routine directly", q); + if (queue_id) *queue_id = -1; + if (process) ((t_pdpmethod) process)(owner); + if (callback) ((t_pdpmethod) callback)(owner); + return; + } + + + /* if queue is full, call directly */ + if (1 == ((q->tail - q->head) & q->mask)) { + //post("pdp_procqueue_add: WARNING: processing queue (%x) is full.\n", q); + //post("pdp_procqueue_add: WARNING: tail %x, head %x, mask %x.\n", q->tail, q->head, q->mask); + //post("pdp_procqueue_add: WARNING: skipping process method, calling callback directly.\n"); + if (queue_id) *queue_id = -1; + if (callback) ((t_pdpmethod) callback)(owner); + return; + //exit(1); + } + + /* schedule method in thread queue */ + i = q->head & q->mask; + q->q[i].x_owner = owner; + q->q[i].x_process = process; + q->q[i].x_callback = callback; + q->q[i].x_queue_id = queue_id; + if (queue_id) *queue_id = i; + //post("pdp_queue_add: added method to queue, index %d", i); + + + // increase the packet count + q->packets++; + + // move head forward + q->head++; + + pdp_procqueue_signal_processor(q); + +} + + +/* processing thread */ +static void *pdp_procqueue_thread(void *vq) +{ + t_pdp_procqueue *q = (t_pdp_procqueue *)vq; + + D post("pdp_procqueue_thread(%x): thread started", q); + + while(1){ + t_process_queue_item *p; + + + D post("pdp_procqueue_thread(%x): waiting for feeder", q); + + /* wait until there is data available */ + pdp_procqueue_wait_for_feeder(q); + + + D post("pdp_procqueue_thread(%x): processing %d", q, q->curr & q->mask); + + + /* call the process routine */ + p = &q->q[q->curr & q->mask]; + if (p->x_process) + (p->x_process)(p->x_owner); + + /* advance */ + q->curr++; + + + } +} + + +/* call back all the callbacks */ +static void pdp_procqueue_callback (t_pdp_procqueue *q) +{ + + /* call callbacks for finished packets */ + while(0 != ((q->curr - q->tail) & q->mask)) + { + int i = q->tail & q->mask; + /* invalidate queue id */ + if(q->q[i].x_queue_id) *q->q[i].x_queue_id = -1; + /* call callback */ + if(q->q[i].x_callback) (q->q[i].x_callback)(q->q[i].x_owner); + //else post("pdp_pq_tick: callback %d is disabled",i ); + q->tail++; + } + +} + +/* the clock method */ +static void pdp_procqueue_tick (t_pdp_procqueue *q) +{ + /* do work */ + //if (!(ticks % 1000)) post("pdp tick %d", ticks); + + if (!q->use_thread) return; + + /* call callbacks */ + pdp_procqueue_callback(q); + + /* increase counter */ + q->ticks++; + + /* set clock for next update */ + clock_delay(q->pdp_clock, q->deltime); +} + + + +void pdp_procqueue_use_thread(t_pdp_procqueue* q, int t) +{ + /* if thread usage is being disabled, + wait for thread to finish processing first */ + if (t == 0) { + pdp_procqueue_wait(q); + q->use_thread = 0; + pdp_procqueue_callback(q); + clock_unset(q->pdp_clock); + } + else { + clock_unset(q->pdp_clock); + clock_delay(q->pdp_clock, q->deltime); + q->use_thread = 1; + } + +} + +void pdp_procqueue_init(t_pdp_procqueue *q, double milliseconds, int logsize) +{ + pthread_attr_t attr; + int size = 1 << logsize; + + /* setup pdp queue processor object */ + q->ticks = 0; + q->deltime = milliseconds; + + /* setup queue data */ + q->mask = size - 1; + q->head = 0; + q->tail = 0; + q->curr = 0; + q->q = pdp_alloc(size * sizeof(t_process_queue_item)); + memset(q->q, 0, size * sizeof(t_process_queue_item)); + + /* enable threads */ + q->use_thread = 1; + + /* setup synchro stuff */ + pthread_mutex_init(&q->mut, NULL); + pthread_cond_init(&q->cond_dataready, NULL); + pthread_cond_init(&q->cond_processingdone, NULL); + + + /* allocate the clock */ + q->pdp_clock = clock_new(q, (t_method)pdp_procqueue_tick); + + /* set the clock */ + clock_delay(q->pdp_clock, 0); + + /* start processing thread */ + + /* glibc doc says SCHED_OTHER is default, + but it seems not to be when initiated from a RT thread + so we explicitly set it here */ + pthread_attr_init (&attr); + //pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + + D post("pdp_procqueue_init(%x): starting thread", q); + pthread_create(&q->thread_id, &attr, pdp_procqueue_thread, (void *)q); + D post("pdp_procqueue_init(%x): back in pd thread", q); + + /* wait for processing thread to finish */ + //pdp_procqueue_wait(q); + + /* set default disable/enable thread here */ + //post("pdp_queue: THREAD PROCESSING ON BY DEFAULT!!"); + pdp_procqueue_use_thread(q,0); + +} + + + + +/* the (static) pdp queue object */ +static t_pdp_procqueue pdp_queue; + + +/* get the default queue */ +t_pdp_procqueue *pdp_queue_get_queue(void){return &pdp_queue;} + + +#if 1 +/* default pdp queue shortcut methods */ +void pdp_queue_wait() {pdp_procqueue_wait(&pdp_queue);} +void pdp_queue_finish(int index) { pdp_procqueue_finish(&pdp_queue, index);} +void pdp_queue_add(void *owner, void *process, void *callback, int *queue_id) { + pdp_procqueue_add(&pdp_queue, owner, process, callback, queue_id); +} +void pdp_queue_use_thread(int t) {pdp_procqueue_use_thread(&pdp_queue, t);} +void pdp_queue_setup(void){ + pdp_procqueue_init(&pdp_queue, PDP_QUEUE_DELTIME, PDP_QUEUE_LOGSIZE); + pdp_procqueue_use_thread(&pdp_queue,0); +} +#endif + + + + + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_type.c b/system/kernel/pdp_type.c new file mode 100644 index 0000000..c220269 --- /dev/null +++ b/system/kernel/pdp_type.c @@ -0,0 +1,501 @@ +/* + * Pure Data Packet system implementation. : code for handling different packet types + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this file contains type handling routines */ + +#include <stdarg.h> +#include <string.h> +#include <pthread.h> +#include "pdp.h" + + +// debug +#define D if (0) + + +static t_pdp_list *conversion_list; + +#define INIT_MAX_CACHE_SIZE 32 + +static t_pdp_list *cached_conversion_list; +static int max_cache_size; + +/* mutex */ +static pthread_mutex_t pdp_hash_mutex; +static pthread_mutex_t pdp_conversion_mutex; +static pthread_mutex_t pdp_cache_mutex; + +#define HASHSIZE 1024 +static t_pdp_symbol *pdp_symhash[HASHSIZE]; + + +/* shamelessly copied from pd src and made thread safe */ +t_pdp_symbol *_pdp_dogensym(char *s, t_pdp_symbol *oldsym) +{ + t_pdp_symbol **sym1, *sym2; + unsigned int hash1 = 0, hash2 = 0; + int length = 0; + char *s2 = s; + while (*s2) + { + hash1 += *s2; + hash2 += hash1; + length++; + s2++; + } + sym1 = pdp_symhash + (hash2 & (HASHSIZE-1)); + + /* lock hash */ + pthread_mutex_lock(&pdp_hash_mutex); + + while (sym2 = *sym1) + { + if (!strcmp(sym2->s_name, s)) goto gotit; + sym1 = &sym2->s_next; + } + if (oldsym) sym2 = oldsym; + else + { + sym2 = (t_pdp_symbol *)pdp_alloc(sizeof(*sym2)); + sym2->s_name = pdp_alloc(length+1); + _pdp_symbol_clear_namespaces(sym2); + sym2->s_next = 0; + strcpy(sym2->s_name, s); + } + *sym1 = sym2; + + gotit: + + /* unlock hash */ + pthread_mutex_unlock(&pdp_hash_mutex); + return (sym2); +} + +t_pdp_symbol *pdp_gensym(char *s) +{ + return(_pdp_dogensym(s, 0)); +} + +/* convert a type to a list */ +t_pdp_list *pdp_type_to_list(t_pdp_symbol *type) +{ + char *c = type->s_name; + char *lastname = c; + char tmp[100]; + int n = 0; + t_pdp_list *l = pdp_list_new(0); + + while(*c){ + if (*c == '/'){ + strncpy(tmp, lastname, n); + tmp[n] = 0; + pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(tmp)); + c++; + lastname = c; + n = 0; + } + else{ + c++; + n++; + } + } + pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(lastname)); + + return l; +} + + +/* get the description symbol. this includes some compat transition stuff with a warning */ +t_pdp_symbol *pdp_packet_get_description(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + + if (!header) return pdp_gensym("invalid"); + else if (!header->desc){ + + post("ERROR: pdp_packet_get_description: packet %d has no description.", packet); + pdp_packet_print_debug(packet); + return pdp_gensym("unknown"); + } + else return header->desc; +} + + + +/* this runs a conversion program */ +int _pdp_type_run_conversion_program(t_pdp_conversion_program *program, + int packet, t_pdp_symbol *dest_template) +{ + /* run a conversion program: + treat the source packet as readonly, and cleanup intermediates, such + that the net result is the production of a new packet, with the + source packet intact. */ + + int p, tmp; + t_pdp_atom *a; + t_pdp_conversion_method m; + + // run the first line of the program + a = program->first; + m = a->w.w_pointer; + D post("DEBUG: _pdp_type_run_conversion_program: method = %x", m); + p = m(packet, dest_template); + D post("DEBUG: _pdp_type_run_conversion_program: packet returned = %d, type = %s", p, pdp_packet_get_description(p)->s_name); + + // run the remaining lines + cleanup intermediates + for (a=a->next; a; a=a->next){ + m = a->w.w_pointer; + D post("DEBUG: _pdp_type_run_conversion_program: next method ptr = %x", m); + tmp = m(p, dest_template); + pdp_packet_mark_unused(p); + p = tmp; + } + return p; +} + + +/* find a conversion program */ +t_pdp_conversion_program * +_pdp_type_find_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern) +{ + t_pdp_conversion *c; + t_pdp_atom *a; + t_pdp_conversion_program *retval = 0; + + /* lock conversion list */ + pthread_mutex_lock(&pdp_conversion_mutex); + + for (a = conversion_list->first; a; a=a->next){ + c = a->w.w_pointer; + /* can be a wildcard match */ + if (pdp_type_description_match(src_pattern, c->src_pattern) && + pdp_type_description_match(dst_pattern, c->dst_pattern)) { + /* found a program */ + D post("DEBUG: _pdp_type_find_conversion_program: found: %s -> %s", c->src_pattern->s_name, c->dst_pattern->s_name); + retval = c->program; + goto gotit; + } + } + + /* no conversion program was found */ + retval = 0; + gotit: + + /* lock conversion list */ + pthread_mutex_unlock(&pdp_conversion_mutex); + return retval; +} + +/* find a cached conversion program + if one is found it will be moved to the back of the queue (MRU) */ +t_pdp_conversion_program * +_pdp_type_find_cached_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern) +{ + t_pdp_conversion *c, *c_tmp; + t_pdp_atom *a; + t_pdp_conversion_program *retval = 0; + + /* lock cached list */ + pthread_mutex_lock(&pdp_cache_mutex); + + for (a = cached_conversion_list->first; a; a=a->next){ + c = a->w.w_pointer; + /* must be exact match */ + if ((src_pattern == c->src_pattern) && + (dst_pattern == c->dst_pattern)) { + + /* found a program */ + D post("DEBUG: _pdp_type_find_cached_conversion_program: found: %s -> %s", c->src_pattern->s_name, c->dst_pattern->s_name); + retval = c->program; + + /* make MRU (move to back) */ + c_tmp = cached_conversion_list->last->w.w_pointer; + cached_conversion_list->last->w.w_pointer = c; + a->w.w_pointer = c_tmp; + goto gotit; + } + } + + retval = 0; + + gotit: + + + /* un lock cached list */ + pthread_mutex_unlock(&pdp_cache_mutex); + + /* no conversion program was found */ + return retval; +} + + +/* conversion program manipulations */ +void pdp_conversion_program_free(t_pdp_conversion_program *program) +{ + pdp_list_free(program); +} + +/* debug print */ +void _pdp_conversion_program_print(t_pdp_conversion_program *program) +{ + post("_pdp_conversion_program_print %x", program); + pdp_list_print(program); +} + +t_pdp_conversion_program *pdp_conversion_program_new(t_pdp_conversion_method method, ...) +{ + t_pdp_conversion_program *p = pdp_list_new(0); + t_pdp_conversion_method m = method; + va_list ap; + + D post("DEBUG: pdp_conversion_program_new:BEGIN"); + + pdp_list_add_back_pointer(p, m); + va_start(ap, method); + while (m = va_arg(ap, t_pdp_conversion_method)) pdp_list_add_back_pointer(p, m); + va_end(ap); + + D post("DEBUG: pdp_conversion_program_new:END"); + + return p; +} + +t_pdp_conversion_program *pdp_conversion_program_copy(t_pdp_conversion_program *program) +{ + if (program) return pdp_list_copy(program); + else return 0; +} + +void pdp_conversion_program_add(t_pdp_conversion_program *program, t_pdp_conversion_program *tail) +{ + return pdp_list_cat(program, tail); +} + +/* conversion registration */ +void pdp_type_register_conversion (t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern, t_pdp_conversion_program *program) +{ + t_pdp_conversion *c = (t_pdp_conversion *)pdp_alloc(sizeof(*c)); + c->src_pattern = src_pattern; + c->dst_pattern = dst_pattern; + c->program = program; + + /* lock conversion list */ + pthread_mutex_lock(&pdp_conversion_mutex); + + pdp_list_add_back_pointer(conversion_list, c); + + /* unlock conversion list */ + pthread_mutex_unlock(&pdp_conversion_mutex); + +} + +/* register a cached conversion */ +void pdp_type_register_cached_conversion (t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern, t_pdp_conversion_program *program) +{ + + /* create the new conversion */ + t_pdp_conversion *c = (t_pdp_conversion *)pdp_alloc(sizeof(*c)); + c->src_pattern = src_pattern; + c->dst_pattern = dst_pattern; + c->program = program; + + /* lock cached conversion list */ + pthread_mutex_lock(&pdp_cache_mutex); + + /* check size, and remove LRU (top) if the cache is full */ + while (cached_conversion_list->elements >= max_cache_size){ + t_pdp_conversion *c_old = pdp_list_pop(cached_conversion_list).w_pointer; + if (c_old->program) pdp_conversion_program_free(c_old->program); + pdp_dealloc(c_old); + } + + /* add and make MRU (back) */ + pdp_list_add_back_pointer(cached_conversion_list, c); + + /* unlock cached conversion list */ + pthread_mutex_unlock(&pdp_cache_mutex); +} + +/* convert a given packet to a certain type (template) */ +int _pdp_packet_convert(int packet, t_pdp_symbol *dest_template) +{ + t_pdp_symbol *type = pdp_packet_get_description(packet); + t_pdp_symbol *tmp_type = 0; + int tmp_packet = -1; + + t_pdp_conversion_program *program = 0; + t_pdp_conversion_program *program_last = 0; + t_pdp_conversion_program *program_tail = 0; + + /* check if there is a program in the cached list, if so run it */ + if (program = _pdp_type_find_cached_conversion_program(type, dest_template)) + return _pdp_type_run_conversion_program(program, packet, dest_template); + + /* if it is not cached, iteratively convert + and save program on top of cache list if a conversion path (program) was found */ + + // run first conversion that matches + program = pdp_conversion_program_copy(_pdp_type_find_conversion_program(type, dest_template)); + program_last = program; + if (!program){ + D post("DEBUG: pdp_type_convert: (1) can't convert %s to %s", type->s_name, dest_template->s_name); + return -1; + } + tmp_packet = _pdp_type_run_conversion_program(program, packet, dest_template); + tmp_type = pdp_packet_get_description(tmp_packet); + + // run more conversions if necessary, deleting intermediate packets + while (!pdp_type_description_match(tmp_type, dest_template)){ + int new_packet; + program_tail = _pdp_type_find_conversion_program(tmp_type, dest_template); + if (!program_tail){ + D post("DEBUG: pdp_type_convert: (2) can't convert %s to %s", tmp_type->s_name, dest_template->s_name); + pdp_packet_mark_unused(tmp_packet); + pdp_conversion_program_free(program); + return -1; + } + if (program_last == program_tail){ + post("ERROR: pdp_packet_convert: conversion loop detected"); + } + program_last = program_tail; + + pdp_conversion_program_add(program, program_tail); + new_packet = _pdp_type_run_conversion_program(program_tail, tmp_packet, dest_template); + pdp_packet_mark_unused(tmp_packet); + tmp_packet = new_packet; + tmp_type = pdp_packet_get_description(tmp_packet); + } + + // save the conversion program + pdp_type_register_cached_conversion(type, dest_template, program); + + // return resulting packet + return tmp_packet; + +} + +/* convert or copy ro */ +int pdp_packet_convert_ro(int packet, t_pdp_symbol *dest_template) +{ + t_pdp_symbol *type = pdp_packet_get_description(packet); + + /* if it is compatible, return a ro copy */ + if (pdp_type_description_match(type, dest_template)) return pdp_packet_copy_ro(packet); + + /* if not, convert to a new type */ + else return _pdp_packet_convert(packet, dest_template); +} + +/* convert or copy rw */ +int pdp_packet_convert_rw(int packet, t_pdp_symbol *dest_template) +{ + t_pdp_symbol *type = pdp_packet_get_description(packet); + + /* if it is compatible, just return a rw copy */ + if (pdp_type_description_match(type, dest_template)) return pdp_packet_copy_rw(packet); + + /* if not, convert to a new type */ + else return _pdp_packet_convert(packet, dest_template); +} + + +static void _setup_type_cache(t_pdp_symbol *s) +{ + t_pdp_list *l = pdp_type_to_list(s); + pthread_mutex_lock(&pdp_hash_mutex); //lock to prevent re-entry + if (!s->s_type){ + s->s_type = l; + l = 0; + } + pthread_mutex_unlock(&pdp_hash_mutex); + if (l) pdp_list_free(l); +} + +/* check if a type description fits a template + this function is symmetric */ +int pdp_type_description_match(t_pdp_symbol *description, t_pdp_symbol *pattern) +{ + int retval = 0; + + t_pdp_atom *ad, *ap; + t_pdp_symbol *wildcard = pdp_gensym("*"); + + + if (!(pattern)) post("PANICA"); + if (!(description)) post("PANICB"); + + + /* same type -> match */ + if (description == pattern) {retval = 1; goto done;} + + /* use the description list stored in the symbol for comparison */ + if (!(description->s_type)) _setup_type_cache(description); + if (!(pattern->s_type)) _setup_type_cache(pattern); + + if (!(pattern->s_type)) post("PANIC1"); + if (!(description->s_type)) post("PANIC2"); + + /* check size */ + if (description->s_type->elements != pattern->s_type->elements){retval = 0; goto done;} + + /* compare symbols of type list */ + for(ad=description->s_type->first, ap=pattern->s_type->first; ad; ad=ad->next, ap=ap->next){ + if (ad->w.w_symbol == wildcard) continue; + if (ap->w.w_symbol == wildcard) continue; + if (ad->w.w_symbol != ap->w.w_symbol) {retval = 0; goto done;} /* difference and not a wildcard */ + } + + /* type templates match */ + retval = 1; + + done: + D post("DEBUG: testing match between %s and %s: %s", + description->s_name, pattern->s_name, retval ? "match" : "no match"); + return retval; + +} + + + + + +/* setup method */ +void pdp_type_setup(void) +{ + int i; + + // create mutexes + pthread_mutex_init(&pdp_hash_mutex, NULL); + pthread_mutex_init(&pdp_conversion_mutex, NULL); + pthread_mutex_init(&pdp_cache_mutex, NULL); + + // init symbol hash + memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *)); + + // create conversion lists + cached_conversion_list = pdp_list_new(0); + conversion_list = pdp_list_new(0); + max_cache_size = INIT_MAX_CACHE_SIZE; + + + + +} diff --git a/system/kernel/pdp_type.c_old b/system/kernel/pdp_type.c_old new file mode 100644 index 0000000..4e42d53 --- /dev/null +++ b/system/kernel/pdp_type.c_old @@ -0,0 +1,542 @@ +/* + * Pure Data Packet system implementation. : code for handling different packet types + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* this file contains type handling routines + note: pd symbols are used for type identification + for porting to other hosts, i suggest reimplementing t_pdp_symbol */ + +// debug + +#include <fnmatch.h> +#include <stdarg.h> +#include <string.h> +#include <pthread.h> +#include "pdp.h" + +/* + + + +*/ + + +#define D if (0) + + +static t_pdp_conversion *conversion_list; + +#define CACHE_SIZE 32 +static t_pdp_conversion *cached_conversion_list_start; +static t_pdp_conversion cached_conversion_array[CACHE_SIZE]; + +/* mutex */ +static pthread_mutex_t pdp_hash_mutex; +static pthread_mutex_t pdp_conversion_mutex; +static pthread_mutex_t pdp_cache_mutex; + +#define HASHSIZE 1024 +static t_pdp_symbol *pdp_symhash[HASHSIZE]; + + +/* shamelessly copied from pd src and made thread safe */ +t_pdp_symbol *_pdp_dogensym(char *s, t_pdp_symbol *oldsym) +{ + t_pdp_symbol **sym1, *sym2; + unsigned int hash1 = 0, hash2 = 0; + int length = 0; + char *s2 = s; + while (*s2) + { + hash1 += *s2; + hash2 += hash1; + length++; + s2++; + } + sym1 = pdp_symhash + (hash2 & (HASHSIZE-1)); + + /* lock hash */ + pthread_mutex_lock(&pdp_hash_mutex); + + while (sym2 = *sym1) + { + if (!strcmp(sym2->s_name, s)) goto gotit; + sym1 = &sym2->s_next; + } + if (oldsym) sym2 = oldsym; + else + { + sym2 = (t_pdp_symbol *)pdp_alloc(sizeof(*sym2)); + sym2->s_name = pdp_alloc(length+1); + _pdp_symbol_clear_namespaces(sym2); + sym2->s_next = 0; + strcpy(sym2->s_name, s); + } + *sym1 = sym2; + + gotit: + + /* unlock hash */ + pthread_mutex_unlock(&pdp_hash_mutex); + return (sym2); +} + +t_pdp_symbol *pdp_gensym(char *s) +{ + return(_pdp_dogensym(s, 0)); +} + +/* convert a type to a list */ +t_pdp_list *pdp_type_to_list(t_pdp_symbol *type) +{ + char *c = type->s_name; + char *lastname = c; + char tmp[100]; + int n = 0; + t_pdp_list *l = pdp_list_new(0); + + while(*c){ + if (*c == '/'){ + strncpy(tmp, lastname, n); + tmp[n] = 0; + pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(tmp)); + c++; + lastname = c; + n = 0; + } + else{ + c++; + n++; + } + } + pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(lastname)); + + return l; +} + + +/* get the description symbol. this includes some compat transition stuff with a warning */ +t_pdp_symbol *pdp_packet_get_description(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + + if (!header) return pdp_gensym("invalid"); + else if (!header->desc){ + /* if description is not defined, try to reconstruct it (for backwards compat) */ + if (header->type == PDP_IMAGE){ + post("FIXME: pdp_type_get_description: packet %d has no description. using workaround.", packet); + return pdp_packet_image_get_description(packet); + } + else + return pdp_gensym("unknown"); + } + else return header->desc; +} + + + +/* this runs a conversion program */ +int _pdp_type_run_conversion_program(t_pdp_conversion_program *program, + int packet, t_pdp_symbol *dest_template) +{ + /* run a conversion program: + treat the source packet as readonly, and cleanup intermediates, such + that the net result is the production of a new packet, with the + source packet intact. */ + + int p, tmp; + + D post("DEBUG: _pdp_type_run_conversion_program: program = %x", program); + + // run the first line of the program + D post("DEBUG: _pdp_type_run_conversion_program: method = %x", *program); + p = (*program->method)(packet, dest_template); + D post("DEBUG: _pdp_type_run_conversion_program: packet returned = %d, type = %s", p, pdp_packet_get_description(p)->s_name); + + // run the remaining lines + cleanup intermediates + for (program = program->next; program ; program = program->next){ + D post("DEBUG: _pdp_type_run_conversion_program: next method ptr = %x", *program->method); + tmp = (*program->method)(p, dest_template); + pdp_packet_mark_unused(p); + p = tmp; + } + return p; +} + + +/* find a conversion program */ +t_pdp_conversion_program * +_pdp_type_find_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern) +{ + t_pdp_conversion *c; + t_pdp_conversion_program *retval = 0; + + /* lock conversion list */ + pthread_mutex_lock(&pdp_conversion_mutex); + + for (c = conversion_list; c; c=c->next){ + /* can be a wildcard match */ + if (pdp_type_description_match(src_pattern, c->src_pattern) && + pdp_type_description_match(dst_pattern, c->dst_pattern)) { + /* found a program */ + D post("DEBUG: _pdp_type_find_conversion_program: found: %s -> %s", c->src_pattern->s_name, c->dst_pattern->s_name); + retval = c->program; + goto gotit; + } + } + + /* no conversion program was found */ + retval = 0; + gotit: + + /* lock conversion list */ + pthread_mutex_unlock(&pdp_conversion_mutex); + return retval; +} + +/* find a cached conversion program */ +t_pdp_conversion_program * +_pdp_type_find_cached_conversion_program(t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern) +{ + t_pdp_conversion *c; + t_pdp_conversion_program *retval = 0; + + /* lock cached list */ + pthread_mutex_lock(&pdp_cache_mutex); + + for (c = cached_conversion_list_start; c; c=c->next){ + /* must be exact match */ + if ((src_pattern == c->src_pattern) && + (dst_pattern == c->dst_pattern)) { + /* found a program */ + D post("DEBUG: _pdp_type_find_cached_conversion_program: found: %s -> %s", c->src_pattern->s_name, c->dst_pattern->s_name); + retval = c->program; + goto gotit; + } + } + + retval = 0; + + gotit: + + /* un lock cached list */ + pthread_mutex_unlock(&pdp_cache_mutex); + + /* no conversion program was found */ + return retval; +} + + +/* conversion program manipulations */ +void pdp_conversion_program_free(t_pdp_conversion_program *program) +{ + t_pdp_conversion_program *p = 0; + while (program) { + p = program; + program = program->next; + pdp_dealloc (p); + } +} + +/* debug print */ +void _pdp_conversion_program_print(t_pdp_conversion_program *program) +{ + D post("DEBUG: conversion program: %x", program); + while(program){ + D post("DEBUG: method: %x", program->method); + program = program->next; + } +} + +t_pdp_conversion_program *pdp_conversion_program_new(t_pdp_conversion_method method, ...) +{ + t_pdp_conversion_program head; + t_pdp_conversion_program *p = &head; + t_pdp_conversion_method m = method; + va_list ap; + + D post("DEBUG: pdp_conversion_program_new:BEGIN"); + + p = p->next = (t_pdp_conversion_program *)pdp_alloc(sizeof(*p)); + p->method = m; + va_start(ap, method); + while (m = va_arg(ap, t_pdp_conversion_method)){ + p = p->next = (t_pdp_conversion_program *)pdp_alloc(sizeof(*p)); + p->method = m; + } + p->next = 0; // terminate list + va_end(ap); + + D post("DEBUG: pdp_conversion_program_new:END"); + D _pdp_conversion_program_print(head.next); + + return head.next; +} + +t_pdp_conversion_program *pdp_conversion_program_copy(t_pdp_conversion_program *program) +{ + t_pdp_conversion_program head; + t_pdp_conversion_program *p = &head; + + for(;program; program=program->next){ + p = p->next = (t_pdp_conversion_program *)pdp_alloc(sizeof(*p)); + p->method = program->method; + } + p->next = 0; + return head.next; +} + +void pdp_conversion_program_add(t_pdp_conversion_program *program, t_pdp_conversion_program *tail) +{ + t_pdp_conversion_program *p = program; + + //D post("DEBUG: pdp_conversion_program_add:BEGIN"); + + + while (p->next) p = p->next; + p->next = pdp_conversion_program_copy(tail); + + //D post("DEBUG: pdp_conversion_program_add:END"); + +} + +/* conversion registration */ +void pdp_type_register_conversion (t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern, t_pdp_conversion_program *program) +{ + t_pdp_conversion head; + t_pdp_conversion *c = &head; + + /* lock conversion list */ + pthread_mutex_lock(&pdp_conversion_mutex); + + head.next = conversion_list; + + while (c->next) c=c->next; // add a new one to the end + c = c->next = (t_pdp_conversion *)pdp_alloc(sizeof(*c)); + c->src_pattern = src_pattern; + c->dst_pattern = dst_pattern; + c->program = program; + c->next = 0; + + conversion_list = head.next; + + /* unlock conversion list */ + pthread_mutex_unlock(&pdp_conversion_mutex); + +} + +/* register a cached conversion */ +void pdp_type_register_cached_conversion (t_pdp_symbol *src_pattern, t_pdp_symbol *dst_pattern, t_pdp_conversion_program *program) +{ + + t_pdp_conversion *save, *c, *c2; + + /* unlock cahed conversion list */ + pthread_mutex_lock(&pdp_cache_mutex); + + c2 = save = cached_conversion_list_start; + while (c2->next->next) c2 = c2->next; + c = c2->next; + c2->next = 0; + if (c->program) pdp_conversion_program_free(c->program); + c->src_pattern = src_pattern; + c->dst_pattern = dst_pattern; + c->program = program; + c->next = save; + cached_conversion_list_start = c; + + /* unlock cached conversion list */ + pthread_mutex_unlock(&pdp_cache_mutex); +} + +/* convert a given packet to a certain type (template) */ +int _pdp_packet_convert(int packet, t_pdp_symbol *dest_template) +{ + t_pdp_symbol *type = pdp_packet_get_description(packet); + t_pdp_symbol *tmp_type = 0; + int tmp_packet = -1; + + t_pdp_conversion_program *program = 0; + t_pdp_conversion_program *program_last = 0; + t_pdp_conversion_program *program_tail = 0; + + /* check if there is a program in the cached list, if so run it */ + if (program = _pdp_type_find_cached_conversion_program(type, dest_template)) + return _pdp_type_run_conversion_program(program, packet, dest_template); + + /* if it is not cached, iteratively convert + and save program on top of cache list if a conversion path (program) was found */ + + // run first conversion that matches + program = pdp_conversion_program_copy(_pdp_type_find_conversion_program(type, dest_template)); + program_last = program; + if (!program){ + D post("DEBUG: pdp_type_convert: (1) can't convert %s to %s", type->s_name, dest_template->s_name); + return -1; + } + tmp_packet = _pdp_type_run_conversion_program(program, packet, dest_template); + tmp_type = pdp_packet_get_description(tmp_packet); + + // run more conversions if necessary, deleting intermediate packets + while (!pdp_type_description_match(tmp_type, dest_template)){ + int new_packet; + program_tail = _pdp_type_find_conversion_program(tmp_type, dest_template); + if (!program_tail){ + D post("DEBUG: pdp_type_convert: (2) can't convert %s to %s", tmp_type->s_name, dest_template->s_name); + pdp_packet_mark_unused(tmp_packet); + pdp_conversion_program_free(program); + return -1; + } + if (program_last == program_tail){ + post("ERROR: pdp_packet_convert: conversion loop detected"); + } + program_last = program_tail; + + pdp_conversion_program_add(program, program_tail); + new_packet = _pdp_type_run_conversion_program(program_tail, tmp_packet, dest_template); + pdp_packet_mark_unused(tmp_packet); + tmp_packet = new_packet; + tmp_type = pdp_packet_get_description(tmp_packet); + } + + // save the conversion program + pdp_type_register_cached_conversion(type, dest_template, program); + + // return resulting packet + return tmp_packet; + +} + +/* convert or copy ro */ +int pdp_packet_convert_ro(int packet, t_pdp_symbol *dest_template) +{ + t_pdp_symbol *type = pdp_packet_get_description(packet); + + /* if it is compatible, return a ro copy */ + if (pdp_type_description_match(type, dest_template)) return pdp_packet_copy_ro(packet); + + /* if not, convert to a new type */ + else return _pdp_packet_convert(packet, dest_template); +} + +/* convert or copy rw */ +int pdp_packet_convert_rw(int packet, t_pdp_symbol *dest_template) +{ + t_pdp_symbol *type = pdp_packet_get_description(packet); + + /* if it is compatible, just return a rw copy */ + if (pdp_type_description_match(type, dest_template)) return pdp_packet_copy_rw(packet); + + /* if not, convert to a new type */ + else return _pdp_packet_convert(packet, dest_template); +} + + +static void _setup_type_cache(t_pdp_symbol *s) +{ + t_pdp_list *l = pdp_type_to_list(s); + pthread_mutex_lock(&pdp_hash_mutex); //lock to prevent re-entry + if (!s->s_type){ + s->s_type = l; + l = 0; + } + pthread_mutex_unlock(&pdp_hash_mutex); + if (l) pdp_list_free(l); +} + +/* check if a type description fits a template + this function is symmetric */ +int pdp_type_description_match(t_pdp_symbol *description, t_pdp_symbol *pattern) +{ + + +#if 1 + t_pdp_atom *ad, *ap; + t_pdp_symbol *wildcard = pdp_gensym("*"); + + /* same type -> match */ + if (description == pattern) return 1; + + /* use the description list stored in the symbol for comparison */ + if (!(description->s_type)) _setup_type_cache(description); + if (!(pattern->s_type)) _setup_type_cache(pattern); + + /* check size */ + if (description->s_type->elements != pattern->s_type->elements) return 0; + + /* compare symbols of type list */ + for(ad=description->s_type->first, ap=pattern->s_type->first; ad; ad=ad->next, ap=ap->next){ + if (ad->w.w_symbol == wildcard) continue; + if (ap->w.w_symbol == wildcard) continue; + if (ad->w.w_symbol != ap->w.w_symbol) return 0; /* difference and not a wildcard */ + } + + /* type templates match */ + return 1; + + +#else + int retval = 0; + if (description == pattern) retval = 1; + //else retval = (!fnmatch(pattern->s_name, description->s_name, FNM_FILE_NAME)); + + //hack: make a symmetric match symmetric by calling fnmatch a second time with reversed attributes + else retval = ((!fnmatch(pattern->s_name, description->s_name, FNM_FILE_NAME)) + ||(!fnmatch(description->s_name, pattern->s_name, FNM_FILE_NAME))); + + + D post("DEBUG: testing match between %s and %s: %s", description->s_name, pattern->s_name, retval ? "match" : "no match"); + + return retval; +#endif +} + + + + + +/* setup method */ +void pdp_type_setup(void) +{ + int i; + + // create mutexes + pthread_mutex_init(&pdp_hash_mutex, NULL); + pthread_mutex_init(&pdp_conversion_mutex, NULL); + pthread_mutex_init(&pdp_cache_mutex, NULL); + + // init hash + memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *)); + + // init conversion lists + conversion_list = 0; + for(i=0; i<CACHE_SIZE; i++){ + t_pdp_conversion *c = cached_conversion_array + i; + c->src_pattern = 0; + c->dst_pattern = 0; + c->program = 0; + c->next = c + 1; + } + cached_conversion_list_start = cached_conversion_array; + cached_conversion_array[CACHE_SIZE-1].next = 0; + + + +} diff --git a/system/kernel/pdp_ut.c b/system/kernel/pdp_ut.c new file mode 100644 index 0000000..dadc493 --- /dev/null +++ b/system/kernel/pdp_ut.c @@ -0,0 +1,262 @@ +/* + * Pure Data Packet - Utility toolkit objects. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* This file contains some small utility pd objects that make working with + pdp objects a lot easier. Mainly as glue to be used in the abstractions + in the distro. */ + + +#include "pdp.h" +#include <math.h> + +/* this object does an add, scale, clip operation */ + +t_class *pdp_ut_addscaleclip_class; + +typedef struct pdp_ut_addscaleclip_struct +{ + t_object x_obj; + t_outlet *x_outlet0; + t_float x_min; + t_float x_max; + t_float x_offset; + t_float x_scale; +} t_pdp_ut_addscaleclip; + + +static void pdp_ut_addscaleclip_float(t_pdp_ut_addscaleclip *x, t_floatarg f) +{ + f += x->x_offset; + f *= x->x_scale; + f = (f < x->x_min) ? x->x_min : f; + f = (f > x->x_max) ? x->x_max : f; + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_addscaleclip_free(t_pdp_ut_addscaleclip *x){} + +void *pdp_ut_addscaleclip_new(t_floatarg offset, t_floatarg scale, t_floatarg min, t_floatarg max) +{ + t_pdp_ut_addscaleclip *x = (t_pdp_ut_addscaleclip *)pd_new(pdp_ut_addscaleclip_class); + x->x_outlet0 = outlet_new(&x->x_obj, &s_float); + x->x_offset = offset; + x->x_scale = scale; + x->x_min = min; + x->x_max = max; + return (void *)x; +} + +void pdp_ut_addscaleclip_setup(void) +{ + pdp_ut_addscaleclip_class = class_new(gensym("pdp_ut_addscaleclip"), (t_newmethod)pdp_ut_addscaleclip_new, + (t_method)pdp_ut_addscaleclip_free, sizeof(t_pdp_ut_addscaleclip), 0, + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_addscaleclip_class, pdp_ut_addscaleclip_float); +} + + +/* pdp_ut_logmap does a logarithmic parameter mapping [0->1] x -> min(max/min)^x max an add, scale, clip operation */ +/* pdp_ut_logmap_comp does x -> min(max/min)^(1-x) */ +/* pdp_ut_linmap dos x -> min + (max - min * x */ + +t_class *pdp_ut_linmap_class; +t_class *pdp_ut_logmap_class; +t_class *pdp_ut_logmap_comp_class; + +typedef struct pdp_ut_map_struct +{ + t_object x_obj; + t_outlet *x_outlet0; + t_float x_min; + t_float x_max; +} t_pdp_ut_map; + + +static void pdp_ut_logmap_float(t_pdp_ut_map *x, t_floatarg f) +{ + f = (f < 0.0f) ? 0.0f : f; + f = (f > 1.0f) ? 1.0f : f; + + f = x->x_min * pow((x->x_max / x->x_min), f); + + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_linmap_float(t_pdp_ut_map *x, t_floatarg f) +{ + f = (f < 0.0f) ? 0.0f : f; + f = (f > 1.0f) ? 1.0f : f; + + f = x->x_min + ((x->x_max - x->x_min) * f); + + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_logmap_comp_float(t_pdp_ut_map *x, t_floatarg f) +{ + f = (f < 0.0f) ? 0.0f : f; + f = (f > 1.0f) ? 1.0f : f; + + f = x->x_min * pow((x->x_max / x->x_min), (1.0f - f)); + + outlet_float(x->x_outlet0, f); +} + +static void pdp_ut_map_free(t_pdp_ut_map *x){} + + +void pdp_ut_map_init(t_pdp_ut_map *x, t_floatarg min, t_floatarg max) +{ + x->x_outlet0 = outlet_new(&x->x_obj, &s_float); + x->x_min = min; + x->x_max = max; +} + +void *pdp_ut_logmap_new(t_floatarg min, t_floatarg max) +{ + t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_class); + pdp_ut_map_init(x, min, max); + return (void *)x; +} + +void *pdp_ut_linmap_new(t_floatarg min, t_floatarg max) +{ + t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_linmap_class); + pdp_ut_map_init(x, min, max); + return (void *)x; +} + +void *pdp_ut_logmap_comp_new(t_floatarg min, t_floatarg max) +{ + t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_comp_class); + pdp_ut_map_init(x, min, max); + return (void *)x; +} + +void pdp_ut_logmap_setup(void) +{ + pdp_ut_logmap_class = class_new(gensym("pdp_ut_logmap"), (t_newmethod)pdp_ut_logmap_new, + (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0, + A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_logmap_class, pdp_ut_logmap_float); +} + +void pdp_ut_logmap_comp_setup(void) +{ + pdp_ut_logmap_comp_class = class_new(gensym("pdp_ut_logmap_comp"), (t_newmethod)pdp_ut_logmap_comp_new, + (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0, + A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_logmap_comp_class, pdp_ut_logmap_comp_float); +} + +void pdp_ut_linmap_setup(void) +{ + pdp_ut_linmap_class = class_new(gensym("pdp_ut_linmap"), (t_newmethod)pdp_ut_linmap_new, + (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0, + A_FLOAT, A_FLOAT, A_NULL); + class_addfloat(pdp_ut_linmap_class, pdp_ut_linmap_float); +} + + + +t_class *pdp_ut_rgb2ycrcb_class; + +typedef struct pdp_ut_rgb2ycrcb +{ + t_object x_obj; + t_outlet *x_outlet_luma; + t_outlet *x_outlet_chroma_red; + t_outlet *x_outlet_chroma_blue; + + t_float x_red, x_green, x_blue; + +} t_pdp_ut_rgb2ycrcb; + + +static void pdp_ut_rgb2ycrcb_bang (t_pdp_ut_rgb2ycrcb* x) +{ + + float luma = 0.299f * x->x_red + 0.587f * x->x_green + 0.114f * x->x_blue; + float chroma_red = (x->x_red - luma) * 0.713f; + float chroma_blue = (x->x_blue - luma) * 0.565f; + + outlet_float(x->x_outlet_chroma_blue, chroma_blue); + outlet_float(x->x_outlet_chroma_red, chroma_red); + outlet_float(x->x_outlet_luma, luma); + +} + + +static void pdp_ut_rgb2ycrcb_red (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_red = f; pdp_ut_rgb2ycrcb_bang(x);} +static void pdp_ut_rgb2ycrcb_green (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_green = f; pdp_ut_rgb2ycrcb_bang(x);} +static void pdp_ut_rgb2ycrcb_blue (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_blue = f; pdp_ut_rgb2ycrcb_bang(x);} + + + +static void pdp_ut_rgb2ycrcb_free (t_pdp_ut_rgb2ycrcb* x) {} +static void* pdp_ut_rgb2ycrcb_new(void) +{ + t_pdp_ut_rgb2ycrcb *x = (t_pdp_ut_rgb2ycrcb *)pd_new(pdp_ut_rgb2ycrcb_class); + + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("green")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("blue")); + + x->x_outlet_luma = outlet_new(&x->x_obj, &s_float); + x->x_outlet_chroma_red = outlet_new(&x->x_obj, &s_float); + x->x_outlet_chroma_blue = outlet_new(&x->x_obj, &s_float); + + x->x_red = 0.0f; + x->x_green = 0.0f; + x->x_blue = 0.0f; + + + return (void *)x; +} + +void pdp_ut_rgb2ycrcb_setup(void) +{ + pdp_ut_rgb2ycrcb_class = class_new(gensym("pdp_ut_rgb2ycrcb"), (t_newmethod)pdp_ut_rgb2ycrcb_new, + (t_method)pdp_ut_rgb2ycrcb_free, sizeof(t_pdp_ut_rgb2ycrcb), 0, A_NULL); + class_addfloat(pdp_ut_rgb2ycrcb_class, pdp_ut_rgb2ycrcb_red); + class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_green, gensym("green"), A_FLOAT, A_NULL); + class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_blue, gensym("blue"), A_FLOAT, A_NULL); +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void pdp_ut_setup(void) +{ + pdp_ut_addscaleclip_setup(); + pdp_ut_logmap_setup(); + pdp_ut_logmap_comp_setup(); + pdp_ut_linmap_setup(); + pdp_ut_rgb2ycrcb_setup(); +} + + +#ifdef __cplusplus +} +#endif diff --git a/system/pdbase/Makefile b/system/pdbase/Makefile new file mode 100644 index 0000000..58efa41 --- /dev/null +++ b/system/pdbase/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = pdp_base.o pdp_imagebase.o pdp_dpd_base.o + + +include ../../Makefile.config + +all: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/pdbase/pdp_base.c b/system/pdbase/pdp_base.c new file mode 100644 index 0000000..4d5593f --- /dev/null +++ b/system/pdbase/pdp_base.c @@ -0,0 +1,415 @@ +/* + * Pure Data Packet base class implementation. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + + This file contains the pdp base class object. + This is really nothing more than an attempt to stay away from c++ + as far as possible, while having some kind of base class functionality + for pdp (tucking away the communication & thread protocol). + +*/ + +#include "pdp_base.h" +#include <stdarg.h> + + +static void pdp_base_debug(t_pdp_base *b, t_floatarg f) +{ + int i; + post("debug"); + post("inlets: %d", b->b_inlets); + post("\tpacket\tnext_packet"); + for (i=0; i<b->b_inlets; i++) + post("\t%d\t%d", b->b_packet[i], b->b_packet_next[i]); + //post("outlets: %d", b->b_inlets); +} + +static void pdp_base_thread(t_pdp_base *b, t_floatarg f) +{ + int i = (int)f; + if ((i == 0) || (i == 1)) b->b_thread_enabled = i; +} + +static void pdp_base_process(t_pdp_base *b) +{ + + if (b->b_process_method) + (*b->b_process_method)(b); +} + +/* this method is called after the thread has finished processing */ +static void pdp_base_postprocess(t_pdp_base *b) +{ + /* call the derived class postproc callback if there is any */ + if (b->b_postproc_method) + (*b->b_postproc_method)(b); + + /* unregister (mark unused) packet and propagate if packet is valid */ + if (b->b_outlet[0]) + pdp_pass_if_valid(b->b_outlet[0], &b->b_packet[0]); +} + + +/* move the passive packets in place */ +void pdp_base_movepassive(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + int i; + + /* if a cold packet was received in the meantime + swap it in, else keep the old one */ + for (i=1; i<b->b_inlets; i++){ + pdp_replace_if_valid(&b->b_packet[i], &b->b_packet_next[i]); + } + + +} + +/* the standard bang method */ +void pdp_base_bang(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + int i; + + /* if pdp thread is still processing, do nothing */ + if (-1 != b->b_queue_id) return; + + /* move packets in place */ + pdp_base_movepassive(x); + + + /* if there is a preproc method defined, call it inside + the pd thread. (mainly for allocations) */ + if (b->b_preproc_method) + (*b->b_preproc_method)(b); + + /* check if we need to use pdp queue */ + if (b->b_thread_enabled){ + + /* add the process method and callback to the process queue */ + pdp_procqueue_add(b->b_q, b, pdp_base_process, pdp_base_postprocess, &b->b_queue_id); + } + else{ + /* call both methods directly */ + pdp_base_process(b); + pdp_base_postprocess(b); + } +} + +/* hot packet input handler */ +void pdp_base_input_hot(t_pdp_base *b, t_symbol *s, t_floatarg f) +{ + + int p = (int)f; + + /* dont register if active inlet is disabled */ + if (!b->b_active_inlet_enabled) return; + + /* register the packet (readonly or read/write) + or drop it if we have an active packet + if type template is not null, packet will be converted */ + + + if (b->b_active_inlet_readonly){ + if (pdp_sym_rro() == s){ + if (b->b_type_template[0]){ + pdp_packet_convert_ro_or_drop(&b->b_packet[0], p, b->b_type_template[0]); + } + else{ + pdp_packet_copy_ro_or_drop(&b->b_packet[0], p); + } + } + } + else{ + if (pdp_sym_rrw() == s) { + if (b->b_type_template[0]){ + pdp_packet_convert_rw_or_drop(&b->b_packet[0], p, b->b_type_template[0]); + } + else{ + pdp_packet_copy_rw_or_drop(&b->b_packet[0], p); + } + } + } + + /* start processing if there is an active packet to process + and the processing method is not active */ + + if ((pdp_sym_prc() == s) && (-1 != b->b_packet[0]) && (-1 == b->b_queue_id)){ + pdp_base_bang(b); + } + //if ((pdp_sym_prc() == s) && (-1 != b->b_packet[0]) && (!b->b_dropped)) pdp_base_bang(b); + +} + +/* cold packet input handlers */ +void pdp_base_input_cold(t_pdp_base *b, t_symbol *s, int ac, t_atom *av) +{ + + int p; + int i; + char msg[] = "pdp1"; + char *c; + + int inlet; + + //post("pdp_base_input_cold: got packet"); + + /* do cheap tests first */ + if (ac != 2) return; + if (av[0].a_type != A_SYMBOL) return; + if (av[0].a_w.w_symbol != pdp_sym_rro()) return; + if (av[1].a_type != A_FLOAT) return; + p = (int)av[1].a_w.w_float; + + + /* check if it's a pdp message + and determine inlet */ + for (i=1; i<MAX_NB_PDP_BASE_INLETS; i++){ + if (s == gensym(msg)){ + inlet = i; + goto found; + } + else{ + msg[3]++; + } + } + return; + + + found: + + /* store the packet and trow away + the old one, if there is any */ + + pdp_packet_copy_ro_or_drop(&b->b_packet_next[inlet], p); +} + + +void pdp_base_set_process_method(void *x, t_pdp_method m) +{ + t_pdp_base *b = (t_pdp_base *)x; + b->b_process_method = m; +} + +void pdp_base_set_preproc_method(void *x, t_pdp_method m) +{ + t_pdp_base *b = (t_pdp_base *)x; + b->b_preproc_method = m; +} + + +void pdp_base_set_postproc_method(void *x, t_pdp_method m) +{ + t_pdp_base *b = (t_pdp_base *)x; + b->b_postproc_method = m; +} + + +void pdp_base_queue_wait(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + pdp_procqueue_wait(b->b_q); +} + +void pdp_base_set_queue(void *x, t_pdp_procqueue *q) +{ + t_pdp_base *b = (t_pdp_base *)x; + pdp_base_queue_wait(x); + b->b_q = q; +} + +t_pdp_procqueue *pdp_base_get_queue(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + return b->b_q; +} + +void pdp_base_setup(t_class *c) +{ + + /* add pdp base class methods */ + class_addmethod(c, (t_method)pdp_base_thread, gensym("thread"), A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_base_debug, gensym("debug"), A_NULL); + + /* hot packet handler */ + class_addmethod(c, (t_method)pdp_base_input_hot, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + /* cold packet handler */ + class_addanything(c, (t_method)pdp_base_input_cold); +} + +/* pdp base instance constructor */ +void pdp_base_init(void *x) +{ + int i; + t_pdp_base *b = (t_pdp_base *)x; + + b->b_channel_mask = -1; + + for(i=0; i<MAX_NB_PDP_BASE_INLETS; i++){ + b->b_packet[i] = -1; + b->b_packet_next[i] = -1; + b->b_type_template[i] = 0; + } + + b->b_queue_id = -1; + //b->b_dropped = 0; + b->b_process_method = 0; + b->b_preproc_method = 0; + b->b_inlets = 1; + b->b_outlets = 0; + b->b_active_inlet_enabled = 1; + b->b_active_inlet_readonly = 0; + b->b_thread_enabled = 1; + + // default queue is pdp queue + b->b_q = pdp_queue_get_queue(); + +} + +/* base instance destructor */ +void pdp_base_free(void *x) +{ + int i; + t_pdp_base *b = (t_pdp_base *)x; + /* remove process method from queue before deleting data */ + pdp_procqueue_finish(b->b_q, b->b_queue_id); + + /* delete stuff */ + for(i=0; i<MAX_NB_PDP_BASE_INLETS; i++){ + pdp_packet_mark_unused(b->b_packet[i]); + pdp_packet_mark_unused(b->b_packet_next[i]); + } + +} + +void pdp_base_readonly_active_inlet(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + b->b_active_inlet_readonly = 1; +} + +void pdp_base_disable_active_inlet(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + b->b_active_inlet_enabled = 0; +} + + +/* add an inlet */ +void pdp_base_add_pdp_inlet(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + char s[] = "pdp0"; + s[3] += b->b_inlets; + + if (b->b_inlets < MAX_NB_PDP_BASE_INLETS){ + inlet_new(&b->x_obj, &b->x_obj.ob_pd, gensym("pdp"), gensym(s)); + b->b_inlets++; + } + else { + post("pdp_base_add_pdp_inlet: only %d pdp inlets allowed. ignoring.", MAX_NB_PDP_BASE_INLETS); + } +} + + +/* add an outlet: only one allowed */ +t_outlet *pdp_base_add_pdp_outlet(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + t_outlet *outlet = outlet_new(&b->x_obj, &s_anything); + + + if (b->b_outlets < MAX_NB_PDP_BASE_OUTLETS){ + b->b_outlet[b->b_outlets] = outlet; + b->b_outlets++; + } + + return outlet; + +} + +void pdp_base_set_packet(void *x, int inlet, int packet) +{ + t_pdp_base *b = (t_pdp_base *)x; + + if (inlet < b->b_inlets){ + //post("%d %d", b->b_packet[inlet], b->b_packet_next[inlet]); + pdp_packet_mark_unused(b->b_packet[inlet]); + b->b_packet[inlet] = packet; + } +} + + +int pdp_base_get_packet(void *x, int inlet) +{ + t_pdp_base *b = (t_pdp_base *)x; + + if (inlet < b->b_inlets){ + //post("%d %d", b->b_packet[inlet], b->b_packet_next[inlet]); + return (b->b_packet[inlet]); + } + + return -1; +} + +int pdp_base_move_packet(void *x, int inlet) +{ + t_pdp_base *b = (t_pdp_base *)x; + int p; + + if (inlet < b->b_inlets){ + p = b->b_packet[inlet]; + b->b_packet[inlet] = -1; + return (p); + } + + return -1; +} + + + +t_object *pdp_base_get_object(void *x) +{ + return (t_object *)x; +} + +void pdp_base_add_gen_inlet(void *x, t_symbol *from, t_symbol *to) +{ + t_object *o = (t_object *)x; + inlet_new(o, &o->ob_pd, from, to); +} + +void pdp_base_disable_thread(void *x) +{ + + t_pdp_base *b = (t_pdp_base *)x; + b->b_thread_enabled = 0; +} + +void pdp_base_set_type_template(void *x, int inlet, t_pdp_symbol *type_template) +{ + t_pdp_base *b = (t_pdp_base *)x; + if (inlet < b->b_inlets){ + b->b_type_template[inlet] = type_template; + } +} diff --git a/system/pdbase/pdp_dpd_base.c b/system/pdbase/pdp_dpd_base.c new file mode 100644 index 0000000..162368d --- /dev/null +++ b/system/pdbase/pdp_dpd_base.c @@ -0,0 +1,270 @@ +/* + * Pure Data Packet module. DPD base class implementation. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include "pdp_dpd_base.h" +#include "pdp_internals.h" + + +#define THIS(b) t_pdp_dpd_base *b = (t_pdp_dpd_base *)x + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +/* PRIVATE METHODS */ + + + + +/* dpd packet context input handler */ +static void _pdp_dpd_base_context_input(t_pdp_dpd_base *b, t_symbol *s, t_floatarg f) +{ + + int p = (int)f; + int i; + + //post ("pdp_dpd_base_context_input: got %s %d", s->s_name, p); + + /* sources/sinks have active inlet disabled */ + if (b->b_dpd_active_inlet_disabled) return; + + /* handle inspect message */ + if (pdp_sym_ins() == s){ + + /* store packet for inspector */ + b->b_context_packet = p; + + /* add inspector to pdp queue + this is special: it doesn't use a command object */ + pdp_dpd_base_queue_command(b, b, b->b_inspector_method, b->b_inspector_callback, 0); + } + + /* handle accumulate message */ + if (pdp_sym_acc() == s){ + + /* store context for accumulator methods */ + b->b_context_packet = p; + + /* call bang */ + pdp_dpd_base_bang(b); + + + } + +} + +/* default command object (returns self) */ +void *_pdp_dpd_base_get_command_object(void *x){return x;} + +/* PUBLIC METHODS */ + + +void pdp_dpd_base_queue_command(void *x, void *c, t_pdp_method process, + t_pdp_method callback, int *id) +{ + THIS(b); + t_pdp_procqueue *q = pdp_base_get_queue(x); + pdp_procqueue_add(q, c, process, callback, id); + +} + +/* bang method (propagate context to outlet) : it is not registered as a pd message by default ! */ +void pdp_dpd_base_bang(void *x) +{ + THIS(b); + int i, id; + void *cobj; + + /* move passive pdp packets in place */ + pdp_base_movepassive(x); + + /* get command object (or use self) */ + cobj = b->b_command_factory_method ? (b->b_command_factory_method)(b) : b; + //post(" command object is %x. object is %x", cobj, b); + + + /* queue acc method & propagate for all outlets */ + for (i=b->b_nb_context_outlets; i--;){ + + + /* propagate the context packet to the outlet */ + if (b->b_outlet_enable[i]){ + pdp_dpd_base_queue_command(x, cobj, b->b_accum_method[i], b->b_accum_callback[i], 0); + outlet_dpd(b->b_context_outlet[i], b->b_context_packet); + } + else{ + //post("outlet %d disabled", i); + } + } + + /* queue cleanup method */ + if (b->b_cleanup_method) + //pdp_procqueue_add(b->b_q, b, b->b_cleanup_method, 0, &b->b_cleanup_queue_id); + pdp_dpd_base_queue_command(x, cobj, b->b_cleanup_method, b->b_cleanup_callback, 0); + + /* send communication complete notify */ + if (b->b_complete_notify) + (b->b_complete_notify)(x); + +} + +/* get/set context packet */ +int pdp_dpd_base_get_context_packet(void *x){ + THIS(b); + return b->b_context_packet; +} +int pdp_dpd_base_move_context_packet(void *x){ + THIS(b); + int p = b->b_context_packet; + b->b_context_packet = -1; + return p; +} + +void pdp_dpd_base_set_context_packet(void *x, int p){ + THIS(b); + pdp_packet_mark_unused(b->b_context_packet); + b->b_context_packet = p; +} + +/* add a cleanup callback (called after all propagation is finished) for sources/sinks */ +void pdp_dpd_base_add_cleanup(void *x, t_pdp_method cleanup_method, t_pdp_method cleanup_callback) +{ + THIS(b); + b->b_cleanup_method = cleanup_method; + b->b_cleanup_callback = cleanup_callback; + //b->b_cleanup_queue_id = -1; +} + +/* add a inspector callback */ +void pdp_dpd_base_add_inspector(void *x, t_pdp_method inspector_method) +{ + THIS(b); + b->b_inspector_method = inspector_method; + //b->b_inspector_queue_id = -1; +} + +/* add a context outlet */ +t_outlet *pdp_dpd_base_add_outlet(void *x, t_pdp_method accum_method, t_pdp_method accum_callback) +{ + THIS(b); + int i = b->b_nb_context_outlets; + if (i < PDP_DPD_MAX_CONTEXT_OUTLETS){ + b->b_context_outlet[i] = outlet_new((t_object *)b, &s_anything); + b->b_outlet_enable[i] = 1; + b->b_accum_method[i] = accum_method; + b->b_accum_callback[i] = accum_callback; + //b->b_accum_queue_id[i] = -1; + b->b_nb_context_outlets++; + return b->b_context_outlet[i]; + } + else{ + post("pdp_dpd_base_add_outlet: no more free outlet slots"); + return 0; + } + +} + + +/* destructor */ +void pdp_dpd_base_free(void *x) +{ + THIS(b); + + /* free base */ + pdp_base_free(b); +} + + +void pdp_dpd_base_disable_active_inlet(void *x) +{ + THIS(b); + b->b_dpd_active_inlet_disabled = 1; +} + + + +void pdp_dpd_base_enable_outlet(void *x, int outlet, int toggle) +{ + THIS(b); + if (outlet >=0 && outlet < PDP_DPD_MAX_CONTEXT_OUTLETS){ + b->b_outlet_enable[outlet] = toggle; + } + +} + + +void pdp_dpd_base_register_complete_notify(void *x, t_pdp_method method) +{ + THIS(b); + b->b_complete_notify = method; +} + +void pdp_dpd_base_register_command_factory_method(void *x, t_pdp_newmethod command_factory_method) +{ + THIS(b); + b->b_command_factory_method = command_factory_method; +} + + +/* init method */ +void pdp_dpd_base_init(void *x) +{ + THIS(b); + + /* super init */ + pdp_base_init(b); + + /* disable pdp messages on active inlet (dpd messages are used as sync) */ + pdp_base_disable_active_inlet(b); + + /* init data */ + b->b_nb_context_outlets = 0; + b->b_context_packet = -1; + b->b_cleanup_method = 0; + //b->b_cleanup_queue_id = -1; + b->b_inspector_method = 0; + //b->b_inspector_queue_id = -1; + b->b_dpd_active_inlet_disabled = 0; + + // default notify == none + b->b_complete_notify = 0; + + // default command object getter + b->b_command_factory_method = 0; + +} + + +void pdp_dpd_base_setup(t_class *class) +{ + + pdp_base_setup(class); + class_addmethod(class, (t_method)_pdp_dpd_base_context_input, gensym("dpd"), A_SYMBOL, A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/system/pdbase/pdp_imagebase.c b/system/pdbase/pdp_imagebase.c new file mode 100644 index 0000000..f9634e1 --- /dev/null +++ b/system/pdbase/pdp_imagebase.c @@ -0,0 +1,79 @@ +/* + * Pure Data Packet image processor base class implementation. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + + This file contains the pdp image base class object. +*/ + +#include "pdp_imagebase.h" +#include <stdarg.h> + + +static void pdp_imagebase_chanmask(t_pdp_base *b, t_floatarg f) +{ + int i = (int)f; + if (i < 0) i = -1; + b->b_channel_mask = i; +} + +void pdp_imagebase_setup(t_class *c) +{ + /* parent class setup */ + pdp_base_setup(c); + + /* add pdp base class methods */ + class_addmethod(c, (t_method)pdp_imagebase_chanmask, gensym("chanmask"), A_FLOAT, A_NULL); + +} + +/* pdp base instance constructor */ +void pdp_imagebase_init(void *x) +{ + int i; + t_pdp_imagebase *b = (t_pdp_imagebase *)x; + + /* init super */ + pdp_base_init(x); + + /* convert all active incoming packet types to image */ + pdp_base_set_type_template(x, 0, pdp_gensym("image/*/*")); + + /* default chanmask == all */ + b->b_channel_mask = -1; + +} + +/* base instance destructor */ +void pdp_imagebase_free(void *x) +{ + /* free super */ + pdp_base_free(x); + +} + +/* chanmask getter */ +u32 pdp_imagebase_get_chanmask(void *x) +{ + t_pdp_base *b = (t_pdp_base *)x; + return b->b_channel_mask; +} + diff --git a/system/png/Makefile b/system/png/Makefile new file mode 100644 index 0000000..9122da6 --- /dev/null +++ b/system/png/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = pdp_png.o + + +include ../../Makefile.config + +all: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/png/pdp_png.c b/system/png/pdp_png.c new file mode 100644 index 0000000..573d42c --- /dev/null +++ b/system/png/pdp_png.c @@ -0,0 +1,405 @@ + +/* + * Pure Data Packet system module. - png glue code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "pdp.h" + +// this is an optional module +#include "pdp_config.h" + + +#ifdef HAVE_PDP_PNG +//if 0 + +#include <png.h> + +#define READING 1 +#define WRITING 2 +#define SIG_BYTES 8 + +#define D if(0) + +typedef struct +{ + FILE *x_fp; + + int x_kindof; //READING or WRITING + png_structp x_png; + png_infop x_info; + png_infop x_end_info; + + png_uint_32 x_width; + png_uint_32 x_height; + int x_bit_depth; + int x_color_type; + int x_interlace_type; + int x_compression_type; + int x_filter_type; + + int x_pdp_bitmap_type; + int x_packet; + +} t_png_image; + +static t_png_image *_init(t_png_image *x) +{ + x->x_png = 0; + x->x_info = 0; + x->x_end_info = 0; + x->x_fp = 0; + x->x_packet = -1; + return x; +} + +static int _cleanup(t_png_image *x) +{ + if (!x) return 1; + pdp_packet_mark_unused(x->x_packet); + if (x->x_png) + switch(x->x_kindof){ + case READING: png_destroy_read_struct(&x->x_png, &x->x_info, &x->x_end_info); break; + case WRITING: png_destroy_write_struct(&x->x_png, &x->x_info); break; + default: exit(1); + } + if (x->x_fp) fclose(x->x_fp); + return 1; +} + +static int _open_read(t_png_image* x, char *file) +{ + char header[SIG_BYTES]; + int is_png; + + x->x_fp = fopen(file, "r"); + if (!x->x_fp) { + D post("can't open %s for reading", file); + return 0; + } + fread(header, 1, SIG_BYTES, x->x_fp); + is_png = !png_sig_cmp(header, 0, SIG_BYTES); + + D post("%s is %s png file", file, is_png ? "a" : "not a"); + + return is_png; +} + +static int _open_write(t_png_image* x, char *file) +{ + char header[SIG_BYTES]; + int is_png; + + x->x_fp = fopen(file, "w"); + if (!x->x_fp) { + D post("can't open %s for writing", file); + return 0; + } + + return 1; +} + +/* progress callback */ + +static void _row_callback(png_structp p, png_uint_32 row, int pass) +{ + fprintf(stderr, "."); +} + +static int _initio_read(t_png_image *x) +{ + png_init_io(x->x_png, x->x_fp); + png_set_sig_bytes(x->x_png, SIG_BYTES); + D png_set_read_status_fn(x->x_png, _row_callback); + return 1; + +} + +static int _initio_write(t_png_image *x) +{ + png_init_io(x->x_png, x->x_fp); + D png_set_write_status_fn(x->x_png, _row_callback); + + return 1; + +} + +static int _checkimagetype(t_png_image *x) +{ + png_read_info(x->x_png, x->x_info); + png_get_IHDR(x->x_png, x->x_info, &x->x_width, &x->x_height, + &x->x_bit_depth, &x->x_color_type, &x->x_interlace_type, + &x->x_compression_type, &x->x_filter_type); + + D post("image info: w=%d, h=%d, depth=%d, type=%d", + (int)x->x_width, (int)x->x_height, (int)x->x_bit_depth, + (int)x->x_color_type); + + + /* handle paletted images: convert to 8 bit RGB(A) */ + if (x->x_color_type == PNG_COLOR_TYPE_PALETTE && + x->x_bit_depth <= 8) { + png_set_expand(x->x_png); + D post("convert palette"); + + /* check if there's an alpha channel and set PDP_BITMAP type */ + x->x_pdp_bitmap_type = + (png_get_valid(x->x_png, x->x_info, PNG_INFO_tRNS)) ? + PDP_BITMAP_RGBA : PDP_BITMAP_RGB; + + return 1; + } + + /* handle bitdepth */ + if (x->x_bit_depth < 8) { + png_set_expand(x->x_png); + D post("convert greyscale to 8 bit"); + } + if (x->x_bit_depth == 16){ + D post("stripping 16 bit to 8 bit"); + png_set_strip_16(x->x_png); + } + + + /* handle greyscale images */ + if (x->x_color_type == PNG_COLOR_TYPE_GRAY){ + x->x_pdp_bitmap_type = PDP_BITMAP_GREY; + if (png_get_valid(x->x_png, x->x_info, PNG_INFO_tRNS)){ + D post("no support for greyscale images with alpha info"); + return 0; + } + return 1; + } + + /* handle RGB imges */ + if (x->x_color_type = PNG_COLOR_TYPE_RGB){ + x->x_pdp_bitmap_type = PDP_BITMAP_RGB; + return 1; + } + + /* handle RGBA imges */ + if (x->x_color_type = PNG_COLOR_TYPE_RGBA){ + x->x_pdp_bitmap_type = PDP_BITMAP_RGBA; + return 1; + } + + + /* can't handle image type */ + D post("image type not supported"); + return 0; + +} + +#define user_error_ptr NULL +#define user_error_fn NULL +#define user_warning_fn NULL + +static int _buildstruct_read(t_png_image *x) +{ + x->x_kindof = READING; + + if (!(x->x_png = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn))) return 0; + + if (!(x->x_info = png_create_info_struct(x->x_png))) return 0; + if (!(x->x_end_info = png_create_info_struct(x->x_png))) return 0; + + return 1; +} + +static int _buildstruct_write(t_png_image *x) +{ + x->x_kindof = WRITING; + + if (!(x->x_png = png_create_write_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn))) return 0; + + if (!(x->x_info = png_create_info_struct(x->x_png))) return 0; + + return 1; +} + +static int _getimagedata(t_png_image *x) +{ + int nbchans = 0; + char *image_data; + png_bytep row_pointers[x->x_height]; + png_uint_32 i; + + D post("reading %d rows ", (int)x->x_height); + + switch (x->x_pdp_bitmap_type){ + case PDP_BITMAP_GREY: nbchans = 1; break; + case PDP_BITMAP_RGB: nbchans = 3; break; + case PDP_BITMAP_RGBA: nbchans = 4; break; + default: + return 0; + } + + x->x_packet = + pdp_packet_new_bitmap(x->x_pdp_bitmap_type, x->x_width, x->x_height); + if (!(image_data = pdp_packet_data(x->x_packet))) return 0; + + for(i=0; i<x->x_height; i++) + row_pointers[i] = image_data + nbchans * i * x->x_width; + + png_read_image(x->x_png, row_pointers); + + D post("DONE"); + + return 1; +} + +static int _saveimagedata(t_png_image *x, int packet) +{ + png_bytep *row_pointers; + png_uint_32 i; + int nbchans; + t_pdp *h = pdp_packet_header(packet); + char *image_data = (char *)pdp_packet_data(packet); + + if (!h) return 0; + if (PDP_BITMAP != h->type) return 0; + if (!image_data) return 0; + + x->x_width = h->info.bitmap.width; + x->x_height = h->info.bitmap.height; + + switch(h->info.image.encoding){ + case PDP_BITMAP_GREY: x->x_color_type = PNG_COLOR_TYPE_GRAY; nbchans = 1; break; + case PDP_BITMAP_RGB: x->x_color_type = PNG_COLOR_TYPE_RGB; nbchans = 3; break; + case PDP_BITMAP_RGBA: x->x_color_type = PNG_COLOR_TYPE_RGBA; nbchans = 4; break; + default: return 0; + } + + png_set_IHDR(x->x_png, x->x_info, x->x_width, x->x_height, 8, + x->x_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(x->x_png, x->x_info); + + D post("writing %d rows ", (int)x->x_height); + + row_pointers = (png_bytep *)pdp_alloc(sizeof(png_bytep) * x->x_height); + for(i=0; i<x->x_height; i++) + row_pointers[i] = image_data + nbchans * i * x->x_width; + + png_write_image(x->x_png, row_pointers); + png_write_end(x->x_png, x->x_info); + pdp_dealloc(row_pointers); + + D post("DONE"); + + return 1; +} + + + +/* simple functions to load and save png images */ + +int pdp_packet_bitmap_from_png_file(char *filename) +{ + int packet = -1; + t_png_image _x; + t_png_image *x = &_x; + + if (!_init(x)) goto exit; + if (!_open_read(x, filename)) goto exit; + if (!_buildstruct_read(x)) goto exit; + if (!_initio_read(x)) goto exit; + if (!_checkimagetype(x)) goto exit; + if (!_getimagedata(x)) goto exit; + + packet = x->x_packet; + x->x_packet = -1; + _cleanup(x); + return packet; + exit: + _cleanup(x); + return -1; + +} + + + +int pdp_packet_bitmap_save_png_file(int packet, char *filename) +{ + + t_png_image _x; + t_png_image *x = &_x; + + if (!_init(x)) goto exit; + if (!_open_write(x, filename)) goto exit; + if (!_buildstruct_write(x)) goto exit; + if (!_initio_write(x)) goto exit; + if (!_saveimagedata(x, packet)) goto exit; + + _cleanup(x); + return 1; + exit: + _cleanup(x); + return 0; + +} + + + +#else //PDP_HAVE_PNG +int pdp_packet_bitmap_save_png_file(int packet, char *filename) +{ + post("WARNING: no png support, can't save png file %s", filename); + return 0; +} + +int pdp_packet_bitmap_from_png_file(char *filename) +{ + post("WARNING: no png support, can't load png file %s", filename); + return -1; +} + + +#endif //PDP_HAVE_PNG + + + + + + + + + + + + + +#if 0 +int main(int argc, char **argv) +{ + char *image = 0; + + if (argc != 2) + post("usage: %s <png file>", argv[0]); + else + image = load_png(argv[1]); + + post ("%s", image ? "OK" : "ERROR"); + +} +#endif diff --git a/system/type/Makefile b/system/type/Makefile new file mode 100644 index 0000000..04f37a8 --- /dev/null +++ b/system/type/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = pdp_bitmap.o pdp_image.o pdp_image_words.o pdp_matrix.o + + +include ../../Makefile.config + +all: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/type/pdp_bitmap.c b/system/type/pdp_bitmap.c new file mode 100644 index 0000000..7b093aa --- /dev/null +++ b/system/type/pdp_bitmap.c @@ -0,0 +1,583 @@ +/* + * Pure Data Packet system implementation. : 8 bit image packet implementation + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "pdp.h" +#include "pdp_internals.h" +#include <stdio.h> + + +/* the class object */ +static t_pdp_class* bitmap_class; + + +t_bitmap *pdp_packet_bitmap_info(int packet) +{ + return (t_bitmap *)pdp_packet_subheader(packet); +} + +/* check if packet is a valid image packet */ +int pdp_packet_bitmap_isvalid(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *image = pdp_packet_bitmap_info(packet); + if (!header) return 0; + if (PDP_BITMAP != header->type) return 0; + + return 1; + +} + + + +/* bitmap constructors */ +t_pdp_symbol *pdp_packet_bitmap_get_description(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + char description[1024]; + char *c = description; + int encoding; + + if (!header) return pdp_gensym("invalid"); + else if (!header->desc){ + /* if description is not defined, try to reconstruct it (for backwards compat) */ + if (header->type == PDP_BITMAP){ + c += sprintf(c, "bitmap"); + encoding = bitmap->encoding; + switch(encoding){ + case PDP_BITMAP_RGB: c += sprintf(c, "/rgb"); break; + case PDP_BITMAP_RGBA: c += sprintf(c, "/rgba"); break; + case PDP_BITMAP_GREY: c += sprintf(c, "/grey"); break; + case PDP_BITMAP_YV12: c += sprintf(c, "/yv12"); break; + default: + c += sprintf(c, "/unknown"); goto exit; + } + c += sprintf(c, "/%dx%d", + bitmap->width, + bitmap->height); + exit: + return pdp_gensym(description); + } + else return pdp_gensym("unknown"); + } + else return header->desc; +} + +int pdp_packet_new_bitmap_yv12(u32 w, u32 h) +{ + t_pdp *header; + t_bitmap *bitmap; + int packet; + + + u32 size = w*h; + u32 totalnbpixels = size; + u32 packet_size = size + (size >> 1); + + packet = pdp_packet_new(PDP_BITMAP, packet_size); + header = pdp_packet_header(packet); + bitmap = pdp_packet_bitmap_info(packet); + if (!header) return -1; + + bitmap->encoding = PDP_BITMAP_YV12; + bitmap->width = w; + bitmap->height = h; + header->desc = pdp_packet_bitmap_get_description(packet); + header->theclass = bitmap_class; + + return packet; +} + +int pdp_packet_new_bitmap_grey(u32 w, u32 h) +{ + t_pdp *header; + t_bitmap *bitmap; + int packet; + + u32 size = w*h; + u32 totalnbpixels = size; + u32 packet_size = totalnbpixels; + + packet = pdp_packet_new(PDP_BITMAP, packet_size); + header = pdp_packet_header(packet); + bitmap = pdp_packet_bitmap_info(packet); + if (!header) return -1; + + bitmap->encoding = PDP_BITMAP_GREY; + bitmap->width = w; + bitmap->height = h; + header->desc = pdp_packet_bitmap_get_description(packet); + header->theclass = bitmap_class; + + return packet; +} + +int pdp_packet_new_bitmap_rgb(u32 w, u32 h) +{ + t_pdp *header; + t_bitmap *bitmap; + int packet; + + + u32 size = w*h; + u32 totalnbpixels = size; + u32 packet_size = totalnbpixels * 3; + + packet = pdp_packet_new(PDP_BITMAP, packet_size); + header = pdp_packet_header(packet); + bitmap = pdp_packet_bitmap_info(packet); + if (!header) return -1; + + bitmap->encoding = PDP_BITMAP_RGB; + bitmap->width = w; + bitmap->height = h; + header->desc = pdp_packet_bitmap_get_description(packet); + header->theclass = bitmap_class; + + return packet; +} + +int pdp_packet_new_bitmap_rgba(u32 w, u32 h) +{ + t_pdp *header; + t_bitmap *bitmap; + int packet; + + + u32 size = w*h; + u32 totalnbpixels = size; + u32 packet_size = totalnbpixels * 4; + + packet = pdp_packet_new(PDP_BITMAP, packet_size); + header = pdp_packet_header(packet); + bitmap = pdp_packet_bitmap_info(packet); + if (!header) return -1; + + bitmap->encoding = PDP_BITMAP_RGBA; + bitmap->width = w; + bitmap->height = h; + header->desc = pdp_packet_bitmap_get_description(packet); + header->theclass = bitmap_class; + + return packet; +} + +int pdp_packet_new_bitmap(int type, u32 w, u32 h) +{ + switch(type){ + case PDP_BITMAP_GREY: return pdp_packet_new_bitmap_grey(w,h); + case PDP_BITMAP_RGB: return pdp_packet_new_bitmap_rgb(w,h); + case PDP_BITMAP_RGBA: return pdp_packet_new_bitmap_rgba(w,h); + default: return -1; + } +} + + +/* some utility methods */ +void pdp_llconv_flip_top_bottom(char *data, int width, int height, int pixelsize); + +/* flip top & bottom */ +void pdp_packet_bitmap_flip_top_bottom(int packet) +{ + t_bitmap *b = (t_bitmap *)pdp_packet_subheader(packet); + char *d = (char *)pdp_packet_data(packet); + int w,h; + if (!pdp_packet_bitmap_isvalid(packet)) return; + if (!b) return; + w = b->width; + h = b->height; + + switch(b->encoding){ + case PDP_BITMAP_GREY: pdp_llconv_flip_top_bottom(d,w,h,1); break; + case PDP_BITMAP_RGB: pdp_llconv_flip_top_bottom(d,w,h,3); break; + case PDP_BITMAP_RGBA: pdp_llconv_flip_top_bottom(d,w,h,4); break; + default: break; + } + +} + + + +/* conversion methods */ +// image/grey/* -> bitmap/grey/* +static int _pdp_packet_bitmap_convert_grey_to_grey8(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + s16 *data = (s16 *)pdp_packet_data(packet); + u8 *new_data; + u32 w,h; + int new_p; + + if (!pdp_packet_image_isvalid(packet)) return -1; + w = image->width; + h = image->height; + + if (!((image->encoding == PDP_IMAGE_GREY) || + (image->encoding == PDP_IMAGE_YV12))) return -1; + + new_p = pdp_packet_new_bitmap_grey(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + /* convert image to greyscale 8 bit */ + pdp_llconv(data,RIF_GREY______S16, new_data, RIF_GREY______U8, w, h); + + return new_p; +} + +static int _pdp_packet_bitmap_convert_YCrCb_to_rgb8(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + s16 *data = (s16 *)pdp_packet_data(packet); + u8 *new_data; + u32 w,h; + int new_p; + + if (!pdp_packet_image_isvalid(packet)) return -1; + w = image->width; + h = image->height; + + if (!((image->encoding == PDP_IMAGE_YV12))) return -1; + + new_p = pdp_packet_new_bitmap_rgb(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + /* convert image to greyscale 8 bit */ + pdp_llconv(data,RIF_YVU__P411_S16, new_data, RIF_RGB__P____U8, w, h); + + return new_p; +} + +static int _pdp_packet_bitmap_convert_image_to_yv12(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + s16 *data = (s16 *)pdp_packet_data(packet); + u8 *new_data; + u32 w,h, nbpixels; + int new_p; + int encoding = image->encoding; + + if (!pdp_packet_image_isvalid(packet)) return -1; + w = image->width; + h = image->height; + nbpixels = w*h; + + new_p = pdp_packet_new_bitmap_yv12(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + switch (encoding){ + case PDP_IMAGE_YV12: + pdp_llconv(data, RIF_YVU__P411_S16, new_data, RIF_YVU__P411_U8, w, h); + break; + case PDP_IMAGE_GREY: + pdp_llconv(data, RIF_GREY______S16, new_data, RIF_GREY______U8, w, h); + memset(new_data + nbpixels, 0x80, nbpixels>>1); + break; + default: + /* not supported, $$$TODO add more */ + pdp_packet_mark_unused(new_p); + new_p = -1; + break; + } + + return new_p; + +} + +static int _pdp_packet_bitmap_convert_rgb8_to_YCrCb(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + u8 *data = (u8 *)pdp_packet_data(packet); + s16 *new_data; + u32 w,h; + int new_p; + + w = bitmap->width; + h = bitmap->height; + new_p = pdp_packet_new_image_YCrCb(w,h); + if (-1 == new_p) return -1; + new_data = (s16 *)pdp_packet_data(new_p); + + /* convert image to greyscale 8 bit */ + pdp_llconv(data, RIF_RGB__P____U8, new_data, RIF_YVU__P411_S16, w, h); + + return new_p; +} + +static int _pdp_packet_bitmap_convert_rgb8_to_bitmap_YCrCb(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + u8 *data = (u8 *)pdp_packet_data(packet); + u8 *new_data; + u32 w,h; + int new_p; + + w = bitmap->width; + h = bitmap->height; + new_p = pdp_packet_new_bitmap_yv12(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + /* convert image to greyscale 8 bit */ + pdp_llconv(data, RIF_RGB__P____U8, new_data, RIF_YVU__P411_U8, w, h); + + return new_p; +} + +static int _pdp_packet_bitmap_convert_grey8_to_grey(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + s16 *data = (s16 *)pdp_packet_data(packet); + u8 *new_data; + u32 w,h; + int new_p; + + w = bitmap->width; + h = bitmap->height; + new_p = pdp_packet_new_image_grey(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + /* convert image to greyscale 8 bit */ + pdp_llconv(data, RIF_GREY______U8, new_data, RIF_GREY______S16, w, h); + + return new_p; +} +static int _pdp_packet_bitmap_convert_rgb8_to_rgba8(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + u8 *data = (u8 *)pdp_packet_data(packet); + u8 *new_data; + int w,h, new_p, i; + + w = bitmap->width; + h = bitmap->height; + new_p = pdp_packet_new_bitmap_rgba(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + for(i=0; i<w*h; i++){ + new_data[4*i+0] = data[3*i + 0]; + new_data[4*i+1] = data[3*i + 1]; + new_data[4*i+2] = data[3*i + 2]; + new_data[4*i+3] = 0; + } + + return new_p; +} +static int _pdp_packet_bitmap_convert_rgba8_to_rgb8(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + u8 *data = (u8 *)pdp_packet_data(packet); + u8 *new_data; + int w,h, new_p, i; + + w = bitmap->width; + h = bitmap->height; + new_p = pdp_packet_new_bitmap_rgb(w,h); + if (-1 == new_p) return -1; + new_data = (u8 *)pdp_packet_data(new_p); + + for(i=0; i<w*h; i++){ + new_data[3*i+0] = data[4*i + 0]; + new_data[3*i+1] = data[4*i + 1]; + new_data[3*i+2] = data[4*i + 2]; + } + + return new_p; +} +static int _pdp_packet_bitmap_convert_rgb8_to_mchp(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + u8 *data = (u8 *)pdp_packet_data(packet); + s16 *new_data; + int w,h; + int new_p, i, plane; + + w = bitmap->width; + h = bitmap->height; + plane = w*h; + new_p = pdp_packet_new_image_multi(w,h,3); + if (-1 == new_p) return -1; + new_data = (s16 *)pdp_packet_data(new_p); + + for(i=0; i<w*h; i++){ + new_data[i] = ((u32)data[3*i + 0]) << 7; + new_data[i+plane] = ((u32)data[3*i + 1]) << 7; + new_data[i+plane*2] = ((u32)data[3*i + 2]) << 7; + } + + return new_p; +} + +static int _pdp_packet_bitmap_convert_yv12_to_image(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_bitmap *bitmap = pdp_packet_bitmap_info(packet); + u8 *data = (u8 *)pdp_packet_data(packet); + s16 *new_data; + int w,h; + int new_p; + + w = bitmap->width; + h = bitmap->height; + new_p = pdp_packet_new_image_YCrCb(w,h); + new_data = pdp_packet_data(new_p); + if (-1 == new_p || !new_data) return -1; + pdp_llconv(data, RIF_YVU__P411_U8, new_data, RIF_YVU__P411_S16, w, h); + + return new_p; +} + +static int _pdp_packet_bitmap_convert_mchp_to_rgb8(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = (t_image *)pdp_packet_subheader(packet); + s16 *data = (s16 *)pdp_packet_data(packet); + u8 *new_data; + int w = image->width; + int h = image->height; + int plane = w*h; + int nb_channels = image->depth; + int new_p, i; + + static inline u8 _map(s32 pixel){ + s32 mask = ~(pixel>>16); + return ((pixel >> 7) & mask); + } + + switch(nb_channels){ + default: return -1; + case 1: + if (-1 == (new_p = pdp_packet_new_bitmap_grey(w,h))) return -1; + new_data = (u8*)pdp_packet_data(new_p); + for(i=0; i<plane; i++) new_data[i] = _map(data[i]); + break; + case 3: + if (-1 == (new_p = pdp_packet_new_bitmap_rgb(w,h))) return -1; + new_data = (u8*)pdp_packet_data(new_p); + for(i=0; i<plane; i++){ + new_data[3*i+0] = _map(data[i]); + new_data[3*i+1] = _map(data[i+plane]); + new_data[3*i+2] = _map(data[i+plane*2]); + } + break; + case 4: + if (-1 == (new_p = pdp_packet_new_bitmap_rgba(w,h))) return -1; + new_data = (u8*)pdp_packet_data(new_p); + for(i=0; i<plane; i++){ + new_data[4*i+0] = _map(data[i]); + new_data[4*i+1] = _map(data[i+plane]); + new_data[4*i+2] = _map(data[i+plane*2]); + new_data[4*i+3] = _map(data[i+plane*3]); + } + break; + + + } + + return new_p; + +} + +static int _pdp_packet_bitmap_convert_fallback(int packet, t_pdp_symbol *dest_template) +{ + post("can't convert image type %s to %s", + pdp_packet_get_description(packet)->s_name, dest_template->s_name); + + return -1; +} + +static int pdp_bitmap_factory(t_pdp_symbol *type) +{ + return -1; +} + +void pdp_bitmap_setup(void) +{ + t_pdp_conversion_program *program; + + /* setup class object */ + bitmap_class = pdp_class_new(pdp_gensym("bitmap/*/*"), pdp_bitmap_factory); + + + /* setup conversion programs */ + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_grey_to_grey8, 0); + pdp_type_register_conversion(pdp_gensym("image/grey/*"), pdp_gensym("bitmap/grey/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_grey8_to_grey, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/grey/*"), pdp_gensym("image/grey/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_YCrCb_to_rgb8, 0); + pdp_type_register_conversion(pdp_gensym("image/YCrCb/*"), pdp_gensym("bitmap/rgb/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_image_to_yv12, 0); + pdp_type_register_conversion(pdp_gensym("image/YCrCb/*"), pdp_gensym("bitmap/yv12/*"), program); + pdp_type_register_conversion(pdp_gensym("image/grey/*"), pdp_gensym("bitmap/yv12/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_yv12_to_image, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/yv12/*"), pdp_gensym("image/YCrCb/*"), program); + + /* rgb->YCrCb converts the colour space */ + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_rgb8_to_YCrCb, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/rgb/*"), pdp_gensym("image/YCrCb/*"), program); + + + /* rgb <-> multi does not convert the colour space */ + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_rgb8_to_mchp, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/rgb/*"), pdp_gensym("image/multi/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_mchp_to_rgb8, 0); + pdp_type_register_conversion(pdp_gensym("image/multi/*"), pdp_gensym("bitmap/*/*"), program); + + + /* rgb <-> rgba */ + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_rgb8_to_rgba8, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/rgb/*"), pdp_gensym("bitmap/rgba/*"), program); + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_rgba8_to_rgb8, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/rgba/*"), pdp_gensym("bitmap/rgb/*"), program); + + + /* fallback rgb convertor */ + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_rgb8_to_YCrCb, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/rgb/*"), pdp_gensym("image/*/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_rgb8_to_bitmap_YCrCb, 0); + pdp_type_register_conversion(pdp_gensym("bitmap/rgb/*"), pdp_gensym("bitmap/yv12/*"), program); + + + /* fallbacks */ + program = pdp_conversion_program_new(_pdp_packet_bitmap_convert_fallback, 0); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("bitmap/*/*"), program); + pdp_type_register_conversion(pdp_gensym("bitmap/*/*"), pdp_gensym("image/*/*"), program); + pdp_type_register_conversion(pdp_gensym("bitmap/*/*"), pdp_gensym("bitmap/*/*"), program); + +} diff --git a/system/type/pdp_image.c b/system/type/pdp_image.c new file mode 100644 index 0000000..1aa9763 --- /dev/null +++ b/system/type/pdp_image.c @@ -0,0 +1,580 @@ +/* + * Pure Data Packet system implementation. : 16 bit image packet implementation + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + This file contains methods for the image packets + pdp_packet_new_* methods are several image packet constructors + pdp_type_* are image type checkers & converters + +*/ + +#include "pdp.h" +#include "pdp_internals.h" +#include <stdio.h> + + +/* the class object */ +static t_pdp_class* image_class; + + + +/* check dimensions */ +static void _checkdim(u32 width, u32 height){ + if ((pdp_imageproc_legalwidth(width) != width) || + (pdp_imageproc_legalheight(height) != height)){ + post("WARNING: request to create image packet with illegal dimensions %d x %d", width, height); + } +} + + +/* image packet constructors */ +int pdp_packet_new_image_YCrCb(u32 w, u32 h) +{ + t_pdp *header; + t_image *image; + int packet; + + + u32 size = w*h; + u32 totalnbpixels = size + (size >> 1); + u32 packet_size = totalnbpixels << 1; + + _checkdim(w,h); + + packet = pdp_packet_new(PDP_IMAGE, packet_size); + header = pdp_packet_header(packet); + image = pdp_packet_image_info(packet); + if (!header) return -1; + + image->encoding = PDP_IMAGE_YV12; + image->width = w; + image->height = h; + header->desc = pdp_packet_image_get_description(packet); + header->theclass = image_class; + + return packet; +} + +int pdp_packet_new_image_grey(u32 w, u32 h) +{ + t_pdp *header; + t_image *image; + int packet; + + + u32 size = w*h; + u32 totalnbpixels = size; + u32 packet_size = totalnbpixels << 1; + + _checkdim(w,h); + + packet = pdp_packet_new(PDP_IMAGE, packet_size); + header = pdp_packet_header(packet); + image = pdp_packet_image_info(packet); + if (!header) return -1; + + image->encoding = PDP_IMAGE_GREY; + image->width = w; + image->height = h; + header->desc = pdp_packet_image_get_description(packet); + header->theclass = image_class; + + return packet; +} + +int pdp_packet_new_image_mchp(u32 w, u32 h, u32 d) +{ + t_pdp *header; + t_image *image; + int packet; + + + u32 size = w*h*d; + u32 totalnbpixels = size; + u32 packet_size = totalnbpixels << 1; + + _checkdim(w,h); + + packet = pdp_packet_new(PDP_IMAGE, packet_size); + header = pdp_packet_header(packet); + image = pdp_packet_image_info(packet); + if (!header) return -1; + + + image->encoding = PDP_IMAGE_MCHP; + image->width = w; + image->height = h; + image->depth = d; + header->desc = pdp_packet_image_get_description(packet); + header->theclass = image_class; + + return packet; +} + + +int pdp_packet_new_image(u32 type, u32 w, u32 h) +{ + switch (type){ + case PDP_IMAGE_YV12: + return pdp_packet_new_image_YCrCb(w,h); + case PDP_IMAGE_GREY: + return pdp_packet_new_image_grey(w,h); + default: + return -1; + } +} + + +/****************** packet type checking and conversion methods ********************/ + + + +/* check if two image packets are allocated and of the same type */ +int pdp_packet_image_compat(int packet0, int packet1) +{ + t_pdp *header0 = pdp_packet_header(packet0); + t_pdp *header1 = pdp_packet_header(packet1); + t_image *image0 = pdp_packet_image_info(packet0); + t_image *image1 = pdp_packet_image_info(packet1); + + + if (!(pdp_packet_compat(packet0, packet1))) return 0; + if (header0->type != PDP_IMAGE){ + //post("pdp_type_compat_image: not a PDP_IMAGE"); + return 0; + } + if (image0->encoding != image1->encoding){ + //post("pdp_type_compat_image: encodings differ"); + return 0; + } + if (image0->width != image1->width){ + //post("pdp_type_compat_image: image withs differ"); + return 0; + } + if (image0->height != image1->height){ + //post("pdp_type_compat_image: image heights differ"); + return 0; + } + return 1; +} + +/* check if packet is a valid image packet */ +int pdp_packet_image_isvalid(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + if (!header) return 0; + if (PDP_IMAGE != header->type) return 0; + if ((PDP_IMAGE_YV12 != image->encoding) + && (PDP_IMAGE_GREY != image->encoding) + && (PDP_IMAGE_MCHP != image->encoding)) return 0; + + return 1; + +} + +/* set the channel mask for the image */ +void pdp_packet_image_set_chanmask(int packet, unsigned int chanmask) +{ + if (pdp_packet_image_isvalid(packet)) pdp_packet_image_info(packet)->chanmask = chanmask; + +} + + +t_image *pdp_packet_image_info(int packet) +{ + return (t_image *)pdp_packet_subheader(packet); +} + + +t_pdp_symbol *pdp_packet_image_get_description(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + char description[1024]; + char *c = description; + int encoding; + + if (!header) return pdp_gensym("invalid"); + else if (!header->desc){ + /* if description is not defined, try to reconstruct it (for backwards compat) */ + if (header->type == PDP_IMAGE){ + c += sprintf(c, "image"); + encoding = image->encoding; + switch(encoding){ + case PDP_IMAGE_YV12: c += sprintf(c, "/YCrCb"); break; + case PDP_IMAGE_GREY: c += sprintf(c, "/grey"); break; + case PDP_IMAGE_MCHP: c += sprintf(c, "/multi"); break; + default: + c += sprintf(c, "/unknown"); goto exit; + } + if (encoding == PDP_IMAGE_MCHP){ + c += sprintf(c, "/%dx%dx%d", + image->width, + image->height, + image->depth); + } + else { + c += sprintf(c, "/%dx%d", + image->width, + image->height); + } + + exit: + return pdp_gensym(description); + } + else return pdp_gensym("unknown"); + } + else return header->desc; +} + + + + + +/* IMAGE PACKAGE CONVERSION ROUTINES */ + +/* note: these are internal: no extra checking is done + it is assumed the packets are of correct type (the type template associated with the conversion program) */ + +// image/YCrCb/* -> image/grey/* +// image/multi/* -> image/grey/* (only first channel) +static int _pdp_packet_image_convert_YCrCb_to_grey(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + int w = image->width; + int h = image->height; + int p = pdp_packet_new_image_grey(w,h); + if (p == -1) return p; + memcpy(pdp_packet_data(p), pdp_packet_data(packet), 2*w*h); + return p; +} + +// image/grey/* -> image/YCrCb/* +static int _pdp_packet_image_convert_grey_to_YCrCb(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + int w = image->width; + int h = image->height; + int p = pdp_packet_new_image_YCrCb(w,h); + int y_bytes = 2*w*h; + void *data; + if (p == -1) return p; + data = pdp_packet_data(p); + memcpy(data, pdp_packet_data(packet), y_bytes); + memset(data+y_bytes, 0, y_bytes >> 1); + return p; +} + +// image/grey/* -> image/multi/* +static int _pdp_packet_image_convert_grey_to_multi(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + int w = image->width; + int h = image->height; + int p = pdp_packet_new_image_mchp(w,h,1); + int y_bytes = 2*w*h; + void *data; + if (p == -1) return p; + data = pdp_packet_data(p); + memcpy(data, pdp_packet_data(packet), y_bytes); + return p; +} + +// image/multi/* -> image/YCrCb/* (only first 3 channels) +static int _pdp_packet_image_convert_multi_to_YCrCb(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + s16 *data, *newdata; + + /* get info */ + int w = image->width; + int h = image->height; + int d = image->depth; + int plane_size = w*h; + + /* create new packet */ + int newpacket = pdp_packet_new_image_YCrCb(w, h); + if (-1 == newpacket) return -1; + + data = pdp_packet_data(packet); + newdata = pdp_packet_data(newpacket); + + /* copy channel 0 */ + memcpy(newdata, data, plane_size<<1); + newdata += plane_size; + data += plane_size; + + /* copy channel 1 */ + if (d >= 1) pdp_resample_halve(data, newdata, w, h); + else memset(newdata, 0, plane_size >> 1); + data += plane_size; + newdata += (plane_size >> 2); + + + /* copy channel 2 */ + if (d >= 2) pdp_resample_halve(data, newdata, w, h); + else memset(newdata, 0, plane_size >> 1); + + return newpacket; + +} + +// image/YCrCb/* -> image/multi/* +static int _pdp_packet_image_convert_YCrCb_to_multi(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + s16 *data, *newdata; + + /* get info */ + int w = image->width; + int h = image->height; + int plane_size = w*h; + + /* create new packet */ + int newpacket = pdp_packet_new_image_mchp(w, h, 3); + if (-1 == newpacket) return -1; + + data = pdp_packet_data(packet); + newdata = pdp_packet_data(newpacket); + + /* copy channel 0 */ + memcpy(newdata, data, plane_size<<1); + newdata += plane_size; + data += plane_size; + w >>= 1; + h >>= 1; + + /* copy channel 1 */ + pdp_resample_double(data, newdata, w, h); + data += (plane_size >> 2); + newdata += plane_size; + + /* copy channel 2 */ + pdp_resample_double(data, newdata, w, h); + + return newpacket; + +} + +static void _pdp_description_get_dims(t_pdp_symbol *template, int *w, int *h, int *d) +{ + char *c = template->s_name; + // get requested dimensions + *w = 0; + *h = 0; + *d = 0; + while (*c++ != '/'); + while (*c++ != '/'); + sscanf(c, "%dx%dx%d", w, h, d); + +} + +// resample image/YCrCb/* +static int _pdp_packet_image_convert_resample_YCrCb(int packet, t_pdp_symbol *dest_template) +{ + int quality = 1; + int dst_w, dst_h, dummy; + int new_packet; + unsigned int src_size, src_voffset, src_uoffset; + unsigned int dst_size, dst_voffset, dst_uoffset; + t_pdp *header0 = pdp_packet_header(packet); + t_image *image0 = pdp_packet_image_info(packet); + unsigned int src_w = image0->width; + unsigned int src_h = image0->height; + short int *src_image = (short int *)pdp_packet_data(packet); + short int *dst_image; + _pdp_description_get_dims(dest_template, &dst_w, &dst_h, &dummy); + new_packet = pdp_packet_new_image_YCrCb(dst_w, dst_h); + if (-1 == new_packet) return -1; + dst_image = (short int*)pdp_packet_data(new_packet); + src_size = src_w*src_h; + src_voffset = src_size; + src_uoffset = src_size + (src_size>>2); + dst_size = dst_w*dst_h; + dst_voffset = dst_size; + dst_uoffset = dst_size + (dst_size>>2); + if (quality){ + pdp_resample_scale_bilin(src_image, dst_image, src_w, src_h, dst_w, dst_h); + pdp_resample_scale_bilin(src_image+src_voffset, dst_image+dst_voffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + pdp_resample_scale_bilin(src_image+src_uoffset, dst_image+dst_uoffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + } + else{ + pdp_resample_scale_nn(src_image, dst_image, src_w, src_h, dst_w, dst_h); + pdp_resample_scale_nn(src_image+src_voffset, dst_image+dst_voffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + pdp_resample_scale_nn(src_image+src_uoffset, dst_image+dst_uoffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + } + return new_packet; +} + +// resample image/grey/* and image/multi/* +static int _pdp_packet_image_convert_resample_multi(int packet, t_pdp_symbol *dest_template) +{ + int quality = 1; + int dst_w, dst_h, depth; + int new_packet; + unsigned int src_size; + unsigned int dst_size; + t_pdp *header0 = pdp_packet_header(packet); + t_image *image0 = pdp_packet_image_info(packet); + unsigned int src_w = image0->width; + unsigned int src_h = image0->height; + short int *src_image = (short int *)pdp_packet_data(packet); + short int *dst_image; + _pdp_description_get_dims(dest_template, &dst_w, &dst_h, &depth); + + if (depth == 0){ + depth = 1; + new_packet = pdp_packet_new_image_grey(dst_w, dst_h); + } + else { + new_packet = pdp_packet_new_image_mchp(dst_w, dst_h, depth); + } + if (-1 == new_packet) return -1; + + dst_image = (short int*)pdp_packet_data(new_packet); + + src_size = src_w*src_h; + dst_size = dst_w*dst_h; + while (depth--){ + if (quality){ + pdp_resample_scale_bilin(src_image, dst_image, src_w, src_h, dst_w, dst_h); + } + else{ + pdp_resample_scale_nn(src_image, dst_image, src_w, src_h, dst_w, dst_h); + } + src_image += src_size; + dst_image += dst_size; + } + + return new_packet; +} + +static int _pdp_packet_image_convert_fallback(int packet, t_pdp_symbol *dest_template) +{ + post("can't convert image type %s to %s", + pdp_packet_get_description(packet)->s_name, dest_template->s_name); + + return -1; +} + + + +/* the expensive factory method */ +static int pdp_image_factory(t_pdp_symbol *type) +{ + t_pdp_list *l; + t_pdp_symbol *s; + int t; + int w = 0; + int h = 0; + int d = 0; + int p = -1; + int n = 0; + int m = 0; + char *garbage = 0; + + //post("creating:"); + //post("%s", type->s_name); + + l = pdp_type_to_list(type); + s = pdp_list_pop(l).w_symbol; // first element is "image" + s = pdp_list_pop(l).w_symbol; + + /* get image type */ + if (s == pdp_gensym("grey")) t = PDP_IMAGE_GREY; + else if (s == pdp_gensym("YCrCb")) t = PDP_IMAGE_YV12; + else if (s == pdp_gensym("multi")) t = PDP_IMAGE_MCHP; + else goto exit; + + /* get image dimensions and create image */ + s = pdp_list_pop(l).w_symbol; + switch (t){ + case PDP_IMAGE_MCHP: + m = sscanf(s->s_name, "%dx%dx%d", &w, &h, &d); + p = pdp_packet_new_image_mchp(w,h,d); + break; + default: + sscanf(s->s_name, "%dx%d", &w, &h); + p = pdp_packet_new_image(t,w,h); + break; + } + if (p != -1){ + t_pdp *h = pdp_packet_header(p); + /* if type is not exact, delete the packet */ + if (type != h->desc) { + pdp_packet_delete(p); + p = -1; + } + } + exit: + pdp_list_free(l); + return p; +} + + + +void pdp_image_words_setup(t_pdp_class *c); + +void pdp_image_setup(void) +{ + t_pdp_conversion_program *program; + + /* setup the class object */ + image_class = pdp_class_new(pdp_gensym("image/*/*"), pdp_image_factory); + + /* setup image words */ + pdp_image_words_setup(image_class); + + + /* setup conversion programs */ + program = pdp_conversion_program_new(_pdp_packet_image_convert_YCrCb_to_grey, 0); + pdp_type_register_conversion(pdp_gensym("image/YCrCb/*"), pdp_gensym("image/grey/*"), program); + pdp_type_register_conversion(pdp_gensym("image/multi/*"), pdp_gensym("image/grey/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_image_convert_grey_to_YCrCb, 0); + pdp_type_register_conversion(pdp_gensym("image/grey/*"), pdp_gensym("image/YCrCb/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_image_convert_grey_to_multi, 0); + pdp_type_register_conversion(pdp_gensym("image/grey/*"), pdp_gensym("image/multi/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_image_convert_multi_to_YCrCb, 0); + pdp_type_register_conversion(pdp_gensym("image/multi/*"), pdp_gensym("image/YCrCb/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_image_convert_YCrCb_to_multi, 0); + pdp_type_register_conversion(pdp_gensym("image/YCrCb/*"), pdp_gensym("image/multi/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_image_convert_resample_YCrCb, 0); + pdp_type_register_conversion(pdp_gensym("image/YCrCb/*"), pdp_gensym("image/YCrCb/*"), program); + + program = pdp_conversion_program_new(_pdp_packet_image_convert_resample_multi, 0); + pdp_type_register_conversion(pdp_gensym("image/multi/*"), pdp_gensym("image/multi/*"), program); + pdp_type_register_conversion(pdp_gensym("image/grey/*"), pdp_gensym("image/grey/*"), program); + + /* catch-all fallback */ + program = pdp_conversion_program_new(_pdp_packet_image_convert_fallback, 0); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("image/*/*"), program); + +} diff --git a/system/type/pdp_image_words.c b/system/type/pdp_image_words.c new file mode 100644 index 0000000..32b6652 --- /dev/null +++ b/system/type/pdp_image_words.c @@ -0,0 +1,176 @@ +/* + * Pure Data Packet system implementation. : methods for the 16 bit image packet + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* image packet method implementation */ + + +#include "pdp.h" +#include "pdp_forth.h" + + + +// (p0, p1) -> (p0 (operator) p1) +t_pdp_word_error pdp_word_image_pp(t_pdp_stack *s, void *process_routine) +{ + int chanmask = -1; + int packet1 = -1; + int packet2 = -1; + + /* handle stack underflow */ + if (s->elements < 2) return e_underflow; + + /* get the packets from the stack */ + pdp_stack_pop_packet(s, &packet1); + pdp_stack_pop_packet(s, &packet2); + + /* ensure the destination packet is writable */ + pdp_packet_replace_with_writable(&packet1); + + /* process */ + pdp_imageproc_dispatch_2buf(process_routine, 0, chanmask, packet1, packet2); + + /* push result on stack */ + pdp_stack_push_packet(s, packet1); + + /* release the second packet */ + pdp_packet_mark_unused(packet2); + + return e_ok; +} + +t_pdp_word_error pdp_word_image_add(t_pdp_stack *s) {return pdp_word_image_pp(s, pdp_imageproc_add_process);} +t_pdp_word_error pdp_word_image_mul(t_pdp_stack *s) {return pdp_word_image_pp(s, pdp_imageproc_mul_process);} + + +/* enter a (float packet packet) -> (packet) routine */ +#define ENTER_FPP_P(f, p0, p1) \ +void *x; \ +int p0; \ +int p1; \ +float f; \ +if (s->elements < (3)) return e_underflow; \ +pdp_stack_pop_float(s, &(f)); \ +pdp_stack_pop_packet(s, &(p0)); \ +pdp_stack_pop_packet(s, &(p1)); \ +pdp_packet_replace_with_writable(&(p0)); + +/* leave a (float packet packet) -> (packet) routine */ +#define LEAVE_FPP_P(p0, p1) \ +pdp_stack_push_packet(s, p0); \ +pdp_packet_mark_unused(p1); \ +return e_ok; + + +// (f, p0, p1) -> ((1-f)*p0 + f*p1) +t_pdp_word_error pdp_word_image_mix(t_pdp_stack *s) +{ + ENTER_FPP_P(mix, p0, p1); + + /* process */ + x = pdp_imageproc_mix_new(); + pdp_imageproc_mix_setleftgain(x, 1.0f - mix); + pdp_imageproc_mix_setrightgain(x, mix); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_mix_process, x, -1, p0, p1); + pdp_imageproc_mix_delete(x); + + LEAVE_FPP_P(p0, p1); +} + +// (f, p0) -> (f*p0) +t_pdp_word_error pdp_word_image_gain(t_pdp_stack *s) +{ + void *gaindata; + int chanmask = -1; + int packet = -1; + float gain = 0.5f; + + /* handle stack underflow */ + if (s->elements < 2) return e_underflow; + + /* get the packets from the stack */ + pdp_stack_pop_float(s, &gain); + pdp_stack_pop_packet(s, &packet); + + /* ensure the destination packet is writable */ + pdp_packet_replace_with_writable(&packet); + + /* process */ + gaindata = pdp_imageproc_gain_new(); + pdp_imageproc_gain_setgain(gaindata, gain); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_gain_process, gaindata, chanmask, packet); + pdp_imageproc_mix_delete(gaindata); + + /* push result on stack */ + pdp_stack_push_packet(s, packet); + + return e_ok; + +} +// (f, p0) -> (f*p0) +t_pdp_word_error pdp_word_image_chanmask(t_pdp_stack *s) +{ + unsigned int chanmask = 0; + int packet = -1; + + /* handle stack underflow */ + if (s->elements < 2) return e_underflow; + + /* get the packets from the stack */ + pdp_stack_pop_int(s, &chanmask); + pdp_stack_pop_packet(s, &packet); + + /* ensure the destination packet is writable */ + pdp_packet_replace_with_writable(&packet); + + /* set channel mask */ + pdp_packet_image_set_chanmask(packet, chanmask); + + /* push result on stack */ + pdp_stack_push_packet(s, packet); + + return e_ok; + +} + +static void _add_image_primitive(char *name, int in, int out, int type_index, void *w) +{ + pdp_forthdict_add_primitive(pdp_gensym(name), w, in, out, type_index, pdp_gensym("image/*/*")); +} + + +void pdp_image_words_setup(t_pdp_class *c) +{ + /* add image specific words */ + _add_image_primitive ("add", 2, 1, 0, pdp_word_image_add); + _add_image_primitive ("mix", 3, 1, 1, pdp_word_image_mix); + _add_image_primitive ("scale", 2, 1, 1, pdp_word_image_gain); + _add_image_primitive ("mul", 2, 1, 0, pdp_word_image_mul); + _add_image_primitive ("chanmask", 2, 1, 1, pdp_word_image_chanmask); + + /* add some type oblivious words */ + pdp_forthdict_compile_word(pdp_gensym("add3"), "(add dup add)", 2, 1, -1, 0); + pdp_forthdict_compile_word(pdp_gensym("mixtest"), "(0.8 2 mov mix)", 2, 1, -1, 0); + + + /* define a processor: + + def = pdp_forth_compile_def("(inlist, outlist, setup, process)"); + */ +} diff --git a/system/type/pdp_matrix.c b/system/type/pdp_matrix.c new file mode 100644 index 0000000..1334864 --- /dev/null +++ b/system/type/pdp_matrix.c @@ -0,0 +1,649 @@ + +#include "pdp.h" +#include "pdp_internals.h" + +/* the class object */ +static t_pdp_class *matrix_class; + + + +/* declare header and subheader variables and exit with errval if invalid */ +#define VALID_MATRIX_HEADER(packet, header, subheader, mtype, errval) \ +t_pdp * header = pdp_packet_header( packet ); \ +t_matrix * subheader = (t_matrix *)pdp_packet_subheader( packet ); \ +if (! header ) return errval; \ +if (PDP_MATRIX != header->type) return errval; \ +if (mtype) {if (subheader->type != mtype) return errval;} + + +int pdp_packet_matrix_isvalid(int p) +{ + VALID_MATRIX_HEADER(p, h, m, 0, 0); + return 1; +} + + +void *pdp_packet_matrix_get_gsl_matrix(int p, u32 type) +{ + VALID_MATRIX_HEADER(p, h, m, type, 0); + return &m->matrix; +} + +void *pdp_packet_matrix_get_gsl_vector(int p, u32 type) +{ + VALID_MATRIX_HEADER(p, h, m, type, 0); + return &m->vector; +} + +int pdp_packet_matrix_get_type(int p) +{ + VALID_MATRIX_HEADER(p, h, m, 0, 0); + return m->type; +} + +int pdp_packet_matrix_isvector(int p) +{ + VALID_MATRIX_HEADER(p, h, m, 0, 0); + return ((m->rows == 1) || (m->columns == 1)); +} + +int pdp_packet_matrix_ismatrix(int p) +{ + VALID_MATRIX_HEADER(p, h, m, 0, 0); + return ((m->rows != 1) && (m->columns != 1)); +} + + + + +/* gsl blas matrix/vector multiplication: + +vector.vector + + (const gsl_vector * x, const gsl_vector * y, double * result) + +gsl_blas_sdot +gsl_blas_ddot +gsl_blas_cdot +gsl_blas_zdot +gsl_blas_cdotu +gsl_blas_zdotu + +matrix.vector + ( + CBLAS_TRANSPOSE_t + TransA, + double alpha, + const gsl_matrix * A, + const gsl_vector * x, + double beta, + gsl_vector * y + ) + +gsl_blas_sgemv +gsl_blas_dgemv +gsl_blas_cgemv +gsl_blas_zgemv + +matrix.matrix + ( + CBLAS_TRANSPOSE_t TransA, + CBLAS_TRANSPOSE_t TransB, + double alpha, + const gsl_matrix * A, + const gsl_matrix * B, + double beta, + gsl_matrix * C + ) + +gsl_blas_sgemm +gsl_blas_dgemm +gsl_blas_cgemm +gsl_blas_zgemm + +*/ + +/* compute the matrix inverse using the LU decomposition */ +/* it only works for double real/complex */ +int pdp_packet_matrix_LU_to_inverse(int p) +{ + int new_p; + u32 n; + int type = pdp_packet_matrix_get_type(p); + t_matrix *sheader = (t_matrix *)pdp_packet_subheader(p); + gsl_matrix *m = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(p, type); + gsl_matrix *im; + if (!m) return -1; + n = m->gsl_rows; + if (n != m->gsl_columns) return -1; + new_p = pdp_packet_new_matrix(n, n, type); + if (-1 == new_p) return -1; + im = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(new_p, type); + + switch(type){ + case PDP_MATRIX_TYPE_RDOUBLE: + gsl_linalg_LU_invert (m, &sheader->perm, im); break; + case PDP_MATRIX_TYPE_CDOUBLE: + gsl_linalg_complex_LU_invert ((gsl_matrix_complex *)m, &sheader->perm, (gsl_matrix_complex *)im); break; + default: + pdp_packet_mark_unused(new_p); + new_p = -1; + } + return new_p; +} +/* compute the LU decomposition of a square matrix */ +/* it only works for double real/complex */ +int pdp_packet_matrix_LU(int p) +{ + int p_LU, bytes; + u32 n; + int type = pdp_packet_matrix_get_type(p); + t_matrix *sh_m_LU; + t_matrix *sh_m = (t_matrix *)pdp_packet_subheader(p); + gsl_matrix *m = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(p, type); + gsl_matrix *m_LU; + if (!m) return -1; + n = m->gsl_rows; + if (n != m->gsl_columns) return -1; + p_LU = pdp_packet_new_matrix(n, n, type); + if (-1 == p_LU) return -1; + sh_m_LU = (t_matrix *)pdp_packet_subheader(p_LU); + m_LU = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(p_LU, type); + /* copy matrix data: move this to copy method */ + memcpy(pdp_packet_data(p_LU), pdp_packet_data(p), sh_m->block.size); + + switch(type){ + case PDP_MATRIX_TYPE_RDOUBLE: + gsl_linalg_LU_decomp (m_LU, &sh_m_LU->perm, &sh_m_LU->signum); break; + case PDP_MATRIX_TYPE_CDOUBLE: + gsl_linalg_complex_LU_decomp ((gsl_matrix_complex *)m_LU, &sh_m_LU->perm, &sh_m_LU->signum); break; + default: + pdp_packet_mark_unused(p_LU); + p_LU = -1; + } + return p_LU; +} + +int pdp_packet_matrix_LU_solve(int p_matrix, int p_vector) +{ + int type = pdp_packet_matrix_get_type(p_matrix); + int p_result_vector = pdp_packet_clone_rw(p_vector); + gsl_matrix *m = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(p_matrix, type); + gsl_vector *v = (gsl_vector *)pdp_packet_matrix_get_gsl_vector(p_vector, type); + gsl_vector *rv = (gsl_vector *)pdp_packet_matrix_get_gsl_vector(p_result_vector, type); + t_matrix *sh_m = (t_matrix *)pdp_packet_subheader(p_matrix); + + if (!(m && v && rv)) goto error; + + switch(type){ + case PDP_MATRIX_TYPE_RDOUBLE: + if (gsl_linalg_LU_solve (m, &sh_m->perm, v, rv)) goto error; break; + case PDP_MATRIX_TYPE_CDOUBLE: + if(gsl_linalg_complex_LU_solve ((gsl_matrix_complex*)m, &sh_m->perm, + (gsl_vector_complex *)v, (gsl_vector_complex *)rv)) goto error; break; + default: + goto error; + } + return p_result_vector; + + error: + pdp_packet_mark_unused(p_result_vector); + //post("error"); + return -1; +} + +/* matrix matrix mul: C is defining type + returns 0 on success */ +int pdp_packet_matrix_blas_mm +( + CBLAS_TRANSPOSE_t TransA, + CBLAS_TRANSPOSE_t TransB, + int pA, + int pB, + int pC, + float scale_r, + float scale_i +) +{ + gsl_complex_float cf_scale = {{scale_r, scale_i}}; + gsl_complex cd_scale = {{(double)scale_r, (double)scale_i}}; + gsl_complex_float cf_one = {{1.0f, 0.0f}}; + gsl_complex cd_one = {{1.0, 0.0}}; + gsl_matrix *mA, *mB, *mC; + int type; + type = pdp_packet_matrix_get_type(pC); + mA = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(pA,type); + mB = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(pB,type); + mC = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(pC,type); + + if (!(mA && mB)) return 1; + + + switch(type){ + case PDP_MATRIX_TYPE_RFLOAT: + return gsl_blas_sgemm(TransA, TransB, scale_r, (gsl_matrix_float *)mA, + (gsl_matrix_float *)mB, 1.0f, (gsl_matrix_float *)mC); + case PDP_MATRIX_TYPE_RDOUBLE: + return gsl_blas_dgemm(TransA, TransB, (double)scale_r, (gsl_matrix *)mA, + (gsl_matrix *)mB, 1.0, (gsl_matrix *)mC); + case PDP_MATRIX_TYPE_CFLOAT: + return gsl_blas_cgemm(TransA, TransB, cf_scale, (gsl_matrix_complex_float *)mA, + (gsl_matrix_complex_float *)mB, cf_one, (gsl_matrix_complex_float *)mC); + case PDP_MATRIX_TYPE_CDOUBLE: + return gsl_blas_zgemm(TransA, TransB, cd_scale, (gsl_matrix_complex *)mA, + (gsl_matrix_complex *)mB, cd_one, (gsl_matrix_complex *)mC); + default: + return 0; + } +} + +/* matrix vector mul: C is defining type + returns 0 on success */ +int pdp_packet_matrix_blas_mv +( + CBLAS_TRANSPOSE_t TransA, + int pA, + int pb, + int pc, + float scale_r, + float scale_i +) +{ + gsl_complex_float cf_scale = {{scale_r, scale_i}}; + gsl_complex cd_scale = {{(double)scale_r, (double)scale_i}}; + gsl_complex_float cf_one = {{1.0f, 0.0f}}; + gsl_complex cd_one = {{1.0, 0.0}}; + gsl_matrix *mA; + gsl_vector *vb, *vc; + int type; + type = pdp_packet_matrix_get_type(pA); + mA = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(pA,type); + vb = (gsl_vector *)pdp_packet_matrix_get_gsl_vector(pb,type); + vc = (gsl_vector *)pdp_packet_matrix_get_gsl_vector(pc,type); + + if (!(vb && vc)) return 1; + + + switch(type){ + case PDP_MATRIX_TYPE_RFLOAT: + return gsl_blas_sgemv(TransA, scale_r, (gsl_matrix_float *)mA, + (gsl_vector_float *)vb, 1.0f, (gsl_vector_float *)vc); + case PDP_MATRIX_TYPE_RDOUBLE: + return gsl_blas_dgemv(TransA, (double)scale_r, (gsl_matrix *)mA, + (gsl_vector *)vb, 1.0, (gsl_vector *)vc); + case PDP_MATRIX_TYPE_CFLOAT: + return gsl_blas_cgemv(TransA, cf_scale, (gsl_matrix_complex_float *)mA, + (gsl_vector_complex_float *)vb, cf_one, (gsl_vector_complex_float *)vc); + case PDP_MATRIX_TYPE_CDOUBLE: + return gsl_blas_zgemv(TransA, cd_scale, (gsl_matrix_complex *)mA, + (gsl_vector_complex *)vb, cd_one, (gsl_vector_complex *)vc); + default: + return 0; + } +} + + + +t_pdp_symbol *_pdp_matrix_get_description(t_pdp *header) +{ + t_matrix *m = (t_matrix *)(&header->info.raw); + char description[100]; + char *c = description; + int encoding; + + if (!header) return pdp_gensym("invalid"); + else if (!header->desc){ + /* if description is not defined, try to reconstruct it (for backwards compat) */ + if (header->type == PDP_MATRIX){ + + c += sprintf(c, "matrix"); + + switch(m->type){ + case PDP_MATRIX_TYPE_RFLOAT: c += sprintf(c, "/float/real"); break; + case PDP_MATRIX_TYPE_CFLOAT: c += sprintf(c, "/float/complex"); break; + case PDP_MATRIX_TYPE_RDOUBLE: c += sprintf(c, "/double/real"); break; + case PDP_MATRIX_TYPE_CDOUBLE: c += sprintf(c, "/double/complex"); break; + default: + c += sprintf(c, "/unknown"); goto exit; + } + + c += sprintf(c, "/%dx%d", (int)m->rows, (int)m->columns); + + exit: + return pdp_gensym(description); + } + else return pdp_gensym("unknown"); + } + else return header->desc; +} + + + +static void _pdp_matrix_copy(t_pdp *dst, t_pdp *src); +static void _pdp_matrix_clone(t_pdp *dst, t_pdp *src); +static void _pdp_matrix_reinit(t_pdp *dst); +static void _pdp_matrix_cleanup(t_pdp *dst); + +static size_t _pdp_matrix_mdata_byte_size(u32 rows, u32 columns, u32 type) +{ + size_t dsize = rows * columns; + switch (type){ + case PDP_MATRIX_TYPE_RFLOAT: dsize *= sizeof(float); break; + case PDP_MATRIX_TYPE_CFLOAT: dsize *= 2*sizeof(float); break; + case PDP_MATRIX_TYPE_RDOUBLE: dsize *= sizeof(double); break; + case PDP_MATRIX_TYPE_CDOUBLE: dsize *= 2*sizeof(double); break; + default: dsize = 0; + } + return dsize; +} + + +static size_t _pdp_matrix_pdata_vector_size(u32 rows, u32 columns) +{ + return (rows>columns ? rows : columns); +} + + +static void _pdp_matrix_init(t_pdp *dst, u32 rows, u32 columns, u32 type) +{ + int i; + t_matrix *m = (t_matrix *)(&dst->info.raw); + void *d = ((void *)dst) + PDP_HEADER_SIZE; + int matrix_bytesize = _pdp_matrix_mdata_byte_size(rows, columns, type); + int permsize = _pdp_matrix_pdata_vector_size(rows, columns); + + /* set meta data */ + m->type = type; + m->rows = rows; + m->columns = columns; + + /* set the block data */ + m->block.size = matrix_bytesize; + m->block.data = (double *)d; + + /* set the vector view */ + m->vector.size = (1==columns) ? rows : columns; + m->vector.stride = 1; + m->vector.data = (double *)d; + m->vector.block = &m->block; + m->vector.owner = 0; + + /* set the matrix view */ + m->matrix.gsl_rows = rows; + m->matrix.gsl_columns = columns; + m->matrix.tda = columns; + m->matrix.data = (double *)d; + m->matrix.block = &m->block; + m->matrix.owner = 0; + + /* set the permutation object & init */ + m->perm.size = permsize; + m->perm.data = (size_t *)(d + matrix_bytesize); + for(i=0; i<permsize; i++) m->perm.data[i] = i; + m->signum = -1; + + /* init packet header */ + dst->theclass = matrix_class; + dst->desc = _pdp_matrix_get_description(dst); + +} + +static void _pdp_matrix_clone(t_pdp *dst, t_pdp *src) +{ + t_matrix *m = (t_matrix *)(&src->info.raw); + _pdp_matrix_init(dst, m->rows, m->columns, m->type); + +} +static void _pdp_matrix_copy(t_pdp *dst, t_pdp *src) +{ + _pdp_matrix_clone(dst, src); + memcpy(dst + PDP_HEADER_SIZE, src + PDP_HEADER_SIZE, src->size - PDP_HEADER_SIZE); + //post("matrix copy successful"); +} +static void _pdp_matrix_reinit(t_pdp *dst) +{ + /* nothing to do, assuming data is correct */ +} +static void _pdp_matrix_cleanup(t_pdp *dst) +{ + /* no extra memory to free */ +} + +int pdp_packet_new_matrix(u32 rows, u32 columns, u32 type) +{ + t_pdp *header; + int dsize, p; + + /* compute the blocksize for the matrix data */ + /* if 0, something went wrong -> return invalid packet */ + if (!(dsize = _pdp_matrix_mdata_byte_size(rows, columns, type))) return -1; + dsize += sizeof(size_t) * _pdp_matrix_pdata_vector_size(rows, columns); + + p = pdp_packet_new(PDP_MATRIX, dsize); + if (-1 == p) return -1; + header = pdp_packet_header(p); + + _pdp_matrix_init(header, rows, columns, type); + + return p; +} + +int pdp_packet_new_matrix_product_result(CBLAS_TRANSPOSE_t TransA, CBLAS_TRANSPOSE_t TransB, int pA, int pB) +{ + gsl_matrix *mA, *mB; + u32 type = pdp_packet_matrix_get_type(pA); + + u32 colA, colB, rowA, rowB; + + /* check if A is a matrix */ + if (!type) return -1; + + mA = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(pA, type); + mB = (gsl_matrix *)pdp_packet_matrix_get_gsl_matrix(pB, type); + + /* check if A and B are same type */ + if (!mB) return -1; + + /* get dims A */ + if (TransA == CblasNoTrans){ + rowA = mA->gsl_rows; + colA = mA->gsl_columns; + } + else { + rowA = mA->gsl_columns; + colA = mA->gsl_rows; + } + + /* get dims B */ + if (TransB == CblasNoTrans){ + rowB = mB->gsl_rows; + colB = mB->gsl_columns; + } + else { + rowB = mB->gsl_columns; + colB = mB->gsl_rows; + } + + /* check if sizes are compatible */ + if (colA != rowB) return -1; + + /* create new packet */ + return pdp_packet_new_matrix(rowA, colB, type); +} + +void pdp_packet_matrix_setzero(int p) +{ + t_matrix *m; + if (!pdp_packet_matrix_isvalid(p)) return; + m = (t_matrix *) pdp_packet_subheader(p); + memset(m->block.data, 0, m->block.size); +} + + + +/* type conversion programs */ +static int _pdp_packet_convert_matrix_to_greyimage(int packet, t_pdp_symbol *dest_template) +{ + t_pdp *header = pdp_packet_header(packet); + t_matrix *matrix = (t_matrix *)pdp_packet_subheader(packet); + void *data = pdp_packet_data(packet); + s16 *new_data; + u32 c,r, nbelements; + int new_p; + u32 i; + + c = matrix->columns; + r = matrix->rows; + nbelements = c*r; + + new_p = pdp_packet_new_image_grey(c,r); + if (-1 == new_p) return -1; + new_data = (s16 *)pdp_packet_data(new_p); + + /* convert first channel */ + switch(matrix->type){ + case PDP_MATRIX_TYPE_RFLOAT: + for (i=0; i<nbelements; i++) (new_data)[i] = (s16)(((float *)data)[i] * (float)0x8000); break; + case PDP_MATRIX_TYPE_CFLOAT: //only copy real channel + for (i=0; i<nbelements; i++) (new_data)[i] = (s16)(((float *)data)[i<<1] * (float)0x8000); break; + case PDP_MATRIX_TYPE_RDOUBLE: + for (i=0; i<nbelements; i++) (new_data)[i] = (s16)(((double *)data)[i] * (float)0x8000); break; + case PDP_MATRIX_TYPE_CDOUBLE: //only copy real channel + for (i=0; i<nbelements; i++) (new_data)[i] = (s16)(((double *)data)[i<<1] * (float)0x8000); break; + default: + pdp_packet_mark_unused(new_p); + new_p = -1; + } + return new_p; +} + +static int _pdp_packet_convert_image_to_matrix(int packet, t_pdp_symbol *dest_template, int type) +{ + t_pdp *header = pdp_packet_header(packet); + t_image *image = pdp_packet_image_info(packet); + s16 *data = (s16 *)pdp_packet_data(packet); + void *new_data; + u32 w,h, nbelements; + int new_p; + int encoding = image->encoding; + u32 i; + + if (!pdp_packet_image_isvalid(packet)) return -1; + w = image->width; + h = image->height; + nbelements = w*h; + + new_p = pdp_packet_new_matrix(h,w, type); + if (-1 == new_p) return -1; + new_data = pdp_packet_data(new_p); + + switch (encoding){ + case PDP_IMAGE_YV12: + case PDP_IMAGE_GREY: + case PDP_IMAGE_MCHP: + /* convert first channel */ + switch(type){ + case PDP_MATRIX_TYPE_RFLOAT: + for (i=0; i<nbelements; i++) + ((float *)new_data)[i] = ((float)data[i]) * (1.0f / (float)0x8000); break; + case PDP_MATRIX_TYPE_RDOUBLE: + for (i=0; i<nbelements; i++) + ((double *)new_data)[i] = ((double)data[i]) * (1.0f / (double)0x8000); break; + case PDP_MATRIX_TYPE_CFLOAT: + for (i=0; i<nbelements; i++){ + ((float *)new_data)[i*2] = ((float)data[i]) * (1.0f / (float)0x8000); + ((float *)new_data)[i*2+1] = 0.0f; + } + break; + case PDP_MATRIX_TYPE_CDOUBLE: + for (i=0; i<nbelements; i++){ + ((double *)new_data)[i*2] = ((double)data[i]) * (1.0f / (double)0x8000); + ((double *)new_data)[i*2+1] = 0.0; + } + break; + default: + post("_pdp_packet_convert_image_to_matrix: INTERNAL ERROR"); + } + break; + default: + pdp_packet_mark_unused(new_p); + new_p = -1; + break; + } + + return new_p; + +} + +static int _pdp_packet_convert_image_to_floatmatrix(int packet, t_pdp_symbol *dest_template){ + return _pdp_packet_convert_image_to_matrix(packet, dest_template, PDP_MATRIX_TYPE_RFLOAT);} +static int _pdp_packet_convert_image_to_doublematrix(int packet, t_pdp_symbol *dest_template){ + return _pdp_packet_convert_image_to_matrix(packet, dest_template, PDP_MATRIX_TYPE_RDOUBLE);} +static int _pdp_packet_convert_image_to_complexfloatmatrix(int packet, t_pdp_symbol *dest_template){ + return _pdp_packet_convert_image_to_matrix(packet, dest_template, PDP_MATRIX_TYPE_CFLOAT);} +static int _pdp_packet_convert_image_to_complexdoublematrix(int packet, t_pdp_symbol *dest_template){ + return _pdp_packet_convert_image_to_matrix(packet, dest_template, PDP_MATRIX_TYPE_CDOUBLE);} + + +static int _pdp_packet_matrix_convert_fallback(int packet, t_pdp_symbol *dest_template) +{ + post("can't convert image type %s to %s", + pdp_packet_get_description(packet)->s_name, dest_template->s_name); + + return -1; +} + + +/* this seems like a nice spot to place a dummy gsl signal handler */ +static void _gsl_error_handler (const char * reason, + const char * file, + int line, + int gsl_errno) +{ + //post("gsl error:\nREASON: %s\nFILE:%s\nLINE:%d\nERRNO:%d", reason, file, line, gsl_errno); +} + +static int pdp_matrix_factory(t_pdp_symbol *type) +{ + return -1; +} +void pdp_matrix_setup(void) +{ + t_pdp_conversion_program *program; + + + /* setup the class */ + matrix_class = pdp_class_new(pdp_gensym("matrix/*/*/*"), pdp_matrix_factory); + matrix_class->copy = _pdp_matrix_copy; + matrix_class->clone = _pdp_matrix_clone; + matrix_class->reinit = _pdp_matrix_reinit; + matrix_class->cleanup = _pdp_matrix_cleanup; + + + /* image -> matrix: default = double/real (most ops available) */ + program = pdp_conversion_program_new(_pdp_packet_convert_image_to_doublematrix, 0); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("matrix/double/real/*"), program); + program = pdp_conversion_program_new(_pdp_packet_convert_image_to_floatmatrix, 0); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("matrix/float/real/*"), program); + program = pdp_conversion_program_new(_pdp_packet_convert_image_to_complexdoublematrix, 0); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("matrix/double/complex/*"), program); + program = pdp_conversion_program_new(_pdp_packet_convert_image_to_complexfloatmatrix, 0); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("matrix/float/complex/*"), program); + + /* matrix -> image */ + program = pdp_conversion_program_new(_pdp_packet_convert_matrix_to_greyimage, 0); + pdp_type_register_conversion(pdp_gensym("matrix/*/*/*"), pdp_gensym("image/grey/*"), program); + + /* fallbacks */ + program = pdp_conversion_program_new(_pdp_packet_matrix_convert_fallback, 0); + pdp_type_register_conversion(pdp_gensym("matrix/*/*/*"), pdp_gensym("image/*/*"), program); + pdp_type_register_conversion(pdp_gensym("matrix/*/*/*"), pdp_gensym("bitmap/*/*"), program); + pdp_type_register_conversion(pdp_gensym("image/*/*"), pdp_gensym("matrix/*/*/*/*"), program); + pdp_type_register_conversion(pdp_gensym("bitmap/*/*"), pdp_gensym("matrix/*/*/*/*"), program); + + /* setup gsl handler */ + if(gsl_set_error_handler(_gsl_error_handler)){ + post("pdp_matrix_setup: WARNING: overriding gsl error handler."); + } + +} |