diff options
Diffstat (limited to 'system')
54 files changed, 13266 insertions, 0 deletions
diff --git a/system/CONTENTS b/system/CONTENTS new file mode 100644 index 0000000..a1dd455 --- /dev/null +++ b/system/CONTENTS @@ -0,0 +1,9 @@ +X11 x window specific code +forth packet forth code +image image processing code +kernel the core pdp system +mmx mmx assembly code +net networking support +png png support +type different packet type implementations + diff --git a/system/Makefile b/system/Makefile new file mode 100644 index 0000000..eba38f9 --- /dev/null +++ b/system/Makefile @@ -0,0 +1,30 @@ + +include ../Makefile.config + +all: $(PDP_TARGET) pdp.o + +common: + make -C net + make -C X11 + make -C png + make -C type + make -C kernel + make -C image $(PDP_TARGET) + +linux_mmx: common + make -C mmx + +linux: common + +darwin: common + +clean: + rm -f *~ + rm -f *.o + make -C mmx clean + make -C net clean + make -C X11 clean + make -C png clean + make -C image clean + make -C type clean + make -C kernel clean diff --git a/system/X11/Makefile b/system/X11/Makefile new file mode 100644 index 0000000..205f975 --- /dev/null +++ b/system/X11/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = $(PDP_X11MOD) + + +include ../../Makefile.config + +all: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/X11/pdp_xvideo.c b/system/X11/pdp_xvideo.c new file mode 100644 index 0000000..dab060d --- /dev/null +++ b/system/X11/pdp_xvideo.c @@ -0,0 +1,201 @@ +/* + * 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 <string.h> + +#include "pdp_xwindow.h" +#include "pdp_xvideo.h" +#include "pdp_post.h" +#include "pdp_packet.h" + +#define D if(0) + + + + +/************************************* PDP_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->xdpy->dpy, xvid->xv_port, xvid->xv_format, (char *)xvid->data, xvid->width, xvid->height); + xvid->last_encoding = -1; + if ((!xvid->xvi) || (!xvid->data)) pdp_post ("ERROR CREATING XVIMAGE"); + //pdp_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)){ + //pdp_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->xdpy->dpy,xvid->xv_port, xwin->win,xwin->gc,xvid->xvi, + 0,0,xvid->width,xvid->height, 0,0,xwin->winwidth,xwin->winheight); + XFlush(xvid->xdpy->dpy); + + + +} + + + +void pdp_xvideo_close(t_pdp_xvideo* xvid) +{ + if (xvid->initialized){ + if (xvid->xvi) pdp_xvideo_destroy_xvimage(xvid); + XvUngrabPort(xvid->xdpy->dpy, xvid->xv_port, CurrentTime); + xvid->xv_port = 0; + xvid->xdpy = 0; + xvid->last_encoding = -1; + xvid->initialized = false; + } +} + +void pdp_xvideo_cleanup(t_pdp_xvideo* xvid) +{ + // close xvideo port (and delete XvImage) + pdp_xvideo_close(xvid); + + // no more dynamic data to free + +} + +void pdp_xvideo_free(t_pdp_xvideo* xvid){ + pdp_xvideo_cleanup(xvid); + pdp_dealloc(xvid); +} + +void pdp_xvideo_init(t_pdp_xvideo *xvid) +{ + + xvid->xdpy = 0; + + 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; + +} +t_pdp_xvideo *pdp_xvideo_new(void) +{ + t_pdp_xvideo *xvid = pdp_alloc(sizeof(*xvid)); + pdp_xvideo_init(xvid); + return xvid; +} + +int pdp_xvideo_open_on_display(t_pdp_xvideo *xvid, t_pdp_xdisplay *d) +{ + unsigned int ver, rel, req, ev, err, i, j; + unsigned int adaptors; + int formats; + XvAdaptorInfo *ai; + + if (xvid->initialized) return 1; + if (!d) return 0; + xvid->xdpy = d; + + if (Success != XvQueryExtension(xvid->xdpy->dpy,&ver,&rel,&req,&ev,&err)) return 0; + + /* find + lock port */ + if (Success != XvQueryAdaptors(xvid->xdpy->dpy,DefaultRootWindow(xvid->xdpy->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->xdpy->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; + pdp_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/X11/pdp_xwindow.c b/system/X11/pdp_xwindow.c new file mode 100644 index 0000000..9454dd7 --- /dev/null +++ b/system/X11/pdp_xwindow.c @@ -0,0 +1,623 @@ +/* + * 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 <string.h> +#include "pdp_xwindow.h" +#include "pdp_post.h" +#include "pdp_debug.h" +#include "pdp_symbol.h" +#include "pdp_list.h" + +#define D if(0) + + +// xwin->xdisplay->screen = DefaultScreen(xwin->xdisplay->dpy); + +/* x display class +typedef struct _pdp_xdisplay +{ + Display *dpy; // the display connection + int screen; // the screen + t_pdp_list *windowlist; // all windows belonging to this connection + // this contains (xwindow object, eventlist) +} t_pdp_xdisplay; */ + + +/************************************* PDP_XDISPLAY ************************************/ + + +t_pdp_xdisplay *pdp_xdisplay_new(char *dpy_string) +{ + t_pdp_xdisplay *d = pdp_alloc(sizeof(*d)); + if (!(d->dpy = XOpenDisplay(dpy_string))){ + pdp_post ("pdp_xdisplay_new: can't open display %s", dpy_string); + pdp_dealloc(d); + return (0); + } + + + d->windowlist = pdp_list_new(0); + d->screen = DefaultScreen(d->dpy); + d->dragbutton = -1; + return d; +} + +void pdp_xdisplay_free(t_pdp_xdisplay *d) +{ + XCloseDisplay(d->dpy); + PDP_ASSERT(0 == d->windowlist->elements); // make sure there are no dangling xwindow objects + pdp_list_free(d->windowlist); + pdp_dealloc(d); +} + +/* some private members */ +static int _windowset_contains(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + t_pdp_atom *a; + for (a=d->windowlist->first; a; a=a->next){ + if (w == a->w.w_list->first->w.w_pointer) return 1; + } + return 0; +} + +static void _windowset_add(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + t_pdp_list *l = pdp_list_new(0); // a new list for this window + t_pdp_list *l_ev = pdp_list_new(0); // the event list + pdp_list_add_back_pointer(l, w); + pdp_list_add_back(l, a_list, (t_pdp_word)l_ev); + + pdp_list_add_back(d->windowlist, a_list, (t_pdp_word)l); +} + +/* get the list describing this window */ +static t_pdp_list *_windowset_get_info_for_Window(t_pdp_xdisplay *d, Window win) +{ + t_pdp_atom *a; + for (a=d->windowlist->first; a; a=a->next){ + if (win == ((t_pdp_xwindow *)a->w.w_list->first->w.w_pointer)->win) return a->w.w_list; + } + return 0; +} + +static t_pdp_list *_windowset_get_info(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + t_pdp_atom *a; + for (a=d->windowlist->first; a; a=a->next){ + if (w == a->w.w_list->first->w.w_pointer) return a->w.w_list; + } + return 0; +} + +static void _windowset_remove(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + t_pdp_list *l = _windowset_get_info(d, w); + if (l){ + pdp_list_remove(d->windowlist, a_list, (t_pdp_word)l); + pdp_tree_free(l); + } +} + +void pdp_xdisplay_register_window(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + + if (!_windowset_contains(d, w)) _windowset_add(d, w); +} +void pdp_xdisplay_unregister_window(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + if (_windowset_contains(d, w)) _windowset_remove(d, w); +} + + +/* LOCKING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111*/ +/* get events from display and store in queues */ +void pdp_xdisplay_get_events(t_pdp_xdisplay *d) +{ + + unsigned int i; + XEvent e; + + /* event tags */ + char tag_drag[] = "drag0"; + char tag_press[] = "press0"; + char tag_release[] = "release0"; + char tag_motion[] = "motion0"; + + char *BUT(char *c) {return c + strlen(c) - 1;} + + /* button chars */ + char *but_drag = BUT(tag_drag); + char *but_press = BUT(tag_press); + char *but_release = BUT(tag_release); + char *but_motion = BUT(tag_motion); + + int nbEvents = XEventsQueued(d->dpy, QueuedAlready); + int bmask = Button1Mask + | Button2Mask + | Button3Mask + | Button4Mask + | Button5Mask; + + + while (XPending(d->dpy)){ + XNextEvent(d->dpy, &e); + + + /* get the window info list for this X11 Window */ + t_pdp_list *winfo = _windowset_get_info_for_Window(d, e.xany.window); + + /* get the window object */ + t_pdp_xwindow *xwin = (t_pdp_xwindow *)winfo->first->w.w_pointer; + + /* get the event list corresponding to this window */ + t_pdp_list *eventlist = winfo->first->next->w.w_list; + + /* set dim scalers */ + float inv_x = 1.0f / (float)(xwin->winwidth); + float inv_y = 1.0f / (float)(xwin->winheight); + + /* list to store new event */ + t_pdp_list *newevent = 0; + + /* event tag */ + char *tag; + char *but; + + + /* handle event */ + switch(e.type){ + case ConfigureNotify: + /* store new dimensions */ + xwin->winwidth = e.xconfigure.width; + xwin->winheight = e.xconfigure.height; + break; + + case ClientMessage: + if ((Atom)e.xclient.data.l[0] == xwin->WM_DELETE_WINDOW) { + newevent = pdp_list_new(1); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym("close")); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + } + break; + + case KeyPress: + case KeyRelease: + newevent = pdp_list_new(2); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym(e.type == KeyPress ? "keypress" : "keyrelease")); + pdp_list_set_1(newevent, a_int, (t_pdp_word)(int)e.xkey.keycode); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + break; + + case ButtonPress: + case ButtonRelease: + /* event specific stuff */ + if (e.type == ButtonPress){ + tag = tag_press; + but = but_press; + } + else { + tag = tag_release; + but = but_release; + } + + /* send generic event */ + *but = 0; + newevent = pdp_list_new(3); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym(tag)); + pdp_list_set_1(newevent, a_float, (t_pdp_word)(inv_x * (float)e.xbutton.x)); + pdp_list_set_2(newevent, a_float, (t_pdp_word)(inv_y * (float)e.xbutton.y)); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + + /* send button specific event */ + *but = '1' + e.xbutton.button - Button1; + newevent = pdp_list_new(3); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym(tag)); + pdp_list_set_1(newevent, a_float, (t_pdp_word)(inv_x * (float)e.xbutton.x)); + pdp_list_set_2(newevent, a_float, (t_pdp_word)(inv_y * (float)e.xbutton.y)); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + + /* save drag button */ + xwin->lastbut = *but; + + break; + + case MotionNotify: + if (e.xbutton.state & bmask){ + /* button is down: it is a drag event */ + tag = tag_drag; + but = but_drag; + + /* send generic event */ + *but = 0; + newevent = pdp_list_new(3); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym(tag)); + pdp_list_set_1(newevent, a_float, (t_pdp_word)(inv_x * (float)e.xbutton.x)); + pdp_list_set_2(newevent, a_float, (t_pdp_word)(inv_y * (float)e.xbutton.y)); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + + /* send button specific event */ + *but = xwin->lastbut; + newevent = pdp_list_new(3); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym(tag)); + pdp_list_set_1(newevent, a_float, (t_pdp_word)(inv_x * (float)e.xbutton.x)); + pdp_list_set_2(newevent, a_float, (t_pdp_word)(inv_y * (float)e.xbutton.y)); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + + } + else { + tag = tag_motion; + but = but_motion; + *but = 0; + + /* send generic event */ + newevent = pdp_list_new(3); + pdp_list_set_0(newevent, a_symbol, (t_pdp_word)pdp_gensym(tag)); + pdp_list_set_1(newevent, a_float, (t_pdp_word)(inv_x * (float)e.xbutton.x)); + pdp_list_set_2(newevent, a_float, (t_pdp_word)(inv_y * (float)e.xbutton.y)); + pdp_list_add_back(eventlist, a_list, (t_pdp_word)newevent); + + } + + + + default: + //pdp_post("pdp_xv: unknown event"); + break; + } + + + } + + +} + + +/* return a list containing event lists */ + +t_pdp_list *pdp_xdisplay_get_events_for_window(t_pdp_xdisplay *d, t_pdp_xwindow *w) +{ + t_pdp_list *info = _windowset_get_info(d, w); + t_pdp_list *eventlist; + PDP_ASSERT(info); + + /* get all pending events from display */ + pdp_xdisplay_get_events(d); + + /* get the event list for this window and create a new one */ + eventlist = info->first->next->w.w_list; + info->first->next->w.w_list = pdp_list_new(0); + + return eventlist; + +} + + +/************************************* PDP_XWINDOW ************************************/ + +void pdp_xwindow_warppointer(t_pdp_xwindow *xwin, int x, int y) +{ + if (xwin->initialized){ + XWarpPointer(xwin->xdisplay->dpy, None, xwin->win, 0, 0, 0, 0, x, y); + } +} + + + + +static void pdp_xwindow_overrideredirect(t_pdp_xwindow *xwin, int b) +{ + XSetWindowAttributes new_attr; + new_attr.override_redirect = b ? True : False; + XChangeWindowAttributes(xwin->xdisplay->dpy, xwin->win, CWOverrideRedirect, &new_attr); + //XFlush(xwin->xdisplay->dpy); + +} + + +void pdp_xwindow_moveresize(t_pdp_xwindow *xwin, int xoffset, int yoffset, int width, int height) +{ + + D pdp_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->xdisplay->dpy, xwin->win, xoffset, yoffset, width, height); + XFlush(xwin->xdisplay->dpy); + } + } +} + + +void pdp_xwindow_fullscreen(t_pdp_xwindow *xwin) +{ + XWindowAttributes rootwin_attr; + + D pdp_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->xdisplay->dpy, RootWindow(xwin->xdisplay->dpy, xwin->xdisplay->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->xdisplay->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 pdp_post("pdp_xwindow_fullscreen"); + + if (xwin->initialized){ + int tile_w; + int tile_h; + XGetWindowAttributes(xwin->xdisplay->dpy, RootWindow(xwin->xdisplay->dpy, xwin->xdisplay->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->xdisplay->dpy, xwin->win, CWOverrideRedirect, &new_attr ); + XMoveResizeWindow(xwin->xdisplay->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 pdp_post("pdp_xwindow_resize"); + if ((width > 0) && (height > 0)){ + xwin->winwidth = width; + xwin->winheight = height; + if (xwin->initialized){ + XResizeWindow(xwin->xdisplay->dpy, xwin->win, width, height); + XFlush(xwin->xdisplay->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 pdp_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) */ +t_pdp_list *pdp_xwindow_get_eventlist(t_pdp_xwindow *xwin) +{ + t_pdp_list *eventlist; + + eventlist = pdp_xdisplay_get_events_for_window(xwin->xdisplay, xwin); + D pdp_list_print(eventlist); + + return eventlist; + +} + + + +/* set an arbitrary cursor image */ +void pdp_xwindow_cursor_image(t_pdp_xwindow *xwin, char *data, int width, int height) +{ + if (!xwin->initialized) return; + + Cursor cursor; + Pixmap pm; + XColor fg; + XColor bg; + + fg.red = fg.green = fg.blue = 0xffff; + bg.red = bg.green = bg.blue = 0x0000; + + pm = XCreateBitmapFromData(xwin->xdisplay->dpy, xwin->win, data, width, height); + cursor = XCreatePixmapCursor(xwin->xdisplay->dpy, pm, pm, &fg, + &bg, width/2, height/2); + XFreePixmap(xwin->xdisplay->dpy, pm); + XDefineCursor(xwin->xdisplay->dpy, xwin->win,cursor); +} + +/* enable / disable cursor */ +void pdp_xwindow_cursor(t_pdp_xwindow *xwin, int i){ + if (!xwin->initialized) return; + if (i == 0) { + char data[] = {0}; + pdp_xwindow_cursor_image(xwin, data, 1, 1); + } + else + XUndefineCursor(xwin->xdisplay->dpy, xwin->win); + + xwin->cursor = i; +} + + +void pdp_xwindow_title(t_pdp_xwindow *xwin, char *title) +{ + if (xwin->initialized) + XStoreName(xwin->xdisplay->dpy, xwin->win, title); +} + + +/* create xwindow */ +int pdp_xwindow_create_on_display(t_pdp_xwindow *xwin, t_pdp_xdisplay *d) +{ + XEvent e; + unsigned int i; + + /* check if already opened */ + if( xwin->initialized ){ + pdp_post("pdp_xwindow_create_on_display: window already created"); + goto exit; + } + + xwin->xdisplay = d; + PDP_ASSERT(xwin->xdisplay); + + /* create a window */ + xwin->win = XCreateSimpleWindow( + xwin->xdisplay->dpy, + RootWindow(xwin->xdisplay->dpy, xwin->xdisplay->screen), xwin->winxoffset, xwin->winyoffset, xwin->winwidth, xwin->winheight, 0, + BlackPixel(xwin->xdisplay->dpy, xwin->xdisplay->screen), + BlackPixel(xwin->xdisplay->dpy, xwin->xdisplay->screen)); + + + /* enable handling of close window event */ + xwin->WM_DELETE_WINDOW = XInternAtom(xwin->xdisplay->dpy, "WM_DELETE_WINDOW", True); + (void)XSetWMProtocols(xwin->xdisplay->dpy, xwin->win, &xwin->WM_DELETE_WINDOW, 1); + + if(!(xwin->win)){ + /* clean up mess */ + pdp_post("pdp_xwindow_create_on_display: could not create window. closing.\n"); + //XCloseDisplay(xwin->xdisplay->dpy); NOT OWNER + xwin->xdisplay = 0; + xwin->initialized = 0; + goto exit; + } + + /* select input events */ + XSelectInput(xwin->xdisplay->dpy, xwin->win, + StructureNotifyMask + | KeyPressMask + | KeyReleaseMask + | ButtonPressMask + | ButtonReleaseMask + | MotionNotify + | PointerMotionMask); + // | ButtonMotionMask); + //XSelectInput(xwin->xdisplay->dpy, xwin->win, StructureNotifyMask); + + + + /* map */ + XMapWindow(xwin->xdisplay->dpy, xwin->win); + + /* create graphics context */ + xwin->gc = XCreateGC(xwin->xdisplay->dpy, xwin->win, 0, 0); + + /* catch mapnotify */ + for(;;){ + XNextEvent(xwin->xdisplay->dpy, &e); + if (e.type == MapNotify) break; + } + + + /* we're done initializing */ + xwin->initialized = 1; + + /* disable/enable cursor */ + pdp_xwindow_cursor(xwin, xwin->cursor); + + /* set window title */ + pdp_xwindow_title(xwin, "pdp"); + + /* register window for events */ + /* TODO: move event selection ETC to xdisplay object */ + + pdp_xdisplay_register_window(xwin->xdisplay, xwin); + + exit: + return xwin->initialized; + +} + +void pdp_xwindow_init(t_pdp_xwindow *xwin) +{ + xwin->xdisplay = 0; + + xwin->winwidth = 320; + xwin->winheight = 240; + xwin->winxoffset = 0; + xwin->winyoffset = 0; + + xwin->initialized = 0; + + xwin->cursor = 0; + //xwin->dragbutton = gensym("drag1"); + +} +t_pdp_xwindow *pdp_xwindow_new(void) +{ + t_pdp_xwindow *xwin = pdp_alloc(sizeof(*xwin)); + pdp_xwindow_init(xwin); + return xwin; +} + + +void pdp_xwindow_close(t_pdp_xwindow *xwin) +{ + + XEvent e; + + if (xwin->initialized){ + XFreeGC(xwin->xdisplay->dpy, xwin->gc); + XDestroyWindow(xwin->xdisplay->dpy, xwin->win); + while(XPending(xwin->xdisplay->dpy)) XNextEvent(xwin->xdisplay->dpy, &e); + pdp_xdisplay_unregister_window(xwin->xdisplay, xwin); + xwin->xdisplay = 0; + xwin->initialized = 0; + } + +} + +void pdp_xwindow_cleanup(t_pdp_xwindow *x) +{ + // close win + pdp_xwindow_close(x); + + // no more dynamic data to free +} + +void pdp_xwindow_free(t_pdp_xwindow *xwin) +{ + pdp_xwindow_cleanup(xwin); + pdp_dealloc(xwin); +} + + 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..184c418 --- /dev/null +++ b/system/image/pdp_imageproc_common.c @@ -0,0 +1,610 @@ +/* + * 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_imageproc.h" +#include "pdp_image.h" +#include "pdp_mem.h" +#include "pdp_packet.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; +} + + +/* 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)) return 0; + if (!(header0)) return 0; + if (header0->type != header1->type) return 0; + return 1; +} + +/* 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; + mask_bot = (s32)(((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..67f6e77 --- /dev/null +++ b/system/image/pdp_imageproc_mmx.c @@ -0,0 +1,594 @@ +/* + * 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" + +/* 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 +#define MIX_SIZE 8*sizeof(s16) +void *pdp_imageproc_mix_new(void){return pdp_alloc(MIX_SIZE);} +int pdp_imageproc_mix_nb_stackwords(void){return PDP_IMAGEPROC_NB_STACKWORDS(MIX_SIZE);} +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 +#define RANDMIX_SIZE 8*sizeof(s16) +void *pdp_imageproc_randmix_new(void){return pdp_alloc(RANDMIX_SIZE);} +int pdp_imageproc_randmix_nb_stackwords(void) {return PDP_IMAGEPROC_NB_STACKWORDS(RANDMIX_SIZE);} +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..112f729 --- /dev/null +++ b/system/image/pdp_imageproc_portable.c @@ -0,0 +1,681 @@ +/* + * 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));} +int pdp_imageproc_mix_nb_stackwords(void) {return PDP_IMAGEPROC_NB_STACKWORDS(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));;} +int pdp_imageproc_randmix_nb_stackwords(void) {return PDP_IMAGEPROC_NB_STACKWORDS(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..f10c61d --- /dev/null +++ b/system/image/pdp_llconv.c @@ -0,0 +1,574 @@ +/* + * 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. */ + +/* seem's there's some confusion between rgb and bgr formats. + not that it matters much which is supposed to be the "real" + rgb or bgr, but opengl and v4l seem to disagree on endianness + grounds.. */ + +#include "pdp_llconv.h" +#include "pdp_mmx.h" +#include "pdp_post.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); + + } + + } + +} + + + +/* common 8 bit rgb -> 16 bit yvu */ +inline static void llconv_rgb2yvu_planar16sub_indexed(unsigned char* src, short int* dst, int w, int h, int ir, int ig, int ib, int stride) +{ + 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 * stride; + + k=0; + for (j=0; j<w*h; j+=(w<<1)){ + k = stride * j; + for (i=0; i<w; i+=2){ + + + // well, this seems to work... strange though + r = src[k+ir]; + g = src[k+ig]; + b = src[k+ib]; + + 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+stride+ir]; + g = src[k+stride+ig]; + b = src[k+stride+ib]; + + 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+ir]; + g = src[loffset + k+ig]; + b = src[loffset + k+ib]; + + 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+stride+ir]; + g = src[loffset + k+stride+ig]; + b = src[loffset + k+stride+ib]; + + k += 2 * stride; + + 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 16 bit planar subsampled yvu */ +static void llconv_rgb2yvu_planar16sub(unsigned char* src, short int* dst, int w, int h) +{ + llconv_rgb2yvu_planar16sub_indexed(src,dst,w,h,0,1,2,3); +} + +/* 8 bit rgba to 16 bit planar subsampled yvu */ +static void llconv_rgba2yvu_planar16sub(unsigned char* src, short int* dst, int w, int h) +{ + llconv_rgb2yvu_planar16sub_indexed(src,dst,w,h,0,1,2,4); +} + +/* 8 bit bgr to 16 bit planar subsampled yvu */ +static void llconv_bgr2yvu_planar16sub(unsigned char* src, short int* dst, int w, int h) +{ + llconv_rgb2yvu_planar16sub_indexed(src,dst,w,h,2,1,0,3); +} + +/* 8 bit bgra to 16 bit planar subsampled yvu */ +static void llconv_bgra2yvu_planar16sub(unsigned char* src, short int* dst, int w, int h) +{ + llconv_rgb2yvu_planar16sub_indexed(src,dst,w,h,2,1,0,4); +} + + +/* 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)); + } + } +} + + +/* 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_RGBA_P____U8, RIF_YVU__P411_S16 ): + llconv_rgba2yvu_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_BGRA_P____U8, RIF_YVU__P411_S16 ): + llconv_bgra2yvu_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: + pdp_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..6fbd274 --- /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 <string.h> +#include "pdp_resample.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/CONTENTS b/system/kernel/CONTENTS new file mode 100644 index 0000000..c2f7c8c --- /dev/null +++ b/system/kernel/CONTENTS @@ -0,0 +1,7 @@ +debug debug stuff +forth the forth system +list the list implementation +mem memory allocation stuf +packet the packet memory manager +type the type handling and conversion system +symbol symbol implementation, with namespaces for forth, types, classes, ... diff --git a/system/kernel/Makefile b/system/kernel/Makefile new file mode 100644 index 0000000..08b21cd --- /dev/null +++ b/system/kernel/Makefile @@ -0,0 +1,16 @@ + +OBJECTS = pdp_packet.o pdp_type.o pdp_dpd_command.o \ + pdp_list.o pdp_debug.o pdp_symbol.o \ + pdp_mem.o pdp_post.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_debug.c b/system/kernel/pdp_debug.c new file mode 100644 index 0000000..07f6541 --- /dev/null +++ b/system/kernel/pdp_debug.c @@ -0,0 +1,21 @@ +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include "pdp_post.h" + +int pdp_debug_sigtrap_on_assert; + + +void pdp_assert_hook (char *condition, char *file, int line) +{ + pdp_post("PDP_ASSERT (%s) failed in file %s, line %u. ", condition, file, line); + pdp_post("%s.\n", pdp_debug_sigtrap_on_assert ? "sending SIGTRAP" : "continuing"); + + if (pdp_debug_sigtrap_on_assert) kill(getpid(), SIGTRAP); +} + + +void pdp_debug_setup(void) +{ + pdp_debug_sigtrap_on_assert = 1; +} diff --git a/system/kernel/pdp_dpd_command.c b/system/kernel/pdp_dpd_command.c new file mode 100644 index 0000000..d812cf2 --- /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_dpd_command.h" +#include "pdp_mem.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_list.c b/system/kernel/pdp_list.c new file mode 100644 index 0000000..deeb7f1 --- /dev/null +++ b/system/kernel/pdp_list.c @@ -0,0 +1,931 @@ + +/* + * 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? + + this is sorth of a compromise between lists, queues, + stacks, lisp and forth. list contain pdp atoms + (floats, ints, symbols, pointers, packets or lists) */ + + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "pdp_list.h" +#include "pdp_symbol.h" +#include "pdp_packet.h" +#include "pdp_type.h" +#include "pdp_types.h" +#include "pdp_mem.h" +#include "pdp_post.h" +#include "pdp_debug.h" + +#define D if (0) + + +t_pdp_fastalloc *_fast_atom_allocator; +t_pdp_fastalloc *_fast_list_allocator; + + + + +static void _pdp_list_dealloc(t_pdp_list *l) +{ + pdp_fastalloc_save_atom(_fast_list_allocator, l); +} + +// allocator macros +#define PDP_ATOM_ALLOC() pdp_fastalloc_new_atom(_fast_atom_allocator) +#define PDP_ATOM_DEALLOC(x) pdp_fastalloc_save_atom(_fast_atom_allocator, x) +#define PDP_LIST_ALLOC() pdp_fastalloc_new_atom(_fast_list_allocator) +#define PDP_LIST_DEALLOC(x) _pdp_list_dealloc(x) +//#define PDP_LIST_DEALLOC(x) pdp_fastalloc_save_atom(_fast_list_allocator, x) + + + + +/* some private helper methods */ + +/* list pool setup */ +void pdp_list_setup(void) +{ + + + + /* create fast allocators */ + _fast_atom_allocator = pdp_fastalloc_new(sizeof(t_pdp_atom)); + _fast_list_allocator = pdp_fastalloc_new(sizeof(t_pdp_list)); + + /* testing code */ + if (0){ + char *next; + t_pdp_list *l = pdp_tree_from_cstring("( een twee (3 vier ()) vijf (6.0)", &next); + if (!l){ + pdp_post("parse error:"); + pdp_post(next); + } + else{ + pdp_list_print(l); + } + exit(1); + } + + +} + + + +/* 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; + +} + +/* BEGIN PARSER CODE */ + +/* real whitespace handling */ +static inline int _is_whitespace(char c){return (c == ' ' || c == '\n' || c == '\t');} +static inline void _skip_real_whitespace(char **c){while (_is_whitespace(**c)) (*c)++;} + +/* comment handling */ +static inline int _is_left_comment(char c) {return (c == '#');} +static inline int _is_right_comment(char c) {return (c == '\n');} +static inline void _skip_comment(char **c) +{ + if (!_is_left_comment(**c)) return; + (*c)++; + while (!_is_right_comment(**c)){ + if (!**c) return; // no terminating newline + (*c)++; + } + (*c)++; +} + +/* comment + whitespace handling */ +static inline void _skip_whitespace(char **c) +{ + char *prev_c; + /* skip comments and whitespace until the + pointer stops moving */ + do { + prev_c = *c; + _skip_real_whitespace(c); + _skip_comment(c); + } while (prev_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_terminator(char c) {return (c == 0);} + +/* the end of an atom is marked by a separator */ +static inline int _is_separator(char c) {return (_is_terminator(c) + || _is_left_separator(c) + || _is_right_separator(c) + || _is_whitespace(c));} + + +/* parse a single pure atom from a zero terminated string + a pure atom is either a number (int or float) xor a symbol +*/ + +static inline void _parse_pure_atom(t_pdp_atom *a, char *c) +{ + char *next; + + /* check if the string has a decimal point */ + int has_decimal = 0; + char *c2; + for(c2 = c; *c2; c2++){ + if (*c2 == '.') { has_decimal = 1; break; } + } + + /* try parsing as a number (int or float) first */ + if (has_decimal){ // try float + float f = strtod(c, &next); + if (next[0] == 0){ // got everything? + D pdp_post("parsing float %f", f); + a->t = a_float; + a->w = (t_pdp_word)f; + return; + } + } + else { // try int + int i = strtol(c, &next, 0); + if (next[0] == 0){ // got everything? + D pdp_post("parsing int %d", i); + a->t = a_int; + a->w = (t_pdp_word)i; + return; + } + } + + + /* number parsing failed: it's a symbol */ + D pdp_post("parsing symbol %s", c); + a->t = a_symbol; + a->w = (t_pdp_word)pdp_gensym(c); + +} + +t_pdp_atom *pdp_atom_new(void){t_pdp_atom *a = PDP_ATOM_ALLOC(); a->next = 0; return a;} +void pdp_atom_free(t_pdp_atom *a){PDP_ATOM_DEALLOC(a);} + +/* there are two parser methods: parse an atom and parse a list + both can call each other recursively. + the atoms and list are allocated with pdp_list_new and + pdp_atom_new respectively */ + +t_pdp_atom *pdp_atom_from_cstring(char *chardef, char **next) +{ + t_pdp_atom *a = 0; + + /* skip whitespace and check if there's anything left */ + _skip_whitespace(&chardef); + if (!chardef[0] || _is_right_separator(*chardef)) goto done; + + + /* check if it's a list atom */ + if(_is_left_separator(*chardef)){ + t_pdp_list *l = pdp_tree_from_cstring(chardef, &chardef); + if (l){ + a = pdp_atom_new(); + a->t = a_list; + a->w.w_list = l; + } + + } + + /* we have a pure atom, copy it to a temp buffer */ + else{ + int n = 0; + while (!_is_separator(chardef[n])) n++; + if (!n) goto done; + else { + char tmp[n+1]; + strncpy(tmp, chardef, n); + tmp[n] = 0; + a = pdp_atom_new(); + _parse_pure_atom(a, tmp); + chardef += n; + } + + } + + done: + if (next) *next = chardef; + return a; + +} + +/* check if a tree (list of lists) matches a certain type syntax + types: + + symbol -> a_sym; + int -> a_int; + float -> a_float; + packet -> a_packet; + list -> a_list; + ... -> zero or more times the preceeding elements in the list +*/ + + + +/* create a list from a character string */ +t_pdp_list *pdp_tree_from_cstring(char *chardef, char **next) +{ + t_pdp_list *l = pdp_list_new(0); + t_pdp_atom *a = 0; + + D pdp_post ("creating list from char: %s", chardef); + + /* find opening parenthesis and skip it*/ + _skip_whitespace(&chardef); + if (!_is_left_separator(*chardef)) goto error; else chardef++; + + /* chardef now points at the first atom, start adding atoms */ + while(1){ + a = pdp_atom_from_cstring(chardef, &chardef); + if (a)pdp_list_add_back_atom(l, a); + else break; + } + + /* skip whitespace and find closing parenthesis */ + _skip_whitespace(&chardef); + if (!_is_right_separator(*chardef)) goto error; else chardef++; + if (next) *next = chardef; + return l; + + error: + /* end of string encountered: parse error */ + D pdp_post("parse error: %s", chardef); + if (next) *next = chardef; + pdp_tree_free(l); //this will free all sublists too + return 0; // parse error + + + +} + +/* END PARSER CODE */ + +// this assumes syntax's syntax is correct +int pdp_tree_check_syntax(t_pdp_list *list, t_pdp_list *syntax) +{ + + t_pdp_atom *la = 0; + t_pdp_atom *sa = 0; + + t_pdp_symbol *ellipsis = pdp_gensym("..."); + t_pdp_symbol *wildcard = pdp_gensym("*"); + + /* handle empty lists */ + if (list->elements == 0){ + + /* check if syntax list is empty */ + if (syntax->elements == 0) goto match; + + /* check if syntax list has ellipsis */ + if (syntax->last->t == a_symbol && + syntax->last->w.w_symbol == ellipsis) goto match; + + /* default: no match */ + goto nomatch; + } + + + /* loop over list and syntax list */ + for (la = list->first, sa = syntax->first; + la && sa; + la = la->next, sa = sa->next){ + + D pdp_post("pdp_tree_check_syntax: starting check"); + + checkatom: + /* what do we expect for this atom ? */ + switch(sa->t){ + case a_list: + D pdp_post("expecting list"); + /* we need to recurse down the tree */ + /* exit if the current list to check + does not have a sublist */ + if (la->t != a_list) { + D pdp_post("not a list"); + goto nomatch; + } + + /* recurse and exit if no match */ + D pdp_post("checking sublist"); + if (!pdp_tree_check_syntax(la->w.w_list, sa->w.w_list)){ + D pdp_post("sublist does not match"); + goto nomatch; + } + + break; + + case a_symbol: + + /* if ellipsis, rewind */ + if (ellipsis == sa->w.w_symbol){ + D pdp_post("got ellipsis"); + /* check if we're not looping */ + if (sa == syntax->first){ + D pdp_post("ellipsis at start of list"); + goto nomatch; + } + /* try again */ + sa = syntax->first; + D pdp_post("ellipsis rewind"); + goto checkatom; + } + + else if (wildcard == sa->w.w_symbol){ + D pdp_post("got wildcard"); + } + + /* ordinary atom: check type */ + else{ + D pdp_post("expecting %s", sa->w.w_symbol->s_name); + switch(la->t){ + + case a_int: + if (sa->w.w_symbol != pdp_gensym("int")) goto nomatch; break; + case a_float: + if (sa->w.w_symbol != pdp_gensym("float")) goto nomatch; break; + case a_symbol: + if (sa->w.w_symbol != pdp_gensym("symbol")) goto nomatch; break; + case a_packet: + if (sa->w.w_symbol != pdp_gensym("packet")) goto nomatch; break; + case a_list: + if (sa->w.w_symbol != pdp_gensym("list")) goto nomatch; break; + + default: + goto nomatch; + } + D pdp_post("OK"); + } + + break; + + default: + D pdp_post("syntax syntax error"); + pdp_list_print(syntax); + goto nomatch; // incorrect syntax description + } + + } + + /* loop ended because one of the lists was finished */ + /* only two cases can be valid: la == 0 and (sa == 0 or ellipsis) */ + + if (la != 0){ + D pdp_post("not end of list -> no match"); + goto nomatch; + } + + if (sa == 0) goto match; + + if (!(sa->t == a_symbol && sa->w.w_symbol == ellipsis)){ + D pdp_post("syntax list not in ellipsis position -> no match"); + goto nomatch; + } + + + /* exits */ + match: + D pdp_post("pdp_tree_check_syntax: match"); + return 1; + nomatch: + D pdp_post("pdp_tree_check_syntax: no match"); + return 0; + +} + + + +/* 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->w.w_packet = -1; + } +} + +static void _atom_packet_copy_ro(t_pdp_atom *a) +{ + int p; + if (a->t == a_packet){ + a->w.w_packet = pdp_packet_copy_ro(a->w.w_packet); + } +} + +void pdp_tree_strip_packets (t_pdp_list *l) +{ + pdp_tree_apply(l, _atom_packet_mark_unused); +} + +static void _pdp_tree_copy_ro_packets (t_pdp_list *l) +{ + pdp_tree_apply(l, _atom_packet_copy_ro); +} + +t_pdp_list *pdp_tree_copy_ro(t_pdp_list *l) +{ + t_pdp_list *l2 = pdp_tree_copy(l); + _pdp_tree_copy_ro_packets(l2); + return l2; +} + +static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a); + +static void _pdp_atom_fprint(FILE* f, t_pdp_atom *a) +{ + if (!a){ + fprintf(f, "<NULL ATOM>"); + return; + } + + switch(a->t){ + /* generic atoms */ + 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>", a->w.w_int); break; + case a_list: _pdp_atomlist_fprint(f, a->w.w_list->first); break; + case a_atom_pointer: + fprintf(f, "->"); + _pdp_atom_fprint(f, a->w.w_atom_pointer); + break; + case a_undef: fprintf(f, "<undef>"); break; + + /* forth atoms */ + case a_forthword: fprintf(f, "#<forth word 0x%08x>", a->w.w_int); break; + case a_vmword: fprintf(f, "#<vm word 0x%08x>", a->w.w_int); break; + case a_vmmacro: fprintf(f, "#<vm macro 0x%08x>", a->w.w_int); break; + + + default: fprintf(f, "<unknown type>"); break; + } +} + +/* debug */ +static void _pdp_atomlist_fprint(FILE* f, t_pdp_atom *a) +{ + fprintf(f, "("); + while (a){ + _pdp_atom_fprint(f,a); + 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); +} + +void pdp_atom_print(t_pdp_atom *a) +{ + _pdp_atom_fprint(stderr, a); +} + +/* public list operations */ + + + + +/* add a atom/word to the start of the list */ +void pdp_list_add_atom(t_pdp_list *l, t_pdp_atom *a) +{ + a->next = l->first; + l->first = a; + l->elements++; + if (!l->last) l->last = a; +} + +void pdp_list_add(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *a = PDP_ATOM_ALLOC(); + a->t = t; + a->w = w; + pdp_list_add_atom(l, a); +} + + +/* add a word to the end of the list */ +void pdp_list_add_back_atom(t_pdp_list *l, t_pdp_atom *a) +{ + + l->elements++; + a->next = 0; + if (l->last){ + l->last->next = a; + } + else{ + l->first = a; + } + l->last = a; +} + +void pdp_list_add_back(t_pdp_list *l, t_pdp_word_type t, t_pdp_word w) +{ + t_pdp_atom *a = PDP_ATOM_ALLOC(); + a->w = w; + a->t = t; + pdp_list_add_back_atom(l, a); +} + +/* get list size */ +int pdp_list_size(t_pdp_list *l) +{ + return l->elements; +} + + + + +/* pop: return first item and remove */ +t_pdp_atom *pdp_list_pop_atom(t_pdp_list *l) +{ + t_pdp_atom *a = l->first; + if (!a) return a; + + l->first = a->next; + l->elements--; + if (!l->first) l->last = 0; + a->next = 0; // detach + return a; +} + + +/* pop: return first item and remove */ +t_pdp_word pdp_list_pop(t_pdp_list *l) +{ + t_pdp_atom *a = pdp_list_pop_atom(l); + t_pdp_word w=a->w; + PDP_ATOM_DEALLOC(a); + return w; +} + + + + +/* pop from one list and push to other */ +void pdp_list_pop_push(t_pdp_list *source, t_pdp_list *dest) +{ + t_pdp_atom *a = source->first; + + /* pop atom */ + if (--(source->elements)){source->last = 0;} + source->first = a->next; + + /* push atom */ + a->next = dest->first; + if (dest->elements++) {dest->last = a;} + dest->first = a; + + return; + +} + + +/* 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_join (t_pdp_list *l, t_pdp_list *tail) +{ + if (tail->elements){ + l->elements += tail->elements; + if (l->last){ + l->last->next = tail->first; + l->last = tail->last; + } + else { + l->first = tail->first; + l->last = tail->last; + } + } + PDP_LIST_DEALLOC(tail); //delete the tail header +} + +void pdp_list_cat (t_pdp_list *l, t_pdp_list *tail) +{ + t_pdp_list *tmp = pdp_list_copy(tail); + pdp_list_join(l, tmp); +} + + +/* in place reverse: atoms stay the same + they are just relinked. so pointers will stay accurate */ +void pdp_list_reverse(t_pdp_list *l) +{ + t_pdp_list tmp; + t_pdp_atom *a; + tmp.first = l->first; + tmp.last = l->last; + tmp.elements = l->elements; + l->first = 0; + l->last = 0; + l->elements = 0; + while (a = pdp_list_pop_atom(&tmp)){ + pdp_list_add_atom(l, a); + } +} + +void pdp_list_reverse_old(t_pdp_list *l) +{ + t_pdp_list *l2 = pdp_list_copy_reverse(l); + pdp_list_clear(l); + l->first = l2->first; + l->last = l2->last; + l->elements = l2->elements; + _pdp_list_dealloc(l2); +} + +/* 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_mem.c b/system/kernel/pdp_mem.c new file mode 100644 index 0000000..33822ef --- /dev/null +++ b/system/kernel/pdp_mem.c @@ -0,0 +1,129 @@ +/* + * Pure Data Packet system file: memory allocation + * 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 "pdp_mem.h" +#include "pdp_debug.h" + + +/* malloc wrapper that calls garbage collector */ +void *pdp_alloc(int size) +{ + void *ptr = malloc(size); + + PDP_ASSERT(ptr); + + return ptr; + + //TODO: REPAIR THIS + //post ("malloc failed in a pdp module: running garbage collector."); + //pdp_pool_collect_garbage(); + //return malloc(size); +} + + +void pdp_dealloc(void *stuff) +{ + free (stuff); +} + + +/* fast atom allocation object + well, this is not too fast yet, but will be later + when it suports linux futexes or atomic operations */ + +//#include <pthread.h> + +/* private linked list struct */ +typedef struct _fastalloc +{ + struct _fastalloc * next; +} t_fastalloc; + + + + +static void _pdp_fastalloc_lock(t_pdp_fastalloc *x){pthread_mutex_lock(&x->mut);} +static void _pdp_fastalloc_unlock(t_pdp_fastalloc *x){pthread_mutex_unlock(&x->mut);} + +static void _pdp_fastalloc_refill_freelist(t_pdp_fastalloc *x) +{ + t_fastalloc *atom; + unsigned int i; + + PDP_ASSERT(x->freelist == 0); + + /* get a new block + there is no means of freeing the data afterwards, + this is a fast implementation with the tradeoff of data + fragmentation "memory leaks".. */ + + x->freelist = pdp_alloc(x->block_elements * x->atom_size); + + /* link all atoms together */ + atom = x->freelist; + for (i=0; i<x->block_elements-1; i++){ + atom->next = (t_fastalloc *)(((char *)atom) + x->atom_size); + atom = atom->next; + } + atom->next = 0; + +} + +void *pdp_fastalloc_new_atom(t_pdp_fastalloc *x) +{ + t_fastalloc *atom; + + _pdp_fastalloc_lock(x); + + /* get an atom from the freelist + or refill it and try again */ + while (!(atom = x->freelist)){ + _pdp_fastalloc_refill_freelist(x); + } + + /* delete the element from the freelist */ + x->freelist = x->freelist->next; + atom->next = 0; + + _pdp_fastalloc_unlock(x); + + return (void *)atom; + +} +void pdp_fastalloc_save_atom(t_pdp_fastalloc *x, void *atom) +{ + _pdp_fastalloc_lock(x); + ((t_fastalloc *)atom)->next = x->freelist; + x->freelist = (t_fastalloc *)atom; + _pdp_fastalloc_unlock(x); +} + +t_pdp_fastalloc *pdp_fastalloc_new(unsigned int size) +{ + t_pdp_fastalloc *x = pdp_alloc(sizeof(*x)); + if (size < sizeof(t_fastalloc)) size = sizeof(t_fastalloc); + x->freelist = 0; + x->atom_size = size; + x->block_elements = PDP_FASTALLOC_BLOCK_ELEMENTS; + pthread_mutex_init(&x->mut, NULL); + return x; +} + diff --git a/system/kernel/pdp_packet.c b/system/kernel/pdp_packet.c new file mode 100644 index 0000000..0eb569a --- /dev/null +++ b/system/kernel/pdp_packet.c @@ -0,0 +1,634 @@ +/* + * 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 <stdio.h> +#include <pthread.h> +#include <unistd.h> +#include <string.h> +#include "pdp_post.h" +#include "pdp_packet.h" +#include "pdp_mem.h" +#include "pdp_list.h" +#include "pdp_type.h" +#include "pdp_debug.h" + + +/* packet implementation. contains class and packet (instance) handling + + some notes on packet operations. + copy ro/rw and unregister are relatively straightforward + packet creation can be done in 2 ways in this interface: + create + reuse + however, these methods should only be called by specific factory + methods, so the user should only create packets using pdp_factory_newpacket + + reuse or create is thus the responsability of the factory methods for + each packet type (class) implementation + + +*/ + + +/* NOTE: + the packet pool methods are called within the pool locks. this probably + needs to change, because it will cause deadlocks for container packets (fobs) */ + + +/* new implementation: probably just a minor adjustment: add the reuse fifo attached + to type desc symbol name + need to check and possibly eliminate hacks for non-pure packets + + pdp_packet_new: + LOCK + 1. check reuse fifo + 2. empty -> create packet+return (search array) + 3. element -> check if type is correct, yes->pop+return, no->goto 1. + UNLOCK + 4. wakeup + + pdp_packet_mark_unused + + 1. check refcount. if > 1 dec + exit + 2. if 1 put packet to sleep + 3. dec refcount + 4. add to reuse fifo (no fifo -> create) + + pdp_packet_delete: analogous to mark_unused + pdp_packet_copy_ro/rw: analogous to new + +*/ + + +/* the pool */ +#define PDP_INITIAL_POOL_SIZE 64 +static int pdp_pool_size; +static t_pdp** pdp_pool; + +/* mutex: protects the pool and reuse lists attached to symbols */ +static pthread_mutex_t pdp_pool_mutex; +#define LOCK pthread_mutex_lock (&pdp_pool_mutex) +#define UNLOCK pthread_mutex_unlock (&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); + pdp_post("debug info for packet %d", packet); + if (!h){ + pdp_post("invalid packet"); + } + else{ + pdp_post ("\ttype: %d", h->type); + pdp_post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown"); + pdp_post ("\tsize: %d", h->size); + pdp_post ("\tflags: %x", h->flags); + pdp_post ("\tusers: %d", h->users); + pdp_post ("\tclass: %x", h->theclass); + } +} + + + +/* setup methods */ + +void +pdp_packet_setup(void) +{ + + pdp_pool_size = PDP_INITIAL_POOL_SIZE; + pdp_pool = (t_pdp **)pdp_alloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *)); + bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *)); + 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 + pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c)); + return c; +} + +/* the packet factory */ +int pdp_factory_newpacket(t_pdp_symbol *type) +{ + int p; + t_pdp_class *c; + t_pdp_atom *a = class_list->first; + + /* try to reuse first + THINK: should this be the responsability of the type specific constructors, + or should a packet allways be reusable (solution: depends on what the cleanup method returns??) + */ + p = pdp_packet_reuse(type); + if (-1 != p) return p; + + + /* call class constructor */ + while(a){ + c = (t_pdp_class *)(a->w.w_pointer); + if (c->type && pdp_type_description_match(type, c->type)){ + //pdp_post("method %x, type %s", c->create, type->s_name); + return (c->create) ? (*c->create)(type) : -1; + } + a = a->next; + } + return -1; +} + +static void +_pdp_pool_expand_nolock(void){ + int i; + + /* double the size */ + int new_pool_size = pdp_pool_size << 1; + t_pdp **new_pool = (t_pdp **)pdp_alloc(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 *)); + pdp_dealloc(pdp_pool); + pdp_pool = new_pool; + pdp_pool_size = new_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_nolock(t_pdp *p) +{ + /* free memory */ + pdp_dealloc (p); +} + +static t_pdp* +_pdp_packet_alloc_nolock(unsigned int datatype, unsigned int datasize) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + t_pdp *p = (t_pdp *)pdp_alloc(totalsize); + if (p){ + memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0 + p->type = datatype; + p->size = totalsize; + p->users = 1; + } + return p; +} + + +/* create a new packet and expand pool if necessary */ +static int +_pdp_packet_create_nolock(unsigned int datatype, unsigned int datasize) +{ + int p = 0; + while(1){ + for (; p < pdp_pool_size; p++){ + if (!pdp_pool[p]){ + /* found slot to store packet*/ + t_pdp *header = _pdp_packet_alloc_nolock(datatype, datasize); + if (!header) return -1; // error allocating packet + pdp_pool[p] = header; + return p; + } + } + /* no slot found, expand pool */ + _pdp_pool_expand_nolock(); + } +} + + +void +pdp_packet_destroy(void) +{ + int i = 0; + /* dealloc all the data in object stack */ + pdp_post("DEBUG: pdp_packet_destroy: clearing object pool."); + while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc_nolock(pdp_pool[i++]); +} + + + + + + + + +/* public pool operations: have to be thread safe so each entry point + locks the mutex */ + + +/* create a new packet. + this should only be used by type specific factory methods, and only if the + reuse method fails, since it will always create a new packet */ +int +pdp_packet_create(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int packet; + LOCK; + packet = _pdp_packet_create_nolock(datatype, datasize); + UNLOCK; + return packet; +} + + +/* return a new packet. + it tries to reuse a packet based on + 1. matching data size + 2. abscence of destructor (which SHOULD mean there are no enclosed references) + + it obviously can't use the reuse fifo tagged to a symbolic type description + + ALWAYS USE pdp_packet_reuse BEFORE calling pdp_packet_new if possible + use both ONLY IN CONSTRUCTORS !!! + + use pdp_packet_factory to create packets as a "user" + + this is a summary of all internal packet creation mechanisms: + + -> pdp_packet_reuse, which uses symbolic type descriptions, and should work for all packet types + it returns an initialized container (meta = correct, data = garbage) + + -> pdp_packet_new, which only works for non-pure packets, and reuses packets based on data type + it returns a pure packet (meta + data = garbage) + + -> pdp_packet_create, like pdp_packet_new, only it always creates a new packet + + + +*/ + +int +pdp_packet_new(unsigned int datatype, unsigned int datasize) +{ + t_pdp *header; + int packet; + LOCK; + for (packet = 0; packet < pdp_pool_size; packet++){ + header = pdp_pool[packet]; + /* check data size */ + if (header + && header->users == 0 // must be unused + && header->size == datasize + PDP_HEADER_SIZE // must be same size + && !(header->theclass && header->theclass->cleanup)){ // must be pure packet (no destructor) + + /* ok, got one. initialize */ + memset(header, 0, PDP_HEADER_SIZE); + header->users = 1; + header->type = datatype; + header->size = datasize + PDP_HEADER_SIZE; + + UNLOCK; //EXIT1 + return packet; + } + } + + /* no usable non-pure packet found, create a new one */ + + UNLOCK; //EXIT2 + return pdp_packet_create(datatype, datasize); + + + +} + + +/* internal method to add a packet to a packet type + description symbol's unused packet fifo */ +void +_pdp_packet_save_nolock(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_pdp_symbol *s; + PDP_ASSERT(header); + PDP_ASSERT(header->users == 0); + PDP_ASSERT(header->desc); + s = header->desc; + if (!s->s_reusefifo) s->s_reusefifo = pdp_list_new(0); + pdp_list_add(s->s_reusefifo, a_packet, (t_pdp_word)packet); +} + +/* this will revive a packet matching a certain type description + no wildcards are allowed */ +int +pdp_packet_reuse(t_pdp_symbol *type_description) +{ + int packet = -1; + t_pdp *header = 0; + t_pdp_list *l = 0; + LOCK; + if (!type_description || !(l = type_description->s_reusefifo)) goto exit; + while(l->elements){ + packet = pdp_list_pop(l).w_packet; + header = pdp_packet_header(packet); + + /* check if reuse fifo is consistent (packet unused + correct type) + packet could be deleted and replaced with another one, or + revived without the index updated (it's a "hint cache") */ + + if (header->users == 0){ + /* check if type matches */ + if (pdp_type_description_match(header->desc, type_description)){ + header->users++; // revive + goto exit; + } + /* if not, add the packet to the correct reuse fifo */ + else{ + _pdp_packet_save_nolock(packet); + } + } + + /* remove dangling refs */ + header = 0; + packet = -1; + } + + exit: + UNLOCK; + if (header && header->theclass && header->theclass->wakeup){ + header->theclass->wakeup(header); // revive if necessary + } + return packet; +} + +/* find all unused packets in pool, marked as used (to protect from other reapers) + and return them as a list. non-pure packets are not revived */ + + + + + +/* this returns a copy of a packet for read only access. + (increases refcount of the packet -> packet will become readonly if it was + writable, i.e. had rc=1 */ + +int +pdp_packet_copy_ro(int handle) +{ + t_pdp* header; + + if (header = pdp_packet_header(handle)){ + PDP_ASSERT(header->users); // consistency check + LOCK; + header->users++; // increment reference count + UNLOCK; + } + else handle = -1; + return handle; +} + +/* clone a packet: create a new packet with the same + type as the source packet */ + +int +pdp_packet_clone_rw(int handle) +{ + t_pdp* header; + int new_handle = -1; + + + if (header = pdp_packet_header(handle)){ + /* consistency checks */ + PDP_ASSERT(header->users); + PDP_ASSERT(header->desc); + + /* first try to reuse old packet */ + new_handle = pdp_packet_reuse(header->desc); + + /* if this failed, create a new one using the central packet factory method */ + if (-1 == new_handle) new_handle = pdp_factory_newpacket(header->desc); + + /* if the factory method failed cline it manually */ + if (-1 == new_handle) { + t_pdp *new_header; + //pdp_post("WARNING: pdp_clone_rw: working around non-implemented factory method."); + new_handle = pdp_packet_new(header->type, header->size - PDP_HEADER_SIZE); + new_header = pdp_packet_header(new_handle); + if (new_header){ + memcpy(new_header, header, PDP_HEADER_SIZE); + } + } + } + + return new_handle; +} + +/* return a copy of a packet (clone + copy data) */ +int +pdp_packet_copy_rw(int handle) +{ + t_pdp *header, *new_header; + int new_handle = -1; + + if (!(header = pdp_packet_header(handle))) return -1; + + /* check if we are allowed to copy */ + if (header->flags & PDP_FLAG_DONOTCOPY) return -1; + + /* get target packet */ + new_handle = pdp_packet_clone_rw(handle); + if (-1 == new_handle) return -1; + new_header = pdp_packet_header(new_handle); + + /* if there is a copy method, use that one */ + if (header->theclass && header->theclass->copy){ + header->theclass->copy(header, new_header); + } + + /* otherwize copy the data verbatim */ + else { + memcpy(pdp_packet_data(new_handle), + pdp_packet_data(handle), + pdp_packet_data_size(handle)); + } + + return new_handle; + +} + + +/* decrement refcount */ +void pdp_packet_mark_unused(int handle) +{ + t_pdp *header; + if (!(header = pdp_packet_header(handle))) return; + + PDP_ASSERT(header->users); // consistency check + + LOCK; + + /* just decrement refcount */ + if (header->users > 1){ + header->users--; + } + + /* put packet to sleep if refcount 1->0 */ + else { + if (header->theclass && header->theclass->sleep){ + /* call sleep method (if any) outside of lock + while the packet is still alive, so it won't be + acclaimed by another thread */ + UNLOCK; + header->theclass->sleep(header); + LOCK; + } + /* clear refcount & save in fifo for later use */ + header->users = 0; + if (header->desc) // sleep could have destructed packet.. + _pdp_packet_save_nolock(handle); + } + + UNLOCK; +} + + + +/* delete a packet. rc needs to be == 1 */ +void pdp_packet_delete(int handle) +{ + t_pdp *header; + header = pdp_packet_header(handle); + PDP_ASSERT(header); + PDP_ASSERT(header->users == 1); // consistency check + + LOCK; + + if (header->theclass && header->theclass->cleanup){ + /* call cleanup method (if any) outside of lock + while the packet is still alive, so it won't be + acclaimed by another thread */ + UNLOCK; + header->theclass->cleanup(header); + LOCK; + } + + /* delete the packet */ + pdp_pool[handle] = 0; + _pdp_packet_dealloc_nolock(header); + + + UNLOCK; +} + + + + + + + +/* 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; +} + +int +pdp_packet_data_size(int handle) +{ + t_pdp *h; + if ((handle >= 0) && (handle < pdp_pool_size)) + { + h = pdp_pool[handle]; + if (!h) return 0; + return h->size - PDP_HEADER_SIZE; + } + else return 0; +} + + + + +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) +{ + pdp_post("ERROR: garbage collector not implemented"); + return 0; +} + +void +pdp_pool_set_max_mem_usage(int max) +{ + pdp_post("ERROR: mem limit not implemented"); +} + + + + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_packet2.c b/system/kernel/pdp_packet2.c new file mode 100644 index 0000000..3717a77 --- /dev/null +++ b/system/kernel/pdp_packet2.c @@ -0,0 +1,623 @@ +/* + * 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 <stdio.h> +#include <pthread.h> +#include <unistd.h> +#include <string.h> +#include "pdp_post.h" +#include "pdp_packet.h" +#include "pdp_mem.h" +#include "pdp_list.h" +#include "pdp_type.h" +#include "pdp_debug.h" + + +/* packet implementation. contains class and packet (instance) handling + + some notes on packet operations. + copy ro/rw and unregister are relatively straightforward + packet creation can be done in 2 ways in this interface: + create + reuse + however, these methods should only be called by specific factory + methods, so the user should only create packets using pdp_factory_newpacket + + reuse or create is thus the responsability of the factory methods for + each packet type (class) implementation + + +*/ + + +/* NOTE: + the packet pool methods are called within the pool locks. this probably + needs to change, because it will cause deadlocks for container packets (fobs) */ + + +/* new implementation: probably just a minor adjustment: add the reuse fifo attached + to type desc symbol name + need to check and possibly eliminate hacks for non-pure packets + + pdp_packet_new: + LOCK + 1. check reuse fifo + 2. empty -> create packet+return (search array) + 3. element -> check if type is correct, yes->pop+return, no->goto 1. + UNLOCK + 4. wakeup + + pdp_packet_mark_unused + + 1. check refcount. if > 1 dec + exit + 2. if 1 put packet to sleep + 3. dec refcount + 4. add to reuse fifo (no fifo -> create) + + pdp_packet_delete: analogous to mark_unused + pdp_packet_copy_ro/rw: analogous to new + +*/ + + +/* the pool */ +#define PDP_INITIAL_POOL_SIZE 64 +static int pdp_pool_size; +static t_pdp** pdp_pool; + +/* mutex: protects the pool and reuse lists attached to symbols */ +static pthread_mutex_t pdp_pool_mutex; +#define LOCK pthread_mutex_lock (&pdp_pool_mutex) +#define UNLOCK pthread_mutex_unlock (&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); + pdp_post("debug info for packet %d", packet); + if (!h){ + pdp_post("invalid packet"); + } + else{ + pdp_post ("\ttype: %d", h->type); + pdp_post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown"); + pdp_post ("\tsize: %d", h->size); + pdp_post ("\tflags: %x", h->flags); + pdp_post ("\tusers: %d", h->users); + pdp_post ("\tclass: %x", h->theclass); + } +} + + + +/* setup methods */ + +void +pdp_packet_setup(void) +{ + + pdp_pool_size = PDP_INITIAL_POOL_SIZE; + pdp_pool = (t_pdp **)pdp_alloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *)); + bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *)); + 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 + pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c)); + return c; +} + +/* the packet factory */ +int pdp_factory_newpacket(t_pdp_symbol *type) +{ + int p; + t_pdp_class *c; + t_pdp_atom *a = class_list->first; + + /* try to reuse first + THINK: should this be the responsability of the type specific constructors, + or should a packet allways be reusable (solution: depends on what the cleanup method returns??) + */ + p = pdp_packet_reuse(type); + if (-1 != p) return p; + + + /* call class constructor */ + while(a){ + c = (t_pdp_class *)(a->w.w_pointer); + if (c->type && pdp_type_description_match(type, c->type)){ + //pdp_post("method %x, type %s", c->create, type->s_name); + return (c->create) ? (*c->create)(type) : -1; + } + a = a->next; + } + return -1; +} + +static void +_pdp_pool_expand_nolock(void){ + int i; + + /* double the size */ + int new_pool_size = pdp_pool_size << 1; + t_pdp **new_pool = (t_pdp **)pdp_alloc(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 *)); + pdp_dealloc(pdp_pool); + pdp_pool = new_pool; + pdp_pool_size = new_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_nolock(t_pdp *p) +{ + /* free memory */ + pdp_dealloc (p); +} + +static t_pdp* +_pdp_packet_alloc_nolock(unsigned int datatype, unsigned int datasize) +{ + unsigned int totalsize = datasize + PDP_HEADER_SIZE; + t_pdp *p = (t_pdp *)pdp_alloc(totalsize); + if (p){ + memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0 + p->type = datatype; + p->size = totalsize; + p->users = 1; + } + return p; +} + + +/* create a new packet and expand pool if necessary */ +static int +_pdp_packet_create_nolock(unsigned int datatype, unsigned int datasize) +{ + int p = 0; + while(1){ + for (; p < pdp_pool_size; p++){ + if (!pdp_pool[p]){ + /* found slot to store packet*/ + t_pdp *header = _pdp_packet_alloc_nolock(datatype, datasize); + if (!header) return -1; // error allocating packet + pdp_pool[p] = header; + return p; + } + } + /* no slot found, expand pool */ + _pdp_pool_expand_nolock(); + } +} + + +void +pdp_packet_destroy(void) +{ + int i = 0; + /* dealloc all the data in object stack */ + pdp_post("DEBUG: pdp_packet_destroy: clearing object pool."); + while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc_nolock(pdp_pool[i++]); +} + + + + + + + + +/* public pool operations: have to be thread safe so each entry point + locks the mutex */ + + +/* create a new packet. + this should only be used by type specific factory methods, and only if the + reuse method fails, since it will always create a new packet */ +int +pdp_packet_create(unsigned int datatype, unsigned int datasize /*without header*/) +{ + int packet; + LOCK; + packet = _pdp_packet_create_nolock(datatype, datasize); + UNLOCK; + return packet; +} + + +/* return a new packet. + it tries to reuse a packet based on + 1. matching data size + 2. abscence of destructor (which SHOULD mean there are no enclosed references) + + it obviously can't use the reuse fifo tagged to a symbolic type description + + ALWAYS USE pdp_packet_reuse BEFORE calling pdp_packet_new if possible + use both ONLY IN CONSTRUCTORS !!! + + use pdp_packet_factory to create packets as a "user" + + this is a summary of all internal packet creation mechanisms: + + -> pdp_packet_reuse, which uses symbolic type descriptions, and should work for all packet types + it returns an initialized container (meta = correct, data = garbage) + + -> pdp_packet_new, which only works for non-pure packets, and reuses packets based on data type + it returns a pure packet (meta + data = garbage) + + -> pdp_packet_create, like pdp_packet_new, only it always creates a new packet + + + +*/ + +int +pdp_packet_new(unsigned int datatype, unsigned int datasize) +{ + t_pdp *header; + int packet; + LOCK; + for (packet = 0; packet < pdp_pool_size; packet++){ + header = pdp_pool[packet]; + /* check data size */ + if (header + && header->users == 0 + && header->size == datasize + PDP_HEADER_SIZE + && !(header->theclass && header->theclass->cleanup)){ + + /* ok, got one. initialize */ + memset(header, 0, PDP_HEADER_SIZE); + header->users = 1; + header->type = datatype; + header->size = datasize + PDP_HEADER_SIZE; + + UNLOCK; //EXIT1 + return packet; + } + } + + /* no usable non-pure packet found, create a new one */ + + UNLOCK; //EXIT2 + return pdp_packet_create(datatype, datasize); + + + +} + + +/* internal method to add a packet to a packet type + description symbol's unused packet fifo */ +void +_pdp_packet_save_nolock(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_pdp_symbol *s; + PDP_ASSERT(header); + PDP_ASSERT(header->users == 0); + PDP_ASSERT(header->desc); + s = header->desc; + if (!s->s_reusefifo) s->s_reusefifo = pdp_list_new(0); + pdp_list_add(s->s_reusefifo, a_packet, (t_pdp_word)packet); +} + +/* this will revive a packet matching a certain type description + no wildcards are allowed */ +int +pdp_packet_reuse(t_pdp_symbol *type_description) +{ + int packet = -1; + t_pdp *header = 0; + t_pdp_list *l = 0; + LOCK; + if (!type_description || !(l = type_description->s_reusefifo)) goto exit; + while(l->elements){ + packet = pdp_list_pop(l).w_packet; + header = pdp_packet_header(packet); + + /* check if reuse fifo is consistent (packet unused + correct type) + packet could be deleted and replaced with another one, or + revived without the index updated (it's a "hint cache") */ + + if (header->users == 0){ + /* check if type matches */ + if (pdp_type_description_match(header->desc, type_description)){ + header->users++; // revive + goto exit; + } + /* if not, add the packet to the correct reuse fifo */ + else{ + _pdp_packet_save_nolock(packet); + } + } + + /* remove dangling refs */ + header = 0; + packet = -1; + } + + exit: + UNLOCK; + if (header && header->theclass && header->theclass->wakeup){ + header->theclass->wakeup(header); // revive if necessary + } + return packet; +} + +/* find all unused packets in pool, marked as used (to protect from other reapers) + and return them as a list. non-pure packets are not revived */ + + + + + +/* this returns a copy of a packet for read only access. + (increases refcount of the packet -> packet will become readonly if it was + writable, i.e. had rc=1 */ + +int +pdp_packet_copy_ro(int handle) +{ + t_pdp* header; + + if (header = pdp_packet_header(handle)){ + PDP_ASSERT(header->users); // consistency check + LOCK; + header->users++; // increment reference count + UNLOCK; + } + else handle = -1; + return handle; +} + +/* clone a packet: create a new packet with the same + type as the source packet */ + +int +pdp_packet_clone_rw(int handle) +{ + t_pdp* header; + int new_handle = -1; + + + if (header = pdp_packet_header(handle)){ + /* consistency checks */ + PDP_ASSERT(header->users); + PDP_ASSERT(header->desc); + + /* first try to reuse old packet */ + new_handle = pdp_packet_reuse(header->desc); + + /* if this failed, create a new one using the central packet factory method */ + if (-1 == new_handle) new_handle = pdp_factory_newpacket(header->desc); + } + + return new_handle; +} + +/* return a copy of a packet (clone + copy data) */ +int +pdp_packet_copy_rw(int handle) +{ + t_pdp *header, *new_header; + int new_handle = -1; + + if (!(header = pdp_packet_header(handle))) return -1; + + /* check if we are allowed to copy */ + if (header->flags & PDP_FLAG_DONOTCOPY) return -1; + + /* get target packet */ + new_handle = pdp_packet_clone_rw(handle); + if (-1 == new_handle) return -1; + new_header = pdp_packet_header(new_handle); + + /* if there is a copy method, use that one */ + if (header->theclass && header->theclass->copy){ + header->theclass->copy(header, new_header); + } + + /* otherwize copy the data verbatim */ + else { + memcpy(pdp_packet_data(new_handle), + pdp_packet_data(handle), + pdp_packet_data_size(handle)); + } + + return new_handle; + +} + + +/* decrement refcount */ +void pdp_packet_mark_unused(int handle) +{ + t_pdp *header; + if (!(header = pdp_packet_header(handle))) return; + + PDP_ASSERT(header->users); // consistency check + + LOCK; + + /* just decrement refcount */ + if (header->users > 1){ + header->users--; + } + + /* put packet to sleep if refcount 1->0 */ + else { + if (header->theclass && header->theclass->sleep){ + /* call sleep method (if any) outside of lock + while the packet is still alive, so it won't be + acclaimed by another thread */ + UNLOCK; + header->theclass->sleep(header); + LOCK; + } + /* clear refcount & save in fifo for later use */ + header->users = 0; + if (header->desc) // sleep could have destructed packet.. + _pdp_packet_save_nolock(handle); + } + + UNLOCK; +} + + + +/* delete a packet. rc needs to be == 1 */ +void pdp_packet_delete(int handle) +{ + t_pdp *header; + header = pdp_packet_header(handle); + PDP_ASSERT(header); + PDP_ASSERT(header->users == 1); // consistency check + + LOCK; + + if (header->theclass && header->theclass->cleanup){ + /* call cleanup method (if any) outside of lock + while the packet is still alive, so it won't be + acclaimed by another thread */ + UNLOCK; + header->theclass->cleanup(header); + LOCK; + } + + /* delete the packet */ + pdp_pool[handle] = 0; + _pdp_packet_dealloc_nolock(header); + + + UNLOCK; +} + + + + + + + +/* 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; +} + +int +pdp_packet_data_size(int handle) +{ + t_pdp *h; + if ((handle >= 0) && (handle < pdp_pool_size)) + { + h = pdp_pool[handle]; + if (!h) return 0; + return h->size - PDP_HEADER_SIZE; + } + else return 0; +} + + + + +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) +{ + pdp_post("ERROR: garbage collector not implemented"); + return 0; +} + +void +pdp_pool_set_max_mem_usage(int max) +{ + pdp_post("ERROR: mem limit not implemented"); +} + + + + + + +#ifdef __cplusplus +} +#endif diff --git a/system/kernel/pdp_post.c b/system/kernel/pdp_post.c new file mode 100644 index 0000000..fb761d0 --- /dev/null +++ b/system/kernel/pdp_post.c @@ -0,0 +1,48 @@ + +/* + * Pure Data Packet system file. pdp logging. + * 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 <stdarg.h> +#include <string.h> +#include <errno.h> +#include "pdp_post.h" + +/* list printing should be moved here too */ + +/* write a message to log (console) */ +void pdp_post_n(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +void pdp_post(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + putc('\n', stderr); +} + + diff --git a/system/kernel/pdp_symbol.c b/system/kernel/pdp_symbol.c new file mode 100644 index 0000000..32e9e33 --- /dev/null +++ b/system/kernel/pdp_symbol.c @@ -0,0 +1,196 @@ +/* + * Pure Data Packet system implementation. : code implementing pdp's namespace (symbols) + * 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 <string.h> +#include <pthread.h> +#include "pdp_symbol.h" +#include "pdp_list.h" +#include "pdp_debug.h" + +// some extra prototypes +void *pdp_alloc(int size); +void pdp_dealloc(void *data); + +// the symbol hash mutex +static pthread_mutex_t pdp_hash_mutex; + +#define HASHSIZE 1024 +static t_pdp_symbol *pdp_symhash[HASHSIZE]; + + +#define LOCK pthread_mutex_lock(&pdp_hash_mutex) +#define UNLOCK pthread_mutex_unlock(&pdp_hash_mutex) + + +static void _pdp_symbol_init(t_pdp_symbol *s) +{ + memset(s, 0, sizeof(*s)); + s->s_forth.t = a_undef; +} + + +/* 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 */ + LOCK; + + 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)); + _pdp_symbol_init(sym2); + sym2->s_name = pdp_alloc(length+1); + sym2->s_next = 0; + strcpy(sym2->s_name, s); + } + *sym1 = sym2; + + gotit: + + /* unlock hash */ + UNLOCK; + return (sym2); +} + +t_pdp_symbol *pdp_gensym(char *s) +{ + return(_pdp_dogensym(s, 0)); +} + + +/* connect a parsed typelist to a symbol type name + 1 = succes, 0 = error (symbol already connected) */ +int pdp_symbol_set_typelist(t_pdp_symbol *s, t_pdp_list *typelist) +{ + int status = 0; + LOCK; + if (!s->s_type){ + s->s_type = typelist; + status = 1; + } + UNLOCK; + return status; +} + + +void pdp_symbol_apply_all(t_pdp_symbol_iterator it) +{ + int i; + for (i=0; i<HASHSIZE; i++){ + t_pdp_symbol *s; + for (s = pdp_symhash[i]; s; s=s->s_next){ + it(s); + } + + } +} + +t_pdp_symbol _pdp_sym_wildcard; +t_pdp_symbol _pdp_sym_float; +t_pdp_symbol _pdp_sym_int; +t_pdp_symbol _pdp_sym_symbol; +t_pdp_symbol _pdp_sym_packet; +t_pdp_symbol _pdp_sym_pointer; +t_pdp_symbol _pdp_sym_invalid; +t_pdp_symbol _pdp_sym_list; +t_pdp_symbol _pdp_sym_question_mark; +t_pdp_symbol _pdp_sym_atom; +t_pdp_symbol _pdp_sym_null; +t_pdp_symbol _pdp_sym_quote_start; +t_pdp_symbol _pdp_sym_quote_end; +t_pdp_symbol _pdp_sym_return; +t_pdp_symbol _pdp_sym_nreturn; +t_pdp_symbol _pdp_sym_defstart; +t_pdp_symbol _pdp_sym_defend; +t_pdp_symbol _pdp_sym_if; +t_pdp_symbol _pdp_sym_then; +t_pdp_symbol _pdp_sym_local; +t_pdp_symbol _pdp_sym_forth; +t_pdp_symbol _pdp_sym_call; +t_pdp_symbol _pdp_sym_push; +t_pdp_symbol _pdp_sym_pop; + +static void _sym(char *name, t_pdp_symbol *s) +{ + t_pdp_symbol *realsym; + _pdp_symbol_init(s); + s->s_name = name; + realsym = _pdp_dogensym(name, s); + PDP_ASSERT(realsym == s); // if this fails, the symbol was already defined +} + +void pdp_symbol_setup(void) +{ + // create mutexes + pthread_mutex_init(&pdp_hash_mutex, NULL); + + // init symbol hash + memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *)); + + // setup predefined symbols (those that have direct pointer access for speedup) + _sym("*", &_pdp_sym_wildcard); + _sym("float", &_pdp_sym_float); + _sym("int", &_pdp_sym_int); + _sym("symbol", &_pdp_sym_symbol); + _sym("packet", &_pdp_sym_packet); + _sym("pointer", &_pdp_sym_pointer); + _sym("invalid", &_pdp_sym_invalid); + _sym("list", &_pdp_sym_list); + _sym("?", &_pdp_sym_question_mark); + _sym("atom", &_pdp_sym_atom); + _sym("null", &_pdp_sym_null); + _sym("[", &_pdp_sym_quote_start); + _sym("]", &_pdp_sym_quote_end); + _sym("ret", &_pdp_sym_return); + _sym("nret", &_pdp_sym_nreturn); + _sym(":", &_pdp_sym_defstart); + _sym(";", &_pdp_sym_defend); + _sym("if", &_pdp_sym_if); + _sym("then", &_pdp_sym_then); + _sym("local", &_pdp_sym_local); + _sym("forth", &_pdp_sym_forth); + _sym("call", &_pdp_sym_call); + _sym("push", &_pdp_sym_push); + _sym("pop", &_pdp_sym_pop); + +} + + diff --git a/system/kernel/pdp_type.c b/system/kernel/pdp_type.c new file mode 100644 index 0000000..0216d07 --- /dev/null +++ b/system/kernel/pdp_type.c @@ -0,0 +1,473 @@ +/* + * 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_list.h" +#include "pdp_symbol.h" +#include "pdp_packet.h" +#include "pdp_post.h" +#include "pdp_type.h" +#include "pdp_mem.h" +#include "pdp_debug.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_conversion_mutex; +static pthread_mutex_t pdp_cache_mutex; + + + +/* convert a type to a list */ +t_pdp_list *pdp_type_to_list(t_pdp_symbol *type) +{ +#define TMPSIZE 1024 + + char *c = type->s_name; + char *lastname = c; + int n = 0; + char tmp[strlen(type->s_name)+1]; + 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_ASSERT(n < TMPSIZE); + } + } + pdp_list_add_back(l, a_symbol, (t_pdp_word)pdp_gensym(lastname)); + + return l; +} + + +/* get the description symbol. */ +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){ + + /* since every packet is obliged to have a description, this is an error */ + pdp_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 pdp_post("DEBUG: _pdp_type_run_conversion_program: method = %x", m); + p = m(packet, dest_template); + D pdp_post("DEBUG: _pdp_type_run_conversion_program:"); + D pdp_post(" 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 pdp_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 pdp_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 pdp_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) +{ + D pdp_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 pdp_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 pdp_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 pdp_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 pdp_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){ + pdp_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 in the cache + 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); +} + + +/* this is a hack. type cache data: (a type description parsed into + a pdp_list for fast comparison) should be setup when + a packet symbol is created, but since that can be everywhere, + we set it up on first use. type cache is permanent. */ + + +static void _setup_type_cache(t_pdp_symbol *s) +{ + t_pdp_list *l = pdp_type_to_list(s); + if (!pdp_symbol_set_typelist(s, l)) + pdp_list_free(l); // list was already present -> free cached list + +} + +/* 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 match = 0; // no match until all tests are passed + + t_pdp_atom *ad, *ap; + t_pdp_symbol *wildcard = PDP_SYM_WILDCARD; + + PDP_ASSERT(pattern); + PDP_ASSERT(description); + + /* same type symbol -> match */ + if (description == pattern) {match = 1; goto done;} + + /* check the description list */ + if (!(description->s_type)) _setup_type_cache(description); + if (!(pattern->s_type)) _setup_type_cache(pattern); + + /* compare symbols of description list */ + for(ad=description->s_type->first, ap=pattern->s_type->first; + ad && ap; + 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) {goto done;} /* difference and not a wildcard */ + } + + /* ok if sizes are equal */ + if (! (ad || ap)) {match = 1; goto done;} + + /* one of the two is shorter, so the shortest list needs + to end with a wildcard to have a match */ + + if (ap && description->s_type->last->w.w_symbol != wildcard) goto done; + if (ad && pattern->s_type->last->w.w_symbol != wildcard) goto done; + + /* all tests passed: type templates match */ + match = 1; + + done: + D pdp_post("DEBUG: testing match between %s and %s: %s", + description->s_name, pattern->s_name, match ? "match" : "no match"); + return match; + +} + + + + + +/* setup method */ +void pdp_type_setup(void) +{ + int i; + + // create mutexes + pthread_mutex_init(&pdp_conversion_mutex, NULL); + pthread_mutex_init(&pdp_cache_mutex, NULL); + + // 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/mmx/Makefile b/system/mmx/Makefile new file mode 100644 index 0000000..9b1f690 --- /dev/null +++ b/system/mmx/Makefile @@ -0,0 +1,31 @@ +include ../../Makefile.config + +OBJ = \ +pixel_pack_s16u8.o \ +pixel_unpack_u8s16.o \ +pixel_add_s16.o \ +pixel_mul_s16.o \ +pixel_mix_s16.o \ +pixel_randmix_s16.o \ +pixel_conv_hor_s16.o \ +pixel_conv_ver_s16.o \ +pixel_biquad_s16.o \ +pixel_ca_s1.o \ +pixel_rand_s16.o \ +pixel_crot_s16.o \ +pixel_gain_s16.o \ +pixel_resample_s16.o \ +pixel_cheby_s16.o + +all: $(OBJ) + +test: pdp_mmx_test.o $(OBJ) + gcc -o pdp_mmx_test pdp_mmx_test.o $(OBJ) -g + +clean: + rm -f *.o + rm -f *~ + rm -f pdp_mmx.a + rm -f pdp_mmx_test + + diff --git a/system/mmx/pdp_mmx_test.c b/system/mmx/pdp_mmx_test.c new file mode 100644 index 0000000..e93539f --- /dev/null +++ b/system/mmx/pdp_mmx_test.c @@ -0,0 +1,62 @@ +#include "pdp_mmx.h" + +#define FP(x) ((short int)(((float)(x) * 2 * 256.0f))) + +#define nbp 256 + + short int a1[4] = {0x0100,0x0100,0x0100,0x0100}; + short int a2[4] = {0x0100,0x0100,0x0100,0x0100}; + short int b0[4] = {0x0100,0x0100,0x0100,0x0100}; + short int b1[4] = {0x0100,0x0100,0x0100,0x0100}; + short int b2[4] = {0x0100,0x0100,0x0100,0x0100}; + + short int u1[4] = {0x0100,0x0100,0x0100,0x0100}; + short int u2[4] = {0x0100,0x0100,0x0100,0x0100}; + + short int x0[4] = {0x0100,0x0100,0x0100,0x0100}; + short int x1[4] = {0x0100,0x0100,0x0100,0x0100}; + short int x2[4] = {0x0100,0x0100,0x0100,0x0100}; + short int x3[4] = {0x0100,0x0100,0x0100,0x0100}; + +void print_pixel(unsigned int i) +{ + if (i) printf("x "); + else printf(". "); +} + +void print_line(void) +{ + printf("\n"); +} + +void print_square(unsigned char *c) +{ + int i,j; + + for(j=7; j>=0; j--){ + for(i=0; i<8; i++) print_pixel(c[j] & (1<<(7-i))); + printf("\n"); + } + +} + +main() +{ + + unsigned char src[16]={1,2,3,4,5,6,7,8,-1,-2,-3,-4,-5,-6,-7,-8}; + unsigned char dst[8]; + + + print_square(src); + print_line(); + print_square(src+8); + print_line(); + + pixel_test_s1(dst,src,1,1); + + print_square(dst); + print_line(); + + + +} diff --git a/system/mmx/pixel_add_s16.s b/system/mmx/pixel_add_s16.s new file mode 100644 index 0000000..8d4c7df --- /dev/null +++ b/system/mmx/pixel_add_s16.s @@ -0,0 +1,55 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_add_s16 +.type pixel_add_s16,@function + +# simple add +# void pixel_add_s16(int *left, int *right, int nb_4pixel_vectors) + +pixel_add_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 8(%ebp), %edi # left array + movl 12(%ebp), %esi # right array + movl 16(%ebp), %ecx # pixel count + + + .align 16 + .loop_mix: + +# prefetch 128(%esi) + movq (%esi), %mm1 # load right 4 pixels from memory + movq (%edi), %mm0 # load 4 left pixels from memory + paddsw %mm1, %mm0 # mix + movq %mm0, (%edi) + addl $8, %esi + addl $8, %edi + decl %ecx + jnz .loop_mix # loop + + emms + + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_biquad_dirI_s16.s b/system/mmx/pixel_biquad_dirI_s16.s new file mode 100644 index 0000000..1729502 --- /dev/null +++ b/system/mmx/pixel_biquad_dirI_s16.s @@ -0,0 +1,361 @@ +# Pure Data Packet mmx routine. +# 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. +# + + + # TODO MOVE TO DIRECT FORM II + # 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] + + # input in register: + # %mm0-mm3: input 4x4 pixels {x0 x1 x2 x3} + # %esi: coef memory (a1, a2, b0, b1, b2) + # %edi: state memory (u1, u2) + + + # return in register: + # %mm0-mm4: 4x4 pixels result + + + .biquad_4x4_pixels: + .align 16 + # prescale + movq -8(%esi), %mm4 + pmulhw %mm4, %mm0 + pmulhw %mm4, %mm1 + pmulhw %mm4, %mm2 + pmulhw %mm4, %mm3 + psllw $1, %mm0 + psllw $1, %mm1 + psllw $1, %mm2 + psllw $1, %mm3 + + + # first vector + movq 0(%edi), %mm4 # mm4 <- u[-1] + movq 8(%edi), %mm5 # mm5 <- u[-2] + movq %mm4, %mm6 + movq %mm5, %mm7 + + pmulhw 0(%esi), %mm6 # multiply by a1 + pmulhw 8(%esi), %mm7 # multiply by a2 + + paddsw %mm6, %mm0 # accumulate + paddsw %mm7, %mm0 # accumulate + paddsw %mm0, %mm0 # scale by 2 (since all fixed point muls are x*y/2) + + movq %mm0, %mm6 # mm6 <- u[0] + movq %mm4, %mm7 # mm7 <- u[-1] + pmulhw 16(%esi), %mm0 # multiply by b0 + pmulhw 24(%esi), %mm4 # multiply by b1 + pmulhw 32(%esi), %mm5 # multiply by b2 + + paddsw %mm4, %mm0 # accumulate + paddsw %mm5, %mm0 # accumulate + + # mm0 is result 0 + + # second vector + movq %mm6, %mm4 # mm4 <- u[0] + movq %mm7, %mm5 # mm5 <- u[-1] + + pmulhw 0(%esi), %mm6 # multiply by a1 + pmulhw 8(%esi), %mm7 # multiply by a2 + + paddsw %mm6, %mm1 # accumulate + paddsw %mm7, %mm1 # accumulate + paddsw %mm1, %mm1 # scale by 2 + + + movq %mm1, %mm6 # mm6 <- u[1] + movq %mm4, %mm7 # mm7 <- u[0] + pmulhw 16(%esi), %mm1 # multiply by b0 + pmulhw 24(%esi), %mm4 # multiply by b1 + pmulhw 32(%esi), %mm5 # multiply by b2 + + paddsw %mm4, %mm1 # accumulate + paddsw %mm5, %mm1 # accumulate + + # mm1 is result 1 + + # third vector + movq %mm6, %mm4 # mm4 <- u[1] + movq %mm7, %mm5 # mm5 <- u[0] + + pmulhw 0(%esi), %mm6 # multiply by a1 + pmulhw 8(%esi), %mm7 # multiply by a2 + + paddsw %mm6, %mm2 # accumulate + paddsw %mm7, %mm2 # accumulate + paddsw %mm2, %mm2 # scale by 2 + + + movq %mm2, %mm6 # mm6 <- u[2] + movq %mm4, %mm7 # mm7 <- u[1] + pmulhw 16(%esi), %mm2 # multiply by b0 + pmulhw 24(%esi), %mm4 # multiply by b1 + pmulhw 32(%esi), %mm5 # multiply by b2 + + paddsw %mm4, %mm2 # accumulate + paddsw %mm5, %mm2 # accumulate + + # mm2 is result 2 + + # fourth vector + movq %mm6, %mm4 # mm4 <- u[2] + movq %mm7, %mm5 # mm5 <- u[1] + + pmulhw 0(%esi), %mm6 # multiply by a1 + pmulhw 8(%esi), %mm7 # multiply by a2 + + paddsw %mm6, %mm3 # accumulate + paddsw %mm7, %mm3 # accumulate + paddsw %mm3, %mm3 # scale by 2 + + + movq %mm3, 0(%edi) # store u[3] + movq %mm4, 8(%edi) # store u[2] + pmulhw 16(%esi), %mm3 # multiply by b0 + pmulhw 24(%esi), %mm4 # multiply by b1 + pmulhw 32(%esi), %mm5 # multiply by b2 + + paddsw %mm4, %mm3 # accumulate + paddsw %mm5, %mm3 # accumulate + + # mm3 is result 3 + + ret + + + # in order to use the 4 line parallel biquad routine on horizontal + # lines, we need to reorder (rotate or transpose) the matrix, since + # images are scanline encoded, and we want to work in parallell + # on 4 lines. + # + # since the 4 lines are independent, it doesnt matter in which order + # the the vector elements are present. + # + # this allows us to use the same routine for left->right and right->left + # processing. + # + # some comments on the non-abelean group of square isometries consisting of + # (I) identity + # (H) horizontal axis mirror + # (V) vertical axis mirror + # (T) transpose (diagonal axis mirror) + # (A) antitranspose (antidiagonal axis mirror) + # (R1) 90deg anticlockwize rotation + # (R2) 180deg rotation + # (R3) 90deg clockwize rotation + # + # + # we basicly have two options: (R1,R3) or (T,A) + # we opt for T and A because they are self inverting, which improves locality + # + # use antitranspose for right to left an transpose + # for left to right (little endian) + + + # antitranspose 4x4 + + # input + # %mm3 == {d0 d1 d2 d3} + # %mm2 == {c0 c1 c2 c3} + # %mm1 == {b0 b1 b2 b3} + # %mm0 == {a0 a1 a2 a3} + + # output + # %mm3 == {a3 b3 c3 d3} + # %mm2 == {a2 b2 c2 d2} + # %mm1 == {a1 b1 c1 d1} + # %mm0 == {a0 b0 c0 d0} + + + .antitranspose_4x4: + .align 16 + movq %mm3, %mm4 + punpcklwd %mm1, %mm4 # mm4 <- {b2 d2 b3 d3} + movq %mm3, %mm5 + punpckhwd %mm1, %mm5 # mm5 <- {b0 d0 b1 d1} + + movq %mm2, %mm6 + punpcklwd %mm0, %mm6 # mm6 <- {a2 c2 a3 c3} + movq %mm2, %mm7 + punpckhwd %mm0, %mm7 # mm7 <- {a0 c0 a1 c1} + + movq %mm4, %mm3 + punpcklwd %mm6, %mm3 # mm3 <- {a3 b3 c3 d3} + movq %mm4, %mm2 + punpckhwd %mm6, %mm2 # mm2 <- {a2 b2 c2 d2} + + movq %mm5, %mm1 + punpcklwd %mm7, %mm1 # mm1 <- {a1 b1 c1 d1} + movq %mm5, %mm0 + punpckhwd %mm7, %mm0 # mm0 <- {a0 b0 c0 d0} + + ret + + + + # transpose 4x4 + + # input + # %mm3 == {d3 d2 d1 d0} + # %mm2 == {c3 c2 c1 c0} + # %mm1 == {b3 b2 b1 b0} + # %mm0 == {a3 a2 a1 a0} + + # output + # %mm3 == {d3 c3 b3 a3} + # %mm2 == {d2 c2 b2 a2} + # %mm1 == {d1 c1 b1 a1} + # %mm0 == {d0 c0 b0 a0} + + + .transpose_4x4: + .align 16 + movq %mm0, %mm4 + punpcklwd %mm2, %mm4 # mm4 <- {c1 a1 c0 a0} + movq %mm0, %mm5 + punpckhwd %mm2, %mm5 # mm5 <- {c3 a3 c2 a2} + + movq %mm1, %mm6 + punpcklwd %mm3, %mm6 # mm6 <- {d1 b1 d0 b0} + movq %mm1, %mm7 + punpckhwd %mm3, %mm7 # mm7 <- {d3 b3 d2 b2} + + movq %mm4, %mm0 + punpcklwd %mm6, %mm0 # mm0 <- {d0 c0 b0 a0} + movq %mm4, %mm1 + punpckhwd %mm6, %mm1 # mm1 <- {d1 c1 b1 a1} + + movq %mm5, %mm2 + punpcklwd %mm7, %mm2 # mm2 <- {d2 c2 b2 a2} + movq %mm5, %mm3 + punpckhwd %mm7, %mm3 # mm3 <- {d3 c3 b3 a3} + + ret + + +.globl pixel_biquad_vertb_s16 +.type pixel_biquad_vertb_s16,@function + + +# pixel_biquad_vertbr_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + + +pixel_biquad_vertb_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + .align 16 + .biquad_vertb_line_loop: + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + movq (%ebx,%eax,1), %mm3 + call .biquad_4x4_pixels + movq %mm0, (%ebx) + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + addl %edx, %ebx + addl %eax, %ebx + decl %ecx + jnz .biquad_vertb_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + +.globl pixel_biquad_horlr_s16 +.type pixel_biquad_horlr_s16,@function + + +# pixel_biquad_hor_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + + +pixel_biquad_horlr_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + .align 16 + .biquad_horlr_line_loop: + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + movq (%ebx,%eax,1), %mm3 + call .transpose_4x4 + call .biquad_4x4_pixels + call .transpose_4x4 + movq %mm0, (%ebx) + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + addl $8, %ebx + decl %ecx + jnz .biquad_horlr_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + + + diff --git a/system/mmx/pixel_biquad_s16.s b/system/mmx/pixel_biquad_s16.s new file mode 100644 index 0000000..844b041 --- /dev/null +++ b/system/mmx/pixel_biquad_s16.s @@ -0,0 +1,451 @@ +# Pure Data Packet mmx routine. +# 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. +# + + + # DIRECT FORM II BIQUAD + # + # 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] + # MACRO: df2 <reg> + # + # computes a direct form 2 biquad + # does not use {mm0-mm3}\<inreg> + # + # input: <reg> == input + # %mm4 == state 1 + # %mm5 == state 2 + # (%esi) == biquad coefs (-a1 -a2 b0 b1 b2) in s1.14 + # output: <reg> == output + # %mm4 == state 1 + # %mm5 == state 2 + + .macro df2 reg + movq \reg, %mm6 # mm6 == x[k] + movq \reg, %mm7 # mm7 == x[k] + pmulhw 16(%esi), %mm6 # mm6 == x[k] * b0 + pmulhw 24(%esi), %mm7 # mm7 == x[k] * b1 + paddw %mm4, %mm6 # mm6 == x[k] * b0 + u1[k-1] == y[k] + paddw %mm5, %mm7 # mm7 == x[k] * b1 + u2[k-1] + paddsw %mm6, %mm6 # compensate for mul = x*y/4 (coefs are s1.14 fixed point) + paddsw %mm6, %mm6 # paddsw ensures saturation + movq \reg, %mm5 # mm5 == x[k] + movq %mm6, %mm4 # mm4 == y[k] + movq %mm6, \reg # reg == y[k] -------------------- + pmulhw 0(%esi), %mm4 # mm4 == y[k] * (-a1) + pmulhw 8(%esi), %mm6 # mm6 == y[k] * (-a2) + pmulhw 32(%esi), %mm5 # mm5 == x[k] * b2 + paddw %mm7, %mm4 # mm4 == u1[k] -------------------- + paddw %mm6, %mm5 # mm5 == u2[k] -------------------- + .endm + + + # input in register: + # %mm0-mm3: input 4x4 pixels {x0 x1 x2 x3} + # %esi: coef memory (-a1, -a2, b0, b1, b2) in s1.14 + # %edi: state memory (u1, u2) + + # return in register: + # %mm0-mm4: 4x4 pixels result + + + + + .macro biquad_4x4_pixels + .align 16 + movq 0(%edi), %mm4 # get state + movq 8(%edi), %mm5 + df2 %mm0 # compute 4 biquads + df2 %mm1 + df2 %mm2 + df2 %mm3 + movq %mm4, 0(%edi) # store state + movq %mm5, 8(%edi) + .endm + + + + # in order to use the 4 line parallel biquad routine on horizontal + # lines, we need to reorder (rotate or transpose) the matrix, since + # images are scanline encoded, and we want to work in parallell + # on 4 lines. + # + # since the 4 lines are independent, it doesnt matter in which order + # the the vector elements are present. + # + # this allows us to use the same routine for left->right and right->left + # processing. + # + # some comments on the non-abelean group of square isometries consisting of + # (I) identity + # (H) horizontal axis mirror + # (V) vertical axis mirror + # (T) transpose (diagonal axis mirror) + # (A) antitranspose (antidiagonal axis mirror) + # (R1) 90deg anticlockwize rotation + # (R2) 180deg rotation + # (R3) 90deg clockwize rotation + # + # + # we basicly have two options: (R1,R3) or (T,A) + # we opt for T and A because they are self inverting, which improves locality + # + # use antitranspose for right to left an transpose + # for left to right (little endian) + + + # antitranspose 4x4 + + # input + # %mm3 == {d0 d1 d2 d3} + # %mm2 == {c0 c1 c2 c3} + # %mm1 == {b0 b1 b2 b3} + # %mm0 == {a0 a1 a2 a3} + + # output + # %mm3 == {a3 b3 c3 d3} + # %mm2 == {a2 b2 c2 d2} + # %mm1 == {a1 b1 c1 d1} + # %mm0 == {a0 b0 c0 d0} + + + .macro antitranspose_4x4: + movq %mm3, %mm4 + punpcklwd %mm1, %mm4 # mm4 <- {b2 d2 b3 d3} + movq %mm3, %mm5 + punpckhwd %mm1, %mm5 # mm5 <- {b0 d0 b1 d1} + + movq %mm2, %mm6 + punpcklwd %mm0, %mm6 # mm6 <- {a2 c2 a3 c3} + movq %mm2, %mm7 + punpckhwd %mm0, %mm7 # mm7 <- {a0 c0 a1 c1} + + movq %mm4, %mm3 + punpcklwd %mm6, %mm3 # mm3 <- {a3 b3 c3 d3} + movq %mm4, %mm2 + punpckhwd %mm6, %mm2 # mm2 <- {a2 b2 c2 d2} + + movq %mm5, %mm1 + punpcklwd %mm7, %mm1 # mm1 <- {a1 b1 c1 d1} + movq %mm5, %mm0 + punpckhwd %mm7, %mm0 # mm0 <- {a0 b0 c0 d0} + + .endm + + + # transpose 4x4 + + # input + # %mm3 == {d3 d2 d1 d0} + # %mm2 == {c3 c2 c1 c0} + # %mm1 == {b3 b2 b1 b0} + # %mm0 == {a3 a2 a1 a0} + + # output + # %mm3 == {d3 c3 b3 a3} + # %mm2 == {d2 c2 b2 a2} + # %mm1 == {d1 c1 b1 a1} + # %mm0 == {d0 c0 b0 a0} + + + .macro transpose_4x4: + movq %mm0, %mm4 + punpcklwd %mm2, %mm4 # mm4 <- {c1 a1 c0 a0} + movq %mm0, %mm5 + punpckhwd %mm2, %mm5 # mm5 <- {c3 a3 c2 a2} + + movq %mm1, %mm6 + punpcklwd %mm3, %mm6 # mm6 <- {d1 b1 d0 b0} + movq %mm1, %mm7 + punpckhwd %mm3, %mm7 # mm7 <- {d3 b3 d2 b2} + + movq %mm4, %mm0 + punpcklwd %mm6, %mm0 # mm0 <- {d0 c0 b0 a0} + movq %mm4, %mm1 + punpckhwd %mm6, %mm1 # mm1 <- {d1 c1 b1 a1} + + movq %mm5, %mm2 + punpcklwd %mm7, %mm2 # mm2 <- {d2 c2 b2 a2} + movq %mm5, %mm3 + punpckhwd %mm7, %mm3 # mm3 <- {d3 c3 b3 a3} + + .endm + +.globl pixel_biquad_vertb_s16 +.type pixel_biquad_vertb_s16,@function + + +# pixel_biquad_vertbr_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + + +pixel_biquad_vertb_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + .align 16 + .biquad_vertb_line_loop: + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + movq (%ebx,%eax,1), %mm3 + biquad_4x4_pixels + movq %mm0, (%ebx) + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + addl %edx, %ebx + addl %eax, %ebx + decl %ecx + jnz .biquad_vertb_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret +.globl pixel_biquad_verbt_s16 +.type pixel_biquad_verbt_s16,@function + + +# pixel_biquad_vertbt_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + + +pixel_biquad_verbt_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %eax # line with + + shll $3, %eax # 4 line byte spacing + decl %ecx + mul %ecx + incl %ecx + addl %eax, %ebx # ebx points to last pixblock + + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + .align 16 + .biquad_verbt_line_loop: + movq (%ebx), %mm3 + movq (%ebx,%edx,1), %mm2 + movq (%ebx,%edx,2), %mm1 + movq (%ebx,%eax,1), %mm0 + biquad_4x4_pixels + movq %mm3, (%ebx) + movq %mm2, (%ebx,%edx,1) + movq %mm1, (%ebx,%edx,2) + movq %mm0, (%ebx,%eax,1) + subl %edx, %ebx + subl %eax, %ebx + decl %ecx + jnz .biquad_verbt_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + +.globl pixel_biquad_horlr_s16 +.type pixel_biquad_horlr_s16,@function +# pixel_biquad_hor_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + +pixel_biquad_horlr_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + .align 16 + .biquad_horlr_line_loop: + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + movq (%ebx,%eax,1), %mm3 + transpose_4x4 + biquad_4x4_pixels + transpose_4x4 + movq %mm0, (%ebx) + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + addl $8, %ebx + decl %ecx + jnz .biquad_horlr_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + + +.globl pixel_biquad_horrl_s16 +.type pixel_biquad_horrl_s16,@function +# pixel_biquad_horrl_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + +pixel_biquad_horrl_s16: + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + + movl %ecx, %eax + decl %eax + shll $3, %eax + addl %eax, %ebx # ebx points to last pixblock + + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + .align 16 + .biquad_horrl_line_loop: + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + movq (%ebx,%eax,1), %mm3 + antitranspose_4x4 + biquad_4x4_pixels + antitranspose_4x4 + movq %mm0, (%ebx) + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + subl $8, %ebx + decl %ecx + jnz .biquad_horrl_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + + +.globl pixel_biquad_time_s16 +.type pixel_biquad_time_s16,@function +# pixel_biquad_time_s16(short int *pixel_array, short int *s1, short int *s2, short int *coefs, int nb_4_pix_vectors) + +pixel_biquad_time_s16: + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %edx # state 1 array + movl 16(%ebp), %edi # state 2 array + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %ecx # nb of 4 pixel vectors + + + .align 16 + .biquad_time_loop: + movq (%ebx), %mm0 # get input + movq (%edx), %mm4 # get state 1 + movq (%edi), %mm5 # get state 2 + df2 %mm0 # compute direct form 2 + movq %mm0, (%ebx) # write output + movq %mm5, (%edi) # write state 2 + movq %mm4, (%edx) # write state 1 + addl $8, %ebx + addl $8, %edi + addl $8, %edx + decl %ecx + jnz .biquad_time_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + + diff --git a/system/mmx/pixel_ca_s1.s b/system/mmx/pixel_ca_s1.s new file mode 100644 index 0000000..d9c730f --- /dev/null +++ b/system/mmx/pixel_ca_s1.s @@ -0,0 +1,189 @@ +# Pure Data Packet mmx routine. +# 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 assembler routines for 2D 1 bit cellular automata + # processing. it is organized around a feeder kernel and a + # stack based bit processor (virtual forth machine) + # + # the feeder kernel is responsable for loading/storing CA cells + # from/to memory. data in memory is organized as a scanline + # encoded toroidial bitplane (lsb = left). to simplify the kernel, the top + # left corner of the rectangular grid of pixels will shift down + # every processing step. + # + # the stack machine has the following architecture: + # CA stack: %esi, TOS: %mm0 (32x2 pixels. lsw = top row) + # CA horizon: %mm4-%mm7 (64x4 pixels. %mm4 = top row) + # + # the stack size / organization is not known to the stack machine. + # it can be thought of as operating on a 3x3 cell neightbourhood. + # the only purpose of forth program is to determine the CA local update rule. + # + # the machine is supposed to be very minimal. no looping control. + # no adressing modes. no conditional code (hey, this is an experiment!) + # so recursion is not allowed (no way to stop it) + # there are 9 words to load the cell neigbourhood on the stack. + # the rest is just logic and stack manips. + + + # this file contains pure asm macros. it is to be included before assembly + # after scaforth.pl has processed the .scaf file + + + # *************************** CA CELL ACCESS MACROS ***************************** + # fetchTL - fetchBR + + # shift / load rectangle macros: + + # shift rectangle horizontal + # result is in reg1 + .macro shift reg1 reg2 count + psllq $(32+\count), \reg1 + psrlq $(32-\count), \reg2 + psrlq $32, \reg1 + psllq $32, \reg2 + por \reg2, \reg1 + .endm + + .macro ldtop reg1 reg2 + movq %mm4, \reg1 + movq %mm5, \reg2 + .endm + + .macro ldcenter reg1 reg2 + movq %mm5, \reg1 + movq %mm6, \reg2 + .endm + + .macro ldbottom reg1 reg2 + movq %mm6, \reg1 + movq %mm7, \reg2 + .endm + + + # fetch from top row + + # fetch the top left square + .macro fetchTL + ldtop %mm0, %mm1 + shift %mm0, %mm1, -1 + .endm + + # fetch the top mid square + .macro fetchTM + ldtop %mm0, %mm1 + shift %mm0, %mm1, 0 + .endm + + # fetch the top right square + .macro fetchTR + ldtop %mm0, %mm1 + shift %mm0, %mm1, 1 + .endm + + + + # fetch from center row + + # fetch the mid left square + .macro fetchML + ldcenter %mm0, %mm1 + shift %mm0, %mm1, -1 + .endm + + # fetch the mid mid square + .macro fetchMM + ldcenter %mm0, %mm1 + shift %mm0, %mm1, 0 + .endm + + # fetch the mid right square + .macro fetchMR + ldcenter %mm0, %mm1 + shift %mm0, %mm1, 1 + .endm + + + + + + # fetch from bottom row + + # fetch the bottom left square + .macro fetchBL + ldbottom %mm0, %mm1 + shift %mm0, %mm1, -1 + .endm + + # fetch the bottom mid square + .macro fetchBM + ldbottom %mm0, %mm1 + shift %mm0, %mm1, 0 + .endm + + # fetch the bottom right square + .macro fetchBR + ldbottom %mm0, %mm1 + shift %mm0, %mm1, 1 + .endm + + + + # *************************** CA STACK MANIP MACROS ***************************** + # dup drop dropdup swap nip dropover + + .macro dup + lea -8(%esi), %esi + movq %mm0, (%esi) + .endm + + .macro drop + movq (%esi), %mm0 + lea 8(%esi), %esi + .endm + + .macro dropdup + movq (%esi), %mm0 + .endm + + .macro swap + movq (%esi), %mm1 + movq %mm0, (%esi) + movq %mm1, %mm0 + .endm + + .macro nip + lea 8(%esi), %esi + .endm + + .macro dropover + movq 8(%esi), %mm0 + .endm + + + # *************************** CA BOOLEAN LOGIC MACROS ***************************** + # overxor + + .macro overxor + pxor (%esi), %mm0 + .endm + + + + + diff --git a/system/mmx/pixel_cascade_s16.s b/system/mmx/pixel_cascade_s16.s new file mode 100644 index 0000000..bf88d08 --- /dev/null +++ b/system/mmx/pixel_cascade_s16.s @@ -0,0 +1,330 @@ +# Pure Data Packet mmx routine. +# 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. +# + + + # TODO: COUPLED CASCADE SECOND ORDER SECTION + # + # s1[k] = ar * s1[k-1] + ai * s2[k-1] + x[k] + # s2[k] = ar * s2[k-1] - ai * s1[k-1] + # y[k] = c0 * x[k] + c1 * s1[k-1] + c2 * s2[k-1] + + + # MACRO: df2 + # + # computes a coupled cascade + # + # input: %mm0 == input + # %mm1 == state 1 + # %mm2 == state 2 + # (%esi) == cascade coefs (ar ai c0 c1 c2) in s0.15 + # output: %mm0 == output + # %mm1 == state 1 + # %mm2 == state 2 + + + .macro coupled + pmovq %mm1, %mm3 # mm3 == s1[k-1] + pmovq %mm1, %mm4 # mm4 == s1[k-1] + pmovq %mm2, %mm5 # mm5 == s2[k-1] + pmovq %mm2, %mm6 # mm5 == s2[k-1] + pmulhw (%esi), %mm1 # mm1 == s1[k-1] * ar + pmulhw 8(%esi), %mm3 # mm3 == s1[k-1] * ai + pmulhw 24(%esi), %mm4 # mm4 == s1[k-1] * c1 + pmulhw (%esi), %mm2 # mm2 == s2[k-1] * ar + pmulhw 8(%esi), %mm5 # mm5 == s2[k-1] * ai + pmulhw 32(%esi), %mm6 # mm6 == s2[k-1] * c2 + paddw %mm5, %mm1 # mm1 == s1[k-1] * ar + s2[k-1] * ai + psubw %mm3, %mm2 # mm2 == s2[k-1] * ar - s1[k-1] * ai == s2[k] + paddw %mm0, %mm1 # mm1 == s1[k] + pmulhw 16(%esi), %mm0 # mm0 == x[k] * c0 + paddw %mm6, %mm4 # mm4 == s1[k-1] * c1 + s2[k-1] * c2 + paddw %mm4, %mm0 # mm0 == y[k] + .endm + + + + + # in order to use the 4 line parallel cascade routine on horizontal + # lines, we need to reorder (rotate or transpose) the matrix, since + # images are scanline encoded, and we want to work in parallell + # on 4 lines. + # + # since the 4 lines are independent, it doesnt matter in which order + # the the vector elements are present. + # + # this allows us to use the same routine for left->right and right->left + # processing. + # + # some comments on the non-abelean group of square isometries consisting of + # (I) identity + # (H) horizontal axis mirror + # (V) vertical axis mirror + # (T) transpose (diagonal axis mirror) + # (A) antitranspose (antidiagonal axis mirror) + # (R1) 90deg anticlockwize rotation + # (R2) 180deg rotation + # (R3) 90deg clockwize rotation + # + # + # we basicly have two options: (R1,R3) or (T,A) + # we opt for T and A because they are self inverting, which improves locality + # + # use antitranspose for right to left an transpose + # for left to right (little endian) + + + # antitranspose 4x4 + + # input + # %mm3 == {d0 d1 d2 d3} + # %mm2 == {c0 c1 c2 c3} + # %mm1 == {b0 b1 b2 b3} + # %mm0 == {a0 a1 a2 a3} + + # output + # %mm3 == {a3 b3 c3 d3} + # %mm2 == {a2 b2 c2 d2} + # %mm1 == {a1 b1 c1 d1} + # %mm0 == {a0 b0 c0 d0} + + + .macro antitranspose_4x4: + movq %mm3, %mm4 + punpcklwd %mm1, %mm4 # mm4 <- {b2 d2 b3 d3} + movq %mm3, %mm5 + punpckhwd %mm1, %mm5 # mm5 <- {b0 d0 b1 d1} + + movq %mm2, %mm6 + punpcklwd %mm0, %mm6 # mm6 <- {a2 c2 a3 c3} + movq %mm2, %mm7 + punpckhwd %mm0, %mm7 # mm7 <- {a0 c0 a1 c1} + + movq %mm4, %mm3 + punpcklwd %mm6, %mm3 # mm3 <- {a3 b3 c3 d3} + movq %mm4, %mm2 + punpckhwd %mm6, %mm2 # mm2 <- {a2 b2 c2 d2} + + movq %mm5, %mm1 + punpcklwd %mm7, %mm1 # mm1 <- {a1 b1 c1 d1} + movq %mm5, %mm0 + punpckhwd %mm7, %mm0 # mm0 <- {a0 b0 c0 d0} + + .endm + + + # transpose 4x4 + + # input + # %mm3 == {d3 d2 d1 d0} + # %mm2 == {c3 c2 c1 c0} + # %mm1 == {b3 b2 b1 b0} + # %mm0 == {a3 a2 a1 a0} + + # output + # %mm3 == {d3 c3 b3 a3} + # %mm2 == {d2 c2 b2 a2} + # %mm1 == {d1 c1 b1 a1} + # %mm0 == {d0 c0 b0 a0} + + + .macro transpose_4x4: + movq %mm0, %mm4 + punpcklwd %mm2, %mm4 # mm4 <- {c1 a1 c0 a0} + movq %mm0, %mm5 + punpckhwd %mm2, %mm5 # mm5 <- {c3 a3 c2 a2} + + movq %mm1, %mm6 + punpcklwd %mm3, %mm6 # mm6 <- {d1 b1 d0 b0} + movq %mm1, %mm7 + punpckhwd %mm3, %mm7 # mm7 <- {d3 b3 d2 b2} + + movq %mm4, %mm0 + punpcklwd %mm6, %mm0 # mm0 <- {d0 c0 b0 a0} + movq %mm4, %mm1 + punpckhwd %mm6, %mm1 # mm1 <- {d1 c1 b1 a1} + + movq %mm5, %mm2 + punpcklwd %mm7, %mm2 # mm2 <- {d2 c2 b2 a2} + movq %mm5, %mm3 + punpckhwd %mm7, %mm3 # mm3 <- {d3 c3 b3 a3} + + .endm + +.globl pixel_cascade_vertb_s16 +.type pixel_cascade_vertb_s16,@function + + +# pixel_cascade_vertbr_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + + +pixel_cascade_vertb_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + subl %edx, %ebx + + movq 0(%edi), %mm1 # s1[k-1] + movq 8(%edi), %mm2 # s2[k-1] + .align 16 + .cascade_vertb_line_loop: + + movq (%ebx,%edx,1), %mm3 + movq %mm3, %mm0 + addl %edx, %ebx + coupled + movq %mm0, (%ebx) + + movq (%ebx,%edx,1), %mm3 + movq %mm3, %mm0 + addl %edx, %ebx + coupled + movq %mm0, (%ebx) + + movq (%ebx,%edx,1), %mm3 + movq %mm3, %mm0 + addl %edx, %ebx + coupled + movq %mm0, (%ebx) + + movq (%ebx,%edx,1), %mm3 + movq %mm3, %mm0 + addl %edx, %ebx + coupled + movq %mm0, (%ebx) + + decl %ecx + jnz .cascade_vertb_line_loop + + movq %mm1, 0(%edi) # s1[k-1] + movq %mm2, 8(%edi) # s2[k-1] + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + +.globl pixel_cascade_horlr_s16 +.type pixel_cascade_horlr_s16,@function + + +# pixel_cascade_hor_s16(char *pixel_array, int nb_rows, int linewidth, short int coef[20], short int state[8]) + + +pixel_cascade_horlr_s16: + + + pushl %ebp + movl %esp, %ebp + push %ebx + push %esi + push %edi + + movl 8(%ebp), %ebx # pixel array offset + movl 12(%ebp), %ecx # nb of 4x4 pixblocks + movl 16(%ebp), %edx # line with + + movl 20(%ebp), %esi # coefs + movl 24(%ebp), %edi # state + + shll $1, %edx # short int addressing + movl %edx, %eax + shll $1, %eax + addl %edx, %eax # eax = 3 * edx + + + .align 16 + .cascade_horlr_line_loop: + movq (%edi), %mm1 + movq 8(%edi), %mm2 + + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + movq (%ebx,%eax,1), %mm3 + + transpose_4x4 + + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + + coupled + + movq %mm0, (%ebx) + movq (%ebx,%edx,1), %mm3 + movq %mm3, %mm0 + + coupled + + movq %mm0, (%ebx, %edx,1) + movq (%ebx,%edx,2), %mm3 + movq %mm3, %mm0 + + coupled + + movq %mm0, (%ebx, %edx,2) + movq (%ebx,%eax,1), %mm3 + movq %mm3, %mm0 + + coupled + + movq %mm1, 0(%edi) # s1[k-1] + movq %mm2, 8(%edi) # s2[k-1] + + movq %mm0, %mm3 + movq (%ebx), %mm0 + movq (%ebx,%edx,1), %mm1 + movq (%ebx,%edx,2), %mm2 + + transpose_4x4 + + movq %mm0, (%ebx) + movq %mm1, (%ebx,%edx,1) + movq %mm2, (%ebx,%edx,2) + movq %mm3, (%ebx,%eax,1) + + addl $8, %ebx + decl %ecx + jnz .cascade_horlr_line_loop + + emms + + pop %edi + pop %esi + pop %ebx + leave + ret + + + diff --git a/system/mmx/pixel_cheby_s16.s b/system/mmx/pixel_cheby_s16.s new file mode 100644 index 0000000..2afe9e2 --- /dev/null +++ b/system/mmx/pixel_cheby_s16.s @@ -0,0 +1,90 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_cheby_s16_3plus +.type pixel_cheby_s16_3plus,@function + +# void pixel_cheby_s16(int *buf, int nb_8pixel_vectors, int order+1, short int *coefs) + + +# coefs are s2.13 fixed point (-4->4) +pixel_cheby_s16_3plus: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + push %edx + + movl 8(%ebp), %esi # input array + movl 12(%ebp), %ecx # vector count + movl 16(%ebp), %eax # get order+1 + + shll $3, %eax + movl 20(%ebp), %edx + addl %eax, %edx # edx = coef endx address + +# jmp skip + + .align 16 + .loop_cheby: + + movl 20(%ebp), %edi # get coefs + movq (%esi), %mm0 # load 4 pixels from memory (mm0 = x) + pcmpeqw %mm2, %mm2 + movq %mm0, %mm1 # mm1 (T_n-1) <- x + psrlw $1, %mm2 # mm2 (T_n-2) <- 1 + + + movq (%edi), %mm4 # mm4 (acc) == a0 + psraw $1, %mm4 # mm4 == a0/2 + movq %mm0, %mm5 # mm5 (intermediate) + pmulhw 8(%edi), %mm5 # mm5 == (x * a1)/2 + paddsw %mm5, %mm4 # acc = c0 + c1 x + addl $16, %edi + + .loop_cheby_inner: + movq %mm1, %mm3 # mm3 == T_n-1 + psraw $2, %mm2 # mm2 == T_n-2 / 4 + pmulhw %mm0, %mm3 # mm3 == (2 x T_n-1) / 4 + psubsw %mm2, %mm3 # mm3 == (2 x T_n-1 - T_n-2) / 4 + paddsw %mm3, %mm3 + paddsw %mm3, %mm3 # mm3 == T_n + movq %mm1, %mm2 # mm2 == new T_n-1 + movq %mm3, %mm1 # mm3 == new T_n-2 + pmulhw (%edi), %mm3 # mm3 = a_n * T_n / 2 + paddsw %mm3, %mm4 # accumulate + addl $8, %edi + cmpl %edx, %edi + jne .loop_cheby_inner + + paddsw %mm4, %mm4 # compensate for 0.125 factor + paddsw %mm4, %mm4 + paddsw %mm4, %mm4 + movq %mm4, (%esi) # store result in memory + addl $8, %esi # increment source/dest pointer + decl %ecx + jnz .loop_cheby # loop + +skip: + emms + + pop %edx + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_conv_hor_s16.s b/system/mmx/pixel_conv_hor_s16.s new file mode 100644 index 0000000..e90a692 --- /dev/null +++ b/system/mmx/pixel_conv_hor_s16.s @@ -0,0 +1,134 @@ +# Pure Data Packet mmx routine. +# 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. +# + # intermediate function + + # input in register: + # %mm0: left 4 pixels + # %mm1: middle 4 pixels + # %mm2: right 4 pixels + + # %mm5: left 4 pixel masks + # %mm6: middle 4 pixel masks + # %mm7: right 4 pixel masks + + # return in register: + # %mm0: middle 4 pixels result + + + .conv_hor_4_pixels: + .align 16 + + # compute quadruplet + + # get left pixels + psrlq $48, %mm0 # shift word 3 to byte 0 + movq %mm1, %mm4 + psllq $16, %mm4 # shift word 0,1,2 to 1,2,3 + por %mm4, %mm0 # combine + pmulhw %mm5, %mm0 + psllw $1, %mm0 + + + # get middle pixels + movq %mm1, %mm4 + pmulhw %mm6, %mm4 + psllw $1, %mm4 + paddsw %mm4, %mm0 + + + # get right pixels + movq %mm2, %mm3 + psllq $48, %mm3 # shift word 0 to word 3 + movq %mm1, %mm4 + psrlq $16, %mm4 # shift word 1,2,3 to 0,1,2 + por %mm4, %mm3 # combine + pmulhw %mm7, %mm3 + psllw $1, %mm3 + paddsw %mm3, %mm0 # accumulate + + ret + +.globl pixel_conv_hor_s16 +.type pixel_conv_hor_s16,@function + + +# pixel_conv_hor_s16(short int *pixel_array, int nb_4_pixel_vectors, short int border[4], short int mask[12]) +# horizontal unsigned pixel conv (1/4 1/2 1/4) not tested +# NOT TESTED + + +pixel_conv_hor_s16: + + + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 8(%ebp), %esi # pixel array offset + movl 12(%ebp), %ecx # nb of 8 pixel vectors in a row (at least 2) + + movl 20(%ebp), %edi # mask vector + movq (%edi), %mm5 + movq 8(%edi), %mm6 + movq 16(%edi), %mm7 + + movl 16(%ebp), %edi # boundary pixel vector + + + + movq (%edi), %mm0 # init regs (left edge, so mm0 is zero) + movq (%esi), %mm1 + movq 8(%esi), %mm2 + + decl %ecx # loop has 2 terminator stubs + decl %ecx # todo: handle if ecx < 3 + + jmp .conv_line_loop + + + .align 16 + .conv_line_loop: + call .conv_hor_4_pixels # compute conv + movq %mm0, (%esi) # store result + movq %mm1, %mm0 # mm0 <- prev (%esi) + movq %mm2, %mm1 # mm1 <- 8(%esi) + movq 16(%esi), %mm2 # mm2 <- 16(%esi) + + addl $8, %esi # increase pointer + decl %ecx + jnz .conv_line_loop + + call .conv_hor_4_pixels # compute conv + movq %mm0, (%esi) # store result + movq %mm1, %mm0 # mm0 <- prev (%esi) + movq %mm2, %mm1 # mm1 <- 8(%esi) + movq (%edi), %mm2 # mm2 <- border + + call .conv_hor_4_pixels # compute last vector + movq %mm0, 8(%esi) # store it + + emms + + pop %edi + pop %esi + leave + ret + + + diff --git a/system/mmx/pixel_conv_ver_s16.s b/system/mmx/pixel_conv_ver_s16.s new file mode 100644 index 0000000..ae2456f --- /dev/null +++ b/system/mmx/pixel_conv_ver_s16.s @@ -0,0 +1,128 @@ +# Pure Data Packet mmx routine. +# 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. +# +#TODO: fix out of bound acces in conv_ver and conv_hor + + # intermediate function + + # input in register: + # %mm0: top 4 pixels + # %mm1: middle 4 pixels + # %mm2: bottom 4 pixels + + # %mm5: top 4 pixel mask + # %mm6: middle 4 pixel mask + # %mm7: bottom 4 pixel mask + + # return in register: + # %mm0: middle 4 pixels result + + + .conv_ver_4_pixels: + .align 16 + + # compute quadruplet + + # get top pixel + pmulhw %mm5, %mm0 + psllw $1, %mm0 + + # get middle pixel + movq %mm1, %mm4 + pmulhw %mm6, %mm4 + psllw $1, %mm4 + paddsw %mm4, %mm0 + + # get bottom pixel + movq %mm2, %mm3 + pmulhw %mm7, %mm3 + psllw $1, %mm3 # mm3 <- mm3/4 + paddsw %mm3, %mm0 + + ret + +.globl pixel_conv_ver_s16 +.type pixel_conv_ver_s16,@function + + +# pixel_conv_ver_s16(short int *pixel_array, int nb_4_pixel_vectors, int row_byte_size, short int border[4]) +# horizontal unsigned pixel conv (1/4 1/2 1/4) not tested +# NOT TESTED + + +pixel_conv_ver_s16: + + + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 8(%ebp), %esi # pixel array offset + movl 12(%ebp), %ecx # nb of 4 pixel vectors in a row (at least 2) + movl 16(%ebp), %edx # rowsize in bytes + + movl 24(%ebp), %edi # mask vector + movq (%edi), %mm5 + movq 8(%edi), %mm6 + movq 16(%edi), %mm7 + + movl 20(%ebp), %edi # edge vector + + + shll $1, %edx + decl %ecx # loop has a terminator stub + decl %ecx # loop has another terminator stub + + + movq (%edi), %mm0 # init regs (left edge, so mm0 is zero) + movq (%esi), %mm1 + movq (%esi,%edx,1), %mm2 + jmp .conv_line_loop + + + .align 16 + .conv_line_loop: + call .conv_ver_4_pixels # compute conv + movq %mm0, (%esi) # store result + movq %mm1, %mm0 # mm0 <- prev (%esi) + movq %mm2, %mm1 # mm1 <- (%esi,%edx,1) + movq (%esi,%edx,2), %mm2 # mm2 <- (%esi,%edx,2) + + addl %edx, %esi # increase pointer + decl %ecx + jnz .conv_line_loop + + call .conv_ver_4_pixels # compute conv + movq %mm0, (%esi) # store result + movq %mm1, %mm0 # mm0 <- prev (%esi) + movq %mm2, %mm1 # mm1 <- (%esi,%edx,1) + movq (%edi), %mm2 # clear invalid edge vector + + addl %edx, %esi # increase pointer + call .conv_ver_4_pixels # compute last vector + movq %mm0, (%esi) # store it + + emms + + pop %edi + pop %esi + leave + ret + + + diff --git a/system/mmx/pixel_crot_s16.s b/system/mmx/pixel_crot_s16.s new file mode 100644 index 0000000..2427869 --- /dev/null +++ b/system/mmx/pixel_crot_s16.s @@ -0,0 +1,153 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_crot3d_s16 +.type pixel_crot3d_s16,@function + + +# 3 dimensional colour space rotation +# 3x3 matrix is column encoded, each coefficient is a 4x16 bit fixed point vector + +# void pixel_crot3d_s16(int *buf, int nb_4pixel_vectors_per_plane, short int *matrix) + +pixel_crot3d_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + + movl 8(%ebp), %esi # input array + movl 12(%ebp), %ecx # pixel count + movl 16(%ebp), %edi # rotation matrix + movl %ecx, %edx + shll $3, %edx # %edx = plane spacing + + + .align 16 + .loop_crot3d: + + movq (%esi), %mm0 # get 1st component + movq (%esi,%edx,1), %mm6 # get 2nd component + movq (%esi,%edx,2), %mm7 # get 3rd component + + movq %mm0, %mm1 # copy 1st component + movq %mm0, %mm2 + + pmulhw (%edi), %mm0 # mul first column + pmulhw 8(%edi), %mm1 + pmulhw 16(%edi), %mm2 + + movq %mm6, %mm5 # copy 2nd component + movq %mm6, %mm3 + + pmulhw 24(%edi), %mm6 # mul second column + pmulhw 32(%edi), %mm5 + pmulhw 40(%edi), %mm3 + + paddsw %mm6, %mm0 # accumulate + paddsw %mm5, %mm1 + paddsw %mm3, %mm2 + + movq %mm7, %mm4 # copy 3rd component + movq %mm7, %mm6 + + pmulhw 48(%edi), %mm4 # mul third column + pmulhw 56(%edi), %mm6 + pmulhw 64(%edi), %mm7 + + paddsw %mm4, %mm0 # accumulate + paddsw %mm6, %mm1 + paddsw %mm7, %mm2 + + paddsw %mm0, %mm0 # double (fixed point normalization) + paddsw %mm1, %mm1 + paddsw %mm2, %mm2 + + movq %mm0, (%esi) # store + movq %mm1, (%esi, %edx, 1) + movq %mm2, (%esi, %edx, 2) + + addl $8, %esi # increment source pointer + decl %ecx + jnz .loop_crot3d # loop + + emms + + pop %edi + pop %esi + leave + ret + + +.globl pixel_crot2d_s16 +.type pixel_crot2d_s16,@function + +# 2 dimensional colour space rotation +# 2x2 matrix is column encoded, each coefficient is a 4x16 bit fixed point vector + +# void pixel_crot2d_s16(int *buf, int nb_4pixel_vectors_per_plane, short int *matrix) + +pixel_crot2d_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + + movl 8(%ebp), %esi # input array + movl 12(%ebp), %ecx # pixel count + movl 16(%ebp), %edi # rotation matrix + movl %ecx, %edx + shll $3, %edx # %edx = plane spacing + + + .align 16 + .loop_crot2d: + + movq (%esi), %mm0 # get 1st component + movq (%esi,%edx,1), %mm2 # get 2nd component + + movq %mm0, %mm1 # copy 1st component + movq %mm2, %mm3 # copy 2nd component + + pmulhw (%edi), %mm0 # mul first column + pmulhw 8(%edi), %mm1 + + pmulhw 16(%edi), %mm2 # mul second column + pmulhw 24(%edi), %mm3 + + paddsw %mm2, %mm0 # accumulate + paddsw %mm3, %mm1 + + paddsw %mm0, %mm0 # fixed point gain correction + paddsw %mm1, %mm1 + + movq %mm0, (%esi) # store + movq %mm1, (%esi, %edx, 1) + + addl $8, %esi # increment source pointer + decl %ecx + jnz .loop_crot2d # loop + + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_gain.s b/system/mmx/pixel_gain.s new file mode 100644 index 0000000..5cd5057 --- /dev/null +++ b/system/mmx/pixel_gain.s @@ -0,0 +1,83 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_gain +.type pixel_gain,@function + +# mmx rgba pixel gain +# void asmtest(char *pixelarray, int32 nbpixels, int *rgba_gain) +# gains are 7.9 fixed point for rgba + +pixel_gain: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 8(%ebp), %esi # pixel array offset + movl 12(%ebp), %ecx # nb of elements + movl 16(%ebp), %edi # int16[4] array of gains + + prefetch (%esi) + + emms + sarl $2, %ecx # process 4 pixels per loop iteration + jz .exit + movq (%edi), %mm7 # read gain array from memory + jmp .loop_gain + + .align 16 + .loop_gain: + + prefetch 128(%esi) + movq (%esi), %mm5 # load pixel 1-2 from memory + movq 8(%esi), %mm6 # load pixel 3-4 from memory + pxor %mm0, %mm0 # zero mm0 - mm3 + pxor %mm1, %mm1 + pxor %mm2, %mm2 + pxor %mm3, %mm3 + punpcklbw %mm5, %mm0 # unpack 1st pixel into 8.8 bit ints + punpckhbw %mm5, %mm1 # unpack 2nd + punpcklbw %mm6, %mm2 # unpack 3rd + punpckhbw %mm6, %mm3 # unpack 4th + psrlw $0x1, %mm0 # shift right to clear sign bit 9.7 + psrlw $0x1, %mm1 + psrlw $0x1, %mm2 + psrlw $0x1, %mm3 + + pmulhw %mm7, %mm0 # multiply 1st pixel 9.7 * 7.9 -> 16.0 + pmulhw %mm7, %mm1 # multiply 2nd + pmulhw %mm7, %mm2 # multiply 3rd + pmulhw %mm7, %mm3 # multiply 4th + + packuswb %mm1, %mm0 # pack & saturate to 8bit vector + movq %mm0, (%esi) # store result in memory + packuswb %mm3, %mm2 # pack & saturate to 8bit vector + movq %mm2, 8(%esi) # store result in memory + + addl $16, %esi # increment source pointer + decl %ecx + jnz .loop_gain # loop + + .exit: + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_gain_s16.s b/system/mmx/pixel_gain_s16.s new file mode 100644 index 0000000..adcfdf5 --- /dev/null +++ b/system/mmx/pixel_gain_s16.s @@ -0,0 +1,71 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_gain_s16 +.type pixel_gain_s16,@function + +# gain is integer, shift count is down +# void pixel_gain_s16(int *buf, int nb_8pixel_vectors, short int gain[4], unsigned long long *shift) + +pixel_gain_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 20(%ebp), %edi + movq (%edi), %mm6 # get shift vector + + movl 16(%ebp), %edi + movq (%edi), %mm7 # get gain vector + + movl 8(%ebp), %esi # input array + movl 12(%ebp), %ecx # pixel count + + + .align 16 + .loop_gain: + + movq (%esi), %mm0 # load 4 pixels from memory + movq %mm0, %mm1 + pmulhw %mm7, %mm1 # apply gain (s15.0) fixed point, high word + pmullw %mm7, %mm0 # low word + + movq %mm0, %mm2 # copy + movq %mm1, %mm3 + + punpcklwd %mm1, %mm0 # unpack lsw components + punpckhwd %mm3, %mm2 # unpack msw components + + psrad %mm6, %mm0 # apply signed shift + psrad %mm6, %mm2 + + packssdw %mm2, %mm0 # pack result & saturate + movq %mm0, (%esi) # store result + + + addl $8, %esi # increment source pointer + decl %ecx + jnz .loop_gain # loop + + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_mix_s16.s b/system/mmx/pixel_mix_s16.s new file mode 100644 index 0000000..9bf41eb --- /dev/null +++ b/system/mmx/pixel_mix_s16.s @@ -0,0 +1,68 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_mix_s16 +.type pixel_mix_s16,@function + +# mmx rgba pixel gain +# void pixel_mix_s16(int *left, int *right, int nb_4pixel_vectors, +# short int gain_left[4], short int gain_right[4]) + +pixel_mix_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 20(%ebp), %edi # int16[4] array of gains + movq (%edi), %mm6 # get left gain array + + movl 24(%ebp), %edi # int16[4] array of gains + movq (%edi), %mm7 # get right gain array + + movl 8(%ebp), %edi # left array + movl 12(%ebp), %esi # right array + movl 16(%ebp), %ecx # pixel count + + + .align 16 + .loop_mix: + +# prefetch 128(%esi) + movq (%esi), %mm1 # load right 4 pixels from memory + pmulhw %mm7, %mm1 # apply right gain + movq (%edi), %mm0 # load 4 left pixels from memory + pmulhw %mm6, %mm0 # apply left gain +# pslaw $1, %mm1 # shift left ((s).15 x (s).15 -> (s0).14)) +# pslaw $1, %mm0 + paddsw %mm0, %mm0 # no shift left arithmic, so use add instead + paddsw %mm1, %mm1 + paddsw %mm1, %mm0 # mix + movq %mm0, (%edi) + addl $8, %esi + addl $8, %edi + decl %ecx + jnz .loop_mix # loop + + emms + + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_mul_s16.s b/system/mmx/pixel_mul_s16.s new file mode 100644 index 0000000..240a024 --- /dev/null +++ b/system/mmx/pixel_mul_s16.s @@ -0,0 +1,56 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_mul_s16 +.type pixel_mul_s16,@function + +# simple add +# void pixel_mul_s16(int *left, int *right, int nb_4pixel_vectors) + +pixel_mul_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 8(%ebp), %edi # left array + movl 12(%ebp), %esi # right array + movl 16(%ebp), %ecx # pixel count + + + .align 16 + .loop_mix: + +# prefetch 128(%esi) + movq (%esi), %mm1 # load right 4 pixels from memory + movq (%edi), %mm0 # load 4 left pixels from memory + pmulhw %mm1, %mm0 # mul + psllw $1, %mm0 # fixed point shift correction + movq %mm0, (%edi) + addl $8, %esi + addl $8, %edi + decl %ecx + jnz .loop_mix # loop + + emms + + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_pack_s16u8.s b/system/mmx/pixel_pack_s16u8.s new file mode 100644 index 0000000..57df702 --- /dev/null +++ b/system/mmx/pixel_pack_s16u8.s @@ -0,0 +1,126 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_pack_s16u8_y +.type pixel_pack_s16u8_y,@function + +# mmx rgba pixel gain +# void pixel_pack_s16u8_y(int *input, int *output, int nb_8pixel_vectors) + +pixel_pack_s16u8_y: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + +# movl 20(%ebp), %edi # int16[4] array of gains +# movq (%edi), %mm7 # get gain array +# psllw $1, %mm7 # adjust for shifted sign bit + + movl 8(%ebp), %esi # input array + movl 12(%ebp), %edi # output array + movl 16(%ebp), %ecx # pixel count + + pxor %mm6, %mm6 + + .align 16 + .loop_pack_y: + +# prefetch 128(%esi) + movq (%esi), %mm0 # load 4 pixels from memory +# pmulhw %mm7, %mm0 # apply gain + movq 8(%esi), %mm1 # load 4 pixels from memory +# pmulhw %mm7, %mm1 # apply gain + +# movq %mm0, %mm2 +# pcmpgtw %mm6, %mm2 # mm2 > 0 ? 0xffff : 0 +# pand %mm2, %mm0 + +# movq %mm1, %mm3 +# pcmpgtw %mm6, %mm3 # mm3 > 0 ? 0xffff : 0 +# pand %mm3, %mm1 + +# psllw $1, %mm0 # shift out sign bit +# psllw $1, %mm1 # shift out sign bit + + psraw $7, %mm0 # shift to lsb + psraw $7, %mm1 # shift to lsb + + packuswb %mm1, %mm0 # pack & saturate to 8bit vector + movq %mm0, (%edi) # store result in memory + + addl $16, %esi # increment source pointer + addl $8, %edi # increment dest pointer + decl %ecx + jnz .loop_pack_y # loop + + emms + + pop %edi + pop %esi + leave + ret + +.globl pixel_pack_s16u8_uv +.type pixel_pack_s16u8_uv,@function + +pixel_pack_s16u8_uv: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + +# movl 20(%ebp), %edi # int16[4] array of gains +# movq (%edi), %mm7 # get gain array + movl 8(%ebp), %esi # pixel array offset + movl 12(%ebp), %edi # nb of elements + movl 16(%ebp), %ecx # pixel count + + pcmpeqw %mm6, %mm6 + psllw $15, %mm6 + movq %mm6, %mm5 + psrlw $8, %mm5 + por %mm5, %mm6 # mm6 <- 8 times 0x80 + + .align 16 + .loop_pack_uv: + +# prefetch 128(%esi) + movq (%esi), %mm0 # load 4 pixels from memory +# pmulhw %mm7, %mm0 # apply gain + movq 8(%esi), %mm1 # load 4 pixels from memory +# pmulhw %mm7, %mm1 # apply gain + + psraw $8, %mm0 # shift to msb + psraw $8, %mm1 + + packsswb %mm1, %mm0 # pack & saturate to 8bit vector + pxor %mm6, %mm0 # flip sign bits + movq %mm0, (%edi) # store result in memory + + addl $16, %esi # increment source pointer + addl $8, %edi # increment dest pointer + decl %ecx + jnz .loop_pack_uv # loop + + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_rand_s16.s b/system/mmx/pixel_rand_s16.s new file mode 100644 index 0000000..649400b --- /dev/null +++ b/system/mmx/pixel_rand_s16.s @@ -0,0 +1,76 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_rand_s16 +.type pixel_rand_s16,@function + +# mmx rgba pixel gain +# void pixel_rand_s16(int *dst, nb_4pixel_vectors, short int random_seed[4]) + +pixel_rand_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 16(%ebp), %esi # int16[4] array of random seeds + movl 8(%ebp), %edi # dst array + movl 12(%ebp), %ecx # pixel count + + movq (%esi), %mm6 + + + pcmpeqw %mm3, %mm3 + psrlw $15, %mm3 # get bit mask 4 times 0x0001 + + .align 16 + .loop_rand: + +# prefetch 128(%esi) + + + movq %mm6, %mm4 # get random vector + psrlw $15, %mm4 # get first component + movq %mm6, %mm5 + psrlw $14, %mm5 # get second component + pxor %mm5, %mm4 + movq %mm6, %mm5 + psrlw $12, %mm5 # get third component + pxor %mm5, %mm4 + movq %mm6, %mm5 + psrlw $3, %mm5 # get forth component + pxor %mm5, %mm4 + + psllw $1, %mm6 # shift left original random vector + pand %mm3, %mm4 # isolate new bit + por %mm4, %mm6 # combine into new random vector + + movq %mm6, (%edi) + addl $8, %edi + decl %ecx + jnz .loop_rand # loop + + + movq %mm6, (%esi) # store random seeds + + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_randmix_s16.s b/system/mmx/pixel_randmix_s16.s new file mode 100644 index 0000000..44e1702 --- /dev/null +++ b/system/mmx/pixel_randmix_s16.s @@ -0,0 +1,91 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_randmix_s16 +.type pixel_randmix_s16,@function + +# mmx rgba pixel gain +# void pixel_randmix_s16(int *left, int *right, int nb_4pixel_vectors, short int random_seed[4], short int threshold[4]) + +pixel_randmix_s16: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 20(%ebp), %edi # int16[4] array of random seeds + movq (%edi), %mm6 + + movl 24(%ebp), %edi # int16[4] array of thresholds + movq (%edi), %mm7 + + movl 8(%ebp), %edi # left array + movl 12(%ebp), %esi # right array + movl 16(%ebp), %ecx # pixel count + + pcmpeqw %mm3, %mm3 + psrlw $15, %mm3 # get bit mask 4 times 0x0001 + + .align 16 + .loop_randmix: + +# prefetch 128(%esi) + movq (%esi), %mm1 # load right 4 pixels from memory + movq (%edi), %mm0 # load 4 left pixels from memory + + movq %mm6, %mm2 # get random vector + pcmpgtw %mm7, %mm2 # compare random vector with threshold + movq %mm2, %mm5 + + pand %mm0, %mm2 # get left array's components + pandn %mm1, %mm5 # get right array's components + por %mm2, %mm5 + + movq %mm5, (%edi) # store pixels + + movq %mm6, %mm4 # get random vector + psrlw $15, %mm4 # get first component + movq %mm6, %mm5 + psrlw $14, %mm5 # get second component + pxor %mm5, %mm4 + movq %mm6, %mm5 + psrlw $12, %mm5 # get third component + pxor %mm5, %mm4 + movq %mm6, %mm5 + psrlw $3, %mm5 # get forth component + pxor %mm5, %mm4 + + psllw $1, %mm6 # shift left original random vector + pand %mm3, %mm4 # isolate new bit + por %mm4, %mm6 # combine into new random vector + + addl $8, %esi + addl $8, %edi + decl %ecx + jnz .loop_randmix # loop + + + movl 20(%ebp), %edi # int16[4] array of random seeds + movq %mm6, (%edi) # store random seeds + + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_resample_s16.s b/system/mmx/pixel_resample_s16.s new file mode 100644 index 0000000..3959f9c --- /dev/null +++ b/system/mmx/pixel_resample_s16.s @@ -0,0 +1,314 @@ + + +#interpolation data: +#* 4 vectors: neighbourhood for samples (TL, TR, BL, BR) +#* 2 vectors: fractional part (unsigned) +#* 2 vectors: addresses of pixel blocks + +#coord conversion data: +#1 vector: 32bit splatted address +#1 vector: 16bit splatted w-1 +#1 vector: 16bit splatted h-1 +#1 vector: 16bit splatted w (reuse w-1 with add?) +#1 dword: 32 bit line offset + +#coord generation data: several vectors for parameter update stuff.. + +#coordinate systems: 16 bit virtual coordinates (signed, center relative) +#* 2 vectors: virtual coordinates +#(evt tussenstap + conversie naar 16 bit virtual) + + +#step 1: generate virtual coords + + +#step 2: virtual coords -> block adresses + fractional adresses +#* mulhigh: real coords (x,y) (center relative) +#* add center -> unsigned (top left relative) +#* mullow: fractional part (x_frac, y_frac) +#* mulhigh, mullow, pack 32bit: y_offset +#* pack 32bit: x_offset +#* add, shift, add start address: real addresses + + +#step3: data fetch using generated addresses: +# this step would be much simpler in 4x16bit rgba. life's a bitch.. + +#step4: billinear interpolation + +#stat5: store + + + + # this can be simplified by doing 32 bit unaligned moves + # and vector unpacking on the data + + + + # cooked image data structure + # pixel environment temp storage + TL1 = 0x00 + TL2 = 0x02 + TL3 = 0x04 + TL4 = 0x06 + TR1 = 0x08 + TR2 = 0x0A + TR3 = 0x0C + TR4 = 0x0E + BL1 = 0x10 + BL2 = 0x12 + BL3 = 0x14 + BL4 = 0x16 + BR1 = 0x18 + BR2 = 0x1A + BR3 = 0x1C + BR4 = 0x1E + # addresses of pixel blocks + ADDRESS1 = 0x20 + ADDRESS2 = 0x24 + ADDRESS3 = 0x28 + ADDRESS4 = 0x2C + + # second env + address buffer (testing: not used) + SECONDBUFFER = 0x30 + + # 32bit splatted bitmap address + V2PLANEADDRESS = 0x60 + # 16bit splatted image constants + V4TWOWIDTHM1 = 0x68 + V4TWOHEIGHTM1 = 0x70 + V4LINEOFFSET = 0x78 + # data struct size + RESAMPLEDATASIZE = 0x80 + + + + # interpolation routine + # input: %mm0, %mm1 4 x 16bit unsigned top left relative virtual x and y coordinates + # %esi: temp & algo data structure + +getpixelsbilin: psrlw $1, %mm0 # convert to range 0->0x7fff [0,0.5[ + psrlw $1, %mm1 + movq %mm0, %mm2 + movq %mm1, %mm3 + movq V4TWOWIDTHM1(%esi), %mm4 # 2 * (width - 1) + movq V4TWOHEIGHTM1(%esi), %mm5 # 2 * (height - 1) + pmulhw %mm5, %mm3 # mm3 == y coord (topleft relative) + pmulhw %mm4, %mm2 # mm2 == x coord (topleft relative) + pmullw %mm5, %mm1 # mm1 == y frac (unsigned) + pmullw %mm4, %mm0 # mm0 == x frac (unsigned) + + movq %mm3, %mm5 # copy y coord + pmullw V4LINEOFFSET(%esi), %mm3 # low part of line offset + pmulhw V4LINEOFFSET(%esi), %mm5 # high part of line offset + + movq %mm2, %mm7 # copy x coord vector + pxor %mm4, %mm4 + punpcklwd %mm4, %mm2 # low part in %mm2 + punpckhwd %mm4, %mm7 # hight part in %mm7 + + movq %mm3, %mm6 # copy + punpcklwd %mm5, %mm3 # unpack low part in %mm3 + punpckhwd %mm5, %mm6 # high part int %mm6 + + paddd %mm2, %mm3 + paddd %mm7, %mm6 + pslld $1, %mm3 # convert to word adresses + pslld $1, %mm6 + + paddd V2PLANEADDRESS(%esi), %mm3 # add pixel plane address + paddd V2PLANEADDRESS(%esi), %mm6 + + movq %mm3, ADDRESS1(%esi) # store adresses + movq %mm6, ADDRESS3(%esi) + + pcmpeqw %mm2, %mm2 # all ones + movq %mm0, %mm4 # copy x frac + movq %mm1, %mm5 # copy y frac + pxor %mm2, %mm4 # compute compliment (approx negative) + pxor %mm2, %mm5 + + psrlw $1, %mm0 # shift right (0.5 * (frac x) + psrlw $1, %mm1 # shift right (0.5 * (frac y) + psrlw $1, %mm4 # shift right (0.5 * (1 - frac x) + psrlw $1, %mm5 # shift right (0.5 * (1 - frac y) + + movq %mm0, %mm2 # copy of frac x + movq %mm4, %mm3 # copy of (1-frac x) + # fetch data + + #jmp skipfetch # seems the fetch is the real killer. try to optimize this + # using 32 bit accesses & shifts + + # the src image data struct is padded to the cooked data struct + movl RESAMPLEDATASIZE(%esi), %edi + shll $1, %edi + + movl ADDRESS1(%esi), %ecx + movl ADDRESS2(%esi), %edx + + movw (%ecx), %ax + movw (%edx), %bx + movw %ax, TL1(%esi) + movw %bx, TL2(%esi) + movw 2(%ecx), %ax + movw 2(%edx), %bx + movw %ax, TR1(%esi) + movw %bx, TR2(%esi) + + addl %edi, %ecx + addl %edi, %edx + + movw (%ecx), %ax + movw (%edx), %bx + movw %ax, BL1(%esi) + movw %bx, BL2(%esi) + movw 2(%ecx), %ax + movw 2(%edx), %bx + movw %ax, BR1(%esi) + movw %bx, BR2(%esi) + + + movl ADDRESS3(%esi), %ecx + movl ADDRESS4(%esi), %edx + + + movw (%ecx), %ax + movw (%edx), %bx + movw %ax, TL3(%esi) + movw %bx, TL4(%esi) + movw 2(%ecx), %ax + movw 2(%edx), %bx + movw %ax, TR3(%esi) + movw %bx, TR4(%esi) + + addl %edi, %ecx + addl %edi, %edx + + movw (%ecx), %ax + movw (%edx), %bx + movw %ax, BL3(%esi) + movw %bx, BL4(%esi) + movw 2(%ecx), %ax + movw 2(%edx), %bx + movw %ax, BR3(%esi) + movw %bx, BR4(%esi) + + +skipfetch: + pmulhw TL1(%esi), %mm4 # bilin interpolation + pmulhw TR1(%esi), %mm0 + pmulhw BL1(%esi), %mm3 + pmulhw BR1(%esi), %mm2 + + + paddw %mm4, %mm0 + paddw %mm3, %mm2 + + pmulhw %mm5, %mm0 + pmulhw %mm1, %mm2 + + paddw %mm2, %mm0 + psllw $2, %mm0 # compensate for gain reduction + + ret + + + // linear mapping data struct + ROWSTATEX = 0x0 + ROWSTATEY = 0x8 + COLSTATEX = 0x10 + COLSTATEY = 0x18 + ROWINCX = 0x20 + ROWINCY = 0x28 + COLINCX = 0x30 + COLINCY = 0x38 + + // image data struct + LINEOFFSET = 0x0 + IMAGEADDRESS = 0x4 + WIDTH = 0x8 + HEIGHT = 0xC + IMAGEDATASIZE = 0x10 + + + +# pixel_resample_linmap_s16(void *x) +.globl pixel_resample_linmap_s16 +.type pixel_resample_linmap_s16,@function + + SOURCEIMAGE = RESAMPLEDATASIZE + DESTIMAGE = SOURCEIMAGE + IMAGEDATASIZE + LINMAPDATA = DESTIMAGE + IMAGEDATASIZE + +pixel_resample_linmap_s16: + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + pushl %ebx + + + movl 8(%ebp), %esi # get data struct + movl DESTIMAGE+HEIGHT(%esi), %edx # image height + movl DESTIMAGE+IMAGEADDRESS(%esi), %edi # dest image address + movl DESTIMAGE+WIDTH(%esi), %ecx # image width + shrl $2, %ecx # vector count + .align 16 + +linmap_looprow: + movq LINMAPDATA+ROWSTATEX(%esi), %mm0 # get current coordinates + movq LINMAPDATA+ROWSTATEY(%esi), %mm1 + +linmap_loopcol: + movq %mm0, %mm4 # copy + movq %mm1, %mm5 + paddd LINMAPDATA+ROWINCX(%esi), %mm4 # increment + paddd LINMAPDATA+ROWINCY(%esi), %mm5 + movq %mm4, %mm6 # copy + movq %mm5, %mm7 + paddd LINMAPDATA+ROWINCX(%esi), %mm6 # increment + paddd LINMAPDATA+ROWINCY(%esi), %mm7 + movq %mm6, LINMAPDATA+ROWSTATEX(%esi) # store next state + movq %mm7, LINMAPDATA+ROWSTATEY(%esi) + + psrad $16, %mm0 # round to 16 bit + psrad $16, %mm1 + psrad $16, %mm4 + psrad $16, %mm5 + packssdw %mm4, %mm0 # pack new coordinates + packssdw %mm5, %mm1 + + push %ecx + push %edx + push %edi + + call getpixelsbilin # do interpolation + + pop %edi + pop %edx + pop %ecx + movq %mm0, (%edi) # store 4 pixels + addl $0x8, %edi # point to next 4 pixels + decl %ecx # dec row counter + jnz linmap_looprow + + movq LINMAPDATA+COLSTATEX(%esi), %mm0 # get column state vector + movq LINMAPDATA+COLSTATEY(%esi), %mm1 + movl DESTIMAGE+WIDTH(%esi), %ecx # image width + shrl $2, %ecx # vector count + paddd LINMAPDATA+COLINCX(%esi), %mm0 # increment + paddd LINMAPDATA+COLINCY(%esi), %mm1 + movq %mm0, LINMAPDATA+COLSTATEX(%esi) # store + movq %mm1, LINMAPDATA+COLSTATEY(%esi) + decl %edx # dec column counter + jnz linmap_loopcol + + emms + popl %ebx + popl %edi + popl %esi + leave + ret + + diff --git a/system/mmx/pixel_s1.s b/system/mmx/pixel_s1.s new file mode 100644 index 0000000..d6bc5ca --- /dev/null +++ b/system/mmx/pixel_s1.s @@ -0,0 +1,201 @@ +# Pure Data Packet mmx routine. +# 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 ops for binary image processing + # 8x8 bit tile encoded + # low byte = bottom row + # low bit = right column + # %mm7 = scratch reg for all macros + + + # ************ load mask ******************* + # compute bit masks for rows and columns + # %mm7: scratch reg + + # load mask top + .macro ldmt count reg + pcmpeqb \reg, \reg + psllq $(64-(\count<<3)), \reg + .endm + + # load mask bottom + .macro ldmb count reg + pcmpeqb \reg, \reg + psrlq $(64-(\count<<3)), \reg + .endm + + # load mask top and bottom + .macro ldmtb count regt regb + ldmb \count, \regb + ldmt \count, \regt + .endm + + # load mask right + .macro ldmr count reg + pcmpeqb %mm7, %mm7 + psrlw $(16-\count), %mm7 + movq %mm7, \reg + psllq $8, %mm7 + por %mm7, \reg + .endm + + # load mask left + .macro ldml count reg + pcmpeqb %mm7, %mm7 + psllw $(16-\count), %mm7 + movq %mm7, \reg + psrlq $8, %mm7 + por %mm7, \reg + .endm + + # load mask left and right + .macro ldmlr count regl regr + pcmpeqb %mm7, %mm7 + psllw $(16-\count), %mm7 + movq %mm7, \regl + psrlq $8, %mm7 + por %mm7, \regl + movq \regl, \regr + psrlq $(8-\count), \regr + .endm + + # ************* shift square ********** + # shifts a square in reg, fills with zeros + + # shift square top + .macro sst count reg + psllq $(\count<<3), \reg + .endm + + # shift square bottom + .macro ssb count reg + psrlq $(\count<<3), \reg + .endm + + # not tested + # shift square left + .macro ssl count reg + movq \reg, %mm7 + pcmpeqb \reg, \reg + psllw $(16-\count), \reg + psrlw $8, \reg + pandn %mm7, \reg + psllw $(\count), \reg + .endm + + # shift square right + .macro ssr count reg + movq \reg, %mm7 + pcmpeqb \reg, \reg + psrlw $(16-\count), \reg + psllw $8, \reg + pandn %mm7, \reg + psrlw $(\count), \reg + .endm + + + # ********** combine square ************* + # combines 2 squares + + # combine right + .macro csr count regr reg + ssl \count, \reg + ssr (8-\count), \regr + por \regr, \reg + .endm + + # combine left + .macro csl count regl reg + ssr \count, \reg + ssl (8-\count), \regl + por \regl, \reg + .endm + + # combine top + .macro cst count regt reg + ssb \count, \reg + sst (8-\count), \regt + por \regt, \reg + .endm + + + # combine bottom + .macro csb count regb reg + sst \count, \reg + ssb (8-\count), \regb + por \regb, \reg + .endm + + + # ********** load combine square ************* + # loads combined square using mask + + # load combined square left + # mask should be count bits set right (i.e. 0x01) + .macro lcsml count mask source sourcel dstreg + movq \mask, \dstreg + movq \mask, %mm7 + pandn \source, \dstreg + pand \sourcel, %mm7 + psrlq $(\count), \dstreg + psllq $(8-\count), %mm7 + por %mm7, \dstreg + .endm + + + +.globl pixel_test_s1 +.type pixel_test_s1,@function + +# simple add +# void pixel_add_s16(void *dest, void *source, int nb_squares, int spacing) + + + + # + + +pixel_test_s1: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 8(%ebp), %edi # dest + movl 12(%ebp), %esi # source + movl 16(%ebp), %ecx # count + movl 20(%ebp), %edx # row distance + + ldmr 1, %mm6 + lcsml 1, %mm6, (%esi), 8(%esi), %mm0 + movq %mm0, (%edi) + + +# movq (%esi), %mm0 +# movq 8(%esi), %mm1 +# csl 4, %mm1, %mm0 +# movq %mm0, (%edi) + + emms + + + pop %edi + pop %esi + leave + ret + diff --git a/system/mmx/pixel_unpack_u8s16.s b/system/mmx/pixel_unpack_u8s16.s new file mode 100644 index 0000000..0fc14c2 --- /dev/null +++ b/system/mmx/pixel_unpack_u8s16.s @@ -0,0 +1,113 @@ +# Pure Data Packet mmx routine. +# 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. +# +.globl pixel_unpack_u8s16_y +.type pixel_unpack_u8s16_y,@function + +# mmx rgba pixel gain +# void pixel_unpack_u8s16_y(char *input, char *output, int32 nb_pixels_div8) + +pixel_unpack_u8s16_y: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + +# movl 20(%ebp), %edi # int16[4] array of gains +# movq (%edi), %mm7 # get gain array + + movl 8(%ebp), %esi # input uint8 pixel array + movl 12(%ebp), %edi # output sint16 pixel array + movl 16(%ebp), %ecx # nb of elements div 8 + + + .align 16 + .loop_unpack_y: + + movq (%esi), %mm5 # load 8 pixels from memory + pxor %mm0, %mm0 # zero mm0 - mm3 + pxor %mm1, %mm1 + punpcklbw %mm5, %mm0 # unpack 1st 4 pixels + punpckhbw %mm5, %mm1 # unpack 2nd 4 pixles + psrlw $0x1, %mm0 # shift right to clear sign bit 9.7 + psrlw $0x1, %mm1 +# pmulhw %mm7, %mm0 # apply gain +# pmulhw %mm7, %mm1 +# paddsw %mm0, %mm0 # correct factor 2 +# paddsw %mm1, %mm1 + movq %mm0, (%edi) # store + movq %mm1, 8(%edi) + + addl $8, %esi # increment source pointer + addl $16, %edi # increment dest pointer + decl %ecx + jnz .loop_unpack_y # loop + + emms + + pop %edi + pop %esi + leave + ret + +.globl pixel_unpack_u8s16_uv +.type pixel_unpack_u8s16_uv,@function +pixel_unpack_u8s16_uv: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + +# movl 20(%ebp), %edi # int16[4] array of gains +# movq (%edi), %mm7 # get gain array + + movl 8(%ebp), %esi # input uint8 pixel array + movl 12(%ebp), %edi # output sint16 pixel array + movl 16(%ebp), %ecx # nb of elements div 8 + + pcmpeqw %mm6, %mm6 + psllw $15, %mm6 + + .align 16 + .loop_unpack_uv: + + movq (%esi), %mm5 # load 8 pixels from memory + pxor %mm0, %mm0 # zero mm0 - mm3 + pxor %mm1, %mm1 + punpcklbw %mm5, %mm0 # unpack 1st 4 pixels + punpckhbw %mm5, %mm1 # unpack 2nd 4 pixles + pxor %mm6, %mm0 # flip sign bit (Cr and Cb are ofset by 128) + pxor %mm6, %mm1 +# pmulhw %mm7, %mm0 # apply gain +# pmulhw %mm7, %mm1 +# paddsw %mm0, %mm0 # correct factor 2 +# paddsw %mm1, %mm1 + movq %mm0, (%edi) # store + movq %mm1, 8(%edi) + + addl $8, %esi # increment source pointer + addl $16, %edi # increment dest pointer + decl %ecx + jnz .loop_unpack_uv # loop + + emms + + pop %edi + pop %esi + leave + ret + diff --git a/system/net/Makefile b/system/net/Makefile new file mode 100644 index 0000000..53d1d61 --- /dev/null +++ b/system/net/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = pdp_net.o + + +include ../../Makefile.config + +all: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o diff --git a/system/net/pdp_net.c b/system/net/pdp_net.c new file mode 100644 index 0000000..04c97d6 --- /dev/null +++ b/system/net/pdp_net.c @@ -0,0 +1,685 @@ + +#include "pdp_net.h" +#include "pdp_debug.h" +#include "pdp_post.h" +#include "pdp_mem.h" + +#define D if (0) // DEBUG MSG +#define DD if (0) // DROP DEBUG MSG + +/* shared internals */ + +static int _is_udp_header(t_pdp_udp_header *header, unsigned int size) +{ + if (size < sizeof(t_pdp_udp_header)) return 0; + if (strcmp(header->signature, "PDP")) return 0; + if (PDP_UDP_VERSION != header->version) return 0; + return 1; +} + +static void _make_udp_header(t_pdp_udp_header *header) +{ + strcpy(header->signature, "PDP"); + header->version = PDP_UDP_VERSION; +} + + + + +/* R E C E I V E R */ + + +/* INTERNALS */ + +static void _send_packet(t_pdp_udp_receiver *x) +{ + _make_udp_header(&x->x_resend_header); + PDP_ASSERT(x->x_resend_udp_packet_size <= sizeof(t_pdp_udp_header) + sizeof(x->x_resend_chunks)); + + /* send the packet */ + if (-1 == sendto (x->x_socket, &x->x_resend_header, x->x_resend_udp_packet_size, 0, + (struct sockaddr *)&x->x_source_socket, x->x_sslen)){ + pdp_post("pdp_netreceive: send failed"); + } +} + +static void _send_ack_new(t_pdp_udp_receiver *x) +{ + /* setup resend header */ + x->x_resend_header.connection_id = x->x_connection_id; + x->x_resend_header.sequence_number = PDP_UDP_ACKNEW; + x->x_resend_udp_packet_size = sizeof(t_pdp_udp_header); + + _send_packet(x); + +} + + +static int _handle_PDP_UDP_NEW(t_pdp_udp_receiver *x) +{ + /* we've got a PDP_UDP_NEW packet, so prepare to receive the data */ + t_pdp_udp_newpacket *np = (t_pdp_udp_newpacket *)x->x_buf; + + + //pdp_post("conn_id = %x", x->x_header.connection_id); + //pdp_post("size = %d", np->data_size); + //pdp_post("nb_chunks = %d", np->nb_chunks); + //pdp_post("chunk_size = %d", np->chunk_size); + //pdp_post("type = %s", np->type); + + /* check if it is a resend of the PDP_UDP_NEW packet (if NEW_ACK didn't get through) + if not, prepare for reception */ + + if (x->x_connection_id != x->x_header.connection_id){ + + + /* prepare for reception : TODO add some more checks here */ + + // setup type info + if (x->x_data_type) pdp_dealloc (x->x_data_type); + x->x_data_type = pdp_alloc(1 + strlen(np->type)); + strcpy(x->x_data_type, np->type); + + // setup data buffer + x->x_data_size = np->data_size; + if (x->x_data) pdp_dealloc (x->x_data); + x->x_data = pdp_alloc(x->x_data_size); + memset(x->x_data, 0, x->x_data_size); // clear for debug + + // setup connection info + x->x_connection_id = x->x_header.connection_id; + x->x_nb_chunks = np->nb_chunks; + x->x_chunk_size = np->chunk_size; + + /* setup chunk list */ + if (x->x_chunk_list) pdp_dealloc(x->x_chunk_list); + x->x_chunk_list = pdp_alloc(sizeof(unsigned int)*x->x_nb_chunks); + memset(x->x_chunk_list, 0, sizeof(unsigned int)*x->x_nb_chunks); + + x->x_receive_finished = 0; // we're in a receiving state + x->x_packet_transferred = 0; // we didn't pass the packet yet + } + + /* send ACK */ + _send_ack_new(x); + + + + return 1; +} + +static void _handle_PDP_UDP_DONE(t_pdp_udp_receiver *x) +{ + unsigned int chunk; + unsigned int missing; + unsigned int i; + unsigned int resend_packet_size; + + + /* check the connection id */ + if (x->x_connection_id != x->x_header.connection_id) return; + + /* determine how many packets are missing */ + missing = 0; + for (i=0; i<x->x_nb_chunks; i++) + if (!x->x_chunk_list[i]) missing++; + + D pdp_post ("last packet %x had %d/%d dropped chunks", x->x_connection_id, missing, x->x_nb_chunks); + + + /* build the resend request (chunk list )*/ + if (missing > RESEND_MAX_CHUNKS) missing = RESEND_MAX_CHUNKS; + chunk = 0; + i = missing; + while(i--){ + while (x->x_chunk_list[chunk]) chunk++; // find next missing chunk + x->x_resend_chunks[i] = chunk++; // store it in list + } + + /* set the packet size to include the list */ + x->x_resend_udp_packet_size = sizeof(t_pdp_udp_header) + + missing * sizeof(unsigned int); + + /* setup resend header */ + strcpy((char *)&x->x_resend_header, "PDP"); + x->x_resend_header.version = PDP_UDP_VERSION; + x->x_resend_header.connection_id = x->x_connection_id; + x->x_resend_header.sequence_number = PDP_UDP_RESEND; + + D pdp_post("pdp_netreceive: sending RESEND response for %u chunks", missing); + + /* send out */ + _send_packet(x); + + /* indicate we're done if there's no chunks missing */ + if (!missing) x->x_receive_finished = 1; + +} + + +static int _handle_UDP_DATA(t_pdp_udp_receiver *x) +{ + unsigned int seq = x->x_header.sequence_number; + unsigned int offset = x->x_chunk_size * seq; + + /* ignore the packet if we're not expecting it */ + if ((!x->x_connection_id) || (x->x_connection_id != x->x_header.connection_id)){ + //pdp_post("pdp_netreceive: got invalid data packet: transmission id %x is not part of current transmisson %x", + // x->x_header.connection_id, x->x_connection_id); + return 0; + } + + /* check if it is valid */ + if (seq >= x->x_nb_chunks){ + pdp_post("pdp_netreceive: got invalid data packet: sequence number %u out of bound (nb_chunks=%u)", + seq, x->x_nb_chunks); + return 0; + } + + /* final check */ + PDP_ASSERT(offset + x->x_buf_size <= x->x_data_size); + + /* write & log it */ + memcpy(x->x_data + offset, x->x_buf, x->x_buf_size); + x->x_chunk_list[seq] = 1; + return 1; + +} + +/* INTERFACE */ + +/* setup */ +t_pdp_udp_receiver *pdp_udp_receiver_new(int port) +{ + t_pdp_udp_receiver *x = pdp_alloc(sizeof(*x)); + memset(x, 0, sizeof(*x)); + + /* init */ + x->x_data = 0; + x->x_data_type = 0; + x->x_data_size = 0; + x->x_chunk_list = 0; + x->x_receive_finished = 0; + x->x_packet_transferred = 0; + x->x_zero_terminator = 0; + + x->x_socket = socket(PF_INET, SOCK_DGRAM, 0); + x->x_connection_id = 0; /* zero for bootstrap (0 == an invalid id) */ + x->x_sslen = sizeof(struct sockaddr_in); + + /* bind socket */ + x->x_sa.sin_port = htons(port); + x->x_sa.sin_addr.s_addr = 0; + if (-1 != bind (x->x_socket, (struct sockaddr *)&x->x_sa, + sizeof(struct sockaddr_in))) return x; + + /* suicide if find failed */ + else { + pdp_dealloc(x); + return 0; + } +} +void pdp_udp_receiver_free(t_pdp_udp_receiver *x) +{ + if (!x) return; + if (x->x_socket != 1) close (x->x_socket); + if (x->x_data) pdp_dealloc(x->x_data); + if (x->x_data_type) pdp_dealloc (x->x_data_type); + if (x->x_chunk_list) pdp_dealloc (x->x_chunk_list); +} + +void pdp_udp_receiver_reset(t_pdp_udp_receiver *x) +{ + x->x_connection_id = 0; +} + + +/* receive loop, returns 1 on success, -1 on error, 0 on timeout */ +int pdp_udp_receiver_receive(t_pdp_udp_receiver *x, unsigned int timeout_ms) +{ + /* listen for packets */ + + unsigned int size; + struct timeval tv = {0,1000 * timeout_ms}; + fd_set inset; + FD_ZERO(&inset); + FD_SET(x->x_socket, &inset); + switch(select (x->x_socket+1, &inset, NULL, NULL, &tv)){ + case -1: + return -1; /* select error */ + case 0: + return 0; /* select time out */ + default: + break; /* data ready */ + } + + /* this won't block, since there's data available */ + if (-1 == (int)(size = recvfrom(x->x_socket, (void *)&x->x_header, + PDP_UDP_BUFSIZE+sizeof(x->x_header), 0, + (struct sockaddr *)&x->x_source_socket, &x->x_sslen))) return -1; + + /* store the data size of the packet */ + x->x_buf_size = size - sizeof(t_pdp_udp_header); + + /* parse the udp packet */ + if (_is_udp_header(&x->x_header, size)){ + + /* it is a control packet */ + if ((int)x->x_header.sequence_number < 0){ + + switch (x->x_header.sequence_number){ + case PDP_UDP_NEW: + _handle_PDP_UDP_NEW(x); + break; + + case PDP_UDP_DONE: + _handle_PDP_UDP_DONE(x); + + /* check if we got a complete packet + and signal arrival if we haven't done this already */ + if (x->x_receive_finished && !x->x_packet_transferred){ + x->x_packet_transferred = 1; + return 1; // data complete, please receive + } + break; + + default: + pdp_post("got unknown msg"); + break; + } + } + + /* it is a data packet */ + else { + _handle_UDP_DATA(x); + } + + + } + + else { + pdp_post("pdp_netreceive: got invalid UDP packet (size = %d)", size); + } + + return 0; //no major event, please poll again + +} + +/* get meta & data */ +char *pdp_udp_receiver_type(t_pdp_udp_receiver *x){return x->x_data_type;} +unsigned int pdp_udp_receiver_size(t_pdp_udp_receiver *x){return x->x_data_size;} +void *pdp_udp_receiver_data(t_pdp_udp_receiver *x){return x->x_data;} + + +/* S E N D E R */ + +/* INTERNALS */ + +static void _sleep(t_pdp_udp_sender *x) +{ + int sleep_period = x->x_sleep_period; + + if (sleep_period) { + if (!x->x_sleep_count++) usleep(x->x_sleepgrain_us); + x->x_sleep_count %= sleep_period; + } +} + +static void _send(t_pdp_udp_sender *x) +{ + //post("sending %u data bytes", x->x_buf_size); + + _make_udp_header(&x->x_header); + + PDP_ASSERT (x->x_buf_size <= PDP_UDP_BUFSIZE); + + if (-1 == sendto (x->x_socket, &x->x_header, x->x_buf_size + sizeof(t_pdp_udp_header), + 0, (struct sockaddr *)&x->x_sa, sizeof(struct sockaddr_in))) + pdp_post("pdp_netsend: send FAILED"); + + _sleep(x); + +} + + +static void _prepare_for_new_transmission(t_pdp_udp_sender *x, char *type, unsigned int size, void *data) +{ + unsigned int i; + + /* setup data for transmission */ + x->x_data_type = type; + x->x_data_size = size; + x->x_data = data; + x->x_chunk_size = x->x_udp_payload_size; + x->x_nb_chunks = (x->x_data_size - 1) / x->x_chunk_size + 1; + + /* generate a connection id (non-zero) */ + while (!(x->x_connection_id = rand())); + + /* setup chunk list to contain all chunks */ + if (x->x_chunk_list) free (x->x_chunk_list); + x->x_chunk_list_size = x->x_nb_chunks; + x->x_chunk_list = malloc(sizeof(unsigned int)*x->x_chunk_list_size); + for (i=0; i<x->x_chunk_list_size; i++) x->x_chunk_list[i] = i; + +} + +static void _send_header_packet(t_pdp_udp_sender *x) +{ + t_pdp_udp_newpacket *np = (t_pdp_udp_newpacket *)x->x_buf; /* buf contains the PDP_UDP_NEW body */ + + /* init packet */ + x->x_header.sequence_number = PDP_UDP_NEW; + x->x_header.connection_id = x->x_connection_id; + np->data_size = x->x_data_size; + np->nb_chunks = x->x_nb_chunks; + np->chunk_size = x->x_chunk_size; + strcpy(np->type, x->x_data_type); + x->x_buf_size = sizeof(*np) + strlen(np->type) + 1; + PDP_ASSERT(x->x_buf_size <= PDP_UDP_BUFSIZE); + + /* send the packet */ + _send(x); +} + +/* saend the chunks in the chunk list */ +static void _send_chunks(t_pdp_udp_sender *x){ + unsigned int i; + unsigned int count = 0; + + /* send chunks: this requires header is setup ok (sig,ver,connid)*/ + for (i=0; i<x->x_chunk_list_size; i++){ + unsigned int offset; + unsigned int current_chunk_size; + unsigned int seq = x->x_chunk_list[i]; + + PDP_ASSERT(seq < x->x_nb_chunks); + x->x_header.sequence_number = seq; // store chunk number + + /* get current chunk offset */ + offset = seq * x->x_chunk_size; + PDP_ASSERT(offset < x->x_data_size); + + + /* get current chunk size */ + current_chunk_size = (offset + x->x_chunk_size > x->x_data_size) ? + (x->x_data_size - offset) : x->x_chunk_size; + x->x_buf_size = current_chunk_size; + PDP_ASSERT(x->x_buf_size <= PDP_UDP_BUFSIZE); + + /* copy chunk to transmission buffer & send */ + PDP_ASSERT(offset + current_chunk_size <= x->x_data_size); + memcpy(x->x_buf, x->x_data + offset, current_chunk_size); + + + /* send the chunk */ + _send(x); + count++; + + } + D pdp_post("sent %d chunks, id=%x", count,x->x_connection_id); +} + +/* send a DONE packet */ +static void _send_done(t_pdp_udp_sender *x){ + x->x_header.sequence_number = PDP_UDP_DONE; + x->x_buf_size = 0; + _send(x); +} +static int _receive_packet(t_pdp_udp_sender *x, int desired_type) +/* 0 == timeout, -1 == error, 1 == got packet */ +{ + unsigned int size; + int type; + + struct timeval tv; + fd_set inset; + int sr; + + + while (1){ + int retval; + + /* wait for incoming */ + tv.tv_sec = 0; + tv.tv_usec = x->x_timeout_us; + FD_ZERO(&inset); + FD_SET(x->x_socket, &inset); + switch (select (x->x_socket+1, &inset, NULL, NULL, &tv)){ + case -1: + return -1; /* select error */ + case 0: + return 0; /* select time out */ + default: + break; /* data ready */ + } + + /* read packet */ + if (-1 == (int)(size = recv(x->x_socket, (void *)&x->x_resend_header, MAX_UDP_PACKET, 0))){ + pdp_post("pdp_netsend: error while reading from socket"); + return -1; + } + + /* check if it is a valid PDP_UDP packet */ + if (!_is_udp_header(&x->x_resend_header, size)){ + pdp_post("pdp_netsend: ignoring invalid UDP packet (size = %u)", size); + continue; + } + + + /* check connection id */ + if (x->x_connection_id != x->x_resend_header.connection_id){ + D pdp_post("pdp_netsend: ignoring ghost packet id=%x, current id=%x", + x->x_resend_header.connection_id, x->x_connection_id); + continue; + } + + /* check type */ + type = x->x_resend_header.sequence_number; + if (type != desired_type) continue; + + + /* setup data buffer for known packets */ + switch(type){ + case PDP_UDP_RESEND: + x->x_resend_items = (size - sizeof(t_pdp_udp_header)) / sizeof(unsigned int); + break; + default: + break; + } + + return 1; + } + +} + +/* get the resend list */ +static int _need_resend(t_pdp_udp_sender *x) { + + int retries = 3; + int retval; + while (retries--){ + + /* send a DONE msg */ + _send_done(x); + + /* wait for ACK */ + switch(_receive_packet(x, PDP_UDP_RESEND)){ + case 0: + /* timeout, retry */ + continue; + case -1: + /* error */ + goto move_on; + + default: + /* got PDP_UDP_RESEND packet: setup resend list */ + if (x->x_resend_items > x->x_nb_chunks){ + pdp_post("pdp_netsend: WARNING: chunk list size (%d) is too big, ignoring RESEND request", + x->x_resend_items); + x->x_resend_items = 0; + continue; + } + x->x_chunk_list_size = x->x_resend_items; + + memcpy(x->x_chunk_list, x->x_resend_chunks, sizeof(unsigned int) * x->x_resend_items); + D pdp_post("got RESEND request for %d chunks (id %x)", x->x_resend_items,x->x_connection_id); + + return x->x_chunk_list_size > 0; + } + + } + + /* timeout */ + move_on: + x->x_chunk_list_size = 0; + return 0; + + +} + + +/* INTERFACE */ + + +/* some flow control hacks */ + +void pdp_udp_sender_timeout_us(t_pdp_udp_sender *x, unsigned int timeout_us) +{ + x->x_timeout_us = timeout_us; +} + + +void pdp_udp_sender_sleepgrain_us(t_pdp_udp_sender *x, unsigned int sleepgrain_us) +{ + x->x_sleepgrain_us = sleepgrain_us; +} + +void pdp_udp_sender_sleepperiod(t_pdp_udp_sender *x, unsigned int sleepperiod) +{ + x->x_sleep_period = sleepperiod; +} + + +void pdp_udp_sender_udp_packet_size(t_pdp_udp_sender *x, unsigned int udp_packet_size) +{ + int i = (int)udp_packet_size - sizeof(t_pdp_udp_header); + if (i < 1024) i = 1024; + if (i > PDP_UDP_BUFSIZE) i = PDP_UDP_BUFSIZE; + x->x_udp_payload_size = i; +} + +void pdp_udp_sender_connect(t_pdp_udp_sender *x, char *host, unsigned int port) +{ + struct hostent *hp; + + hp = gethostbyname(host); + if (!hp){ + pdp_post("pdp_udp_sender: host %s not found", host); + } + else{ + /* host ok, setup address */ + x->x_sa.sin_family = AF_INET; + x->x_sa.sin_port = htons(port); + memcpy((char *)&x->x_sa.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* create the a socket if necessary */ + if (x->x_socket == -1){ + if (-1 == (x->x_socket = socket(PF_INET, SOCK_DGRAM, 0))){ + pdp_post("pdp_udp_sender: can't create socket"); + } + if (1){ + int on = 1; + if (setsockopt(x->x_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on))<0) + pdp_post("pdp_udp_sender: can't set broadcast flag"); + } + } + } +} + +/* setup */ +t_pdp_udp_sender *pdp_udp_sender_new(void) +{ + t_pdp_udp_sender *x = pdp_alloc(sizeof(*x)); + memset(x,0,sizeof(*x)); + + x->x_chunk_list = 0; + + /* no connection */ + x->x_socket = -1; + + + /* set flow control */ + pdp_udp_sender_timeout_us(x, 50000); + x->x_sleep_count = 0; + pdp_udp_sender_sleepgrain_us(x, 0); + pdp_udp_sender_sleepperiod(x, 50); + pdp_udp_sender_udp_packet_size(x, 1472); //optimal udp packet size (ip: 1500 = 28 + 1472) + + + return x; +} + +void pdp_udp_sender_free(t_pdp_udp_sender *x) +{ + int i; + void* retval; + if (x->x_socket != -1) close(x->x_socket); + if (x->x_chunk_list) free (x->x_chunk_list); +} + +/* send, returns 1 on success, 0 on error */ +int pdp_udp_sender_send(t_pdp_udp_sender *x, char* type, unsigned int size, void *data) +{ + + /* SEND A PACKET */ + + /* get the type and data from caller */ + /* send header packet and make sure it has arrived */ + /* send a chunk burst */ + /* send done packet and get the resend list */ + /* repeat until send list is empty */ + + + int hs_retry = 5; // retry count for initial handshake + int rs_retry = 5; // retry count for resends + + /* check if we have a target */ + if (-1 == x->x_socket) goto transerror; + + /* setup internal state */ + _prepare_for_new_transmission(x,type,size,data); + + /* handshake a new transmission */ + do { + if (!(hs_retry--)) break; + // pdp_post("handshake retry %d for packet %x", hscount, x->x_connection_id); + _send_header_packet(x); + } while (!_receive_packet(x, PDP_UDP_ACKNEW)); + + + /* exit if no handshake was possible */ + if (hs_retry < 0){ + DD pdp_post("pdp_netsend: DROP: receiver does not accept new transmission"); + goto transerror; + } + + /* transmission loop */ + do { + if (!(rs_retry--)) break; + _send_chunks(x); + } while (_need_resend(x)); + + /* exit if transmission was not successful */ + if (rs_retry < 0){ + DD pdp_post("pdp_netsend: DROP: receiver did not confirm reception"); + goto transerror; + } + + /* send successful */ + return 1; + + transerror: + /* transmission error */ + return 0; +} diff --git a/system/pdp.c b/system/pdp.c new file mode 100644 index 0000000..d74323b --- /dev/null +++ b/system/pdp.c @@ -0,0 +1,230 @@ +/* + * 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 <stdio.h> +#include "pdp_config.h" +#include "pdp_post.h" + + +/* all symbols are C style */ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* module setup declarations (all C-style) */ + +/* pdp system / internal stuff */ +void pdp_debug_setup(void); +void pdp_list_setup(void); +void pdp_pdsym_setup(void); +void pdp_forth_setup(void); +void pdp_forth_def_setup(void); +void pdp_symbol_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); +void pdp_udp_send_setup(void); +void pdp_udp_receive_setup(void); +void pdp_rawin_setup(void); +void pdp_rawout_setup(void); + + +/* hacks */ +void pdp_inspect_setup(void); + +/* testing */ +void pdp_dpd_test_setup(void); + + + + +/* library setup routine */ +void pdp_setup(void){ + + /* babble */ + pdp_post ("PDP: pure data packet"); + +#ifdef PDP_VERSION + pdp_post("PDP: version " PDP_VERSION ); +#endif + + + /* setup pdp system */ + + /* kernel */ + pdp_pdsym_setup(); + pdp_debug_setup(); + pdp_symbol_setup(); + pdp_list_setup(); + pdp_type_setup(); + pdp_packet_setup(); + pdp_control_setup(); + + /* types */ + pdp_image_setup(); + pdp_bitmap_setup(); + + + +#ifdef HAVE_PDP_GSL + pdp_matrix_setup(); +#endif + + pdp_queue_setup(); + + /* setup utility toolkit */ + pdp_ut_setup(); + + /* setup pdp pd 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_grey2mask_setup(); + pdp_constant_setup(); + pdp_logic_setup(); + pdp_loop_setup(); + pdp_description_setup(); + pdp_convert_setup(); + pdp_stateless_setup(); + + + pdp_cog_setup(); + pdp_array_setup(); + pdp_rawin_setup(); + pdp_rawout_setup(); + + + /* experimental stuff */ + pdp_inspect_setup(); + pdp_udp_send_setup(); + pdp_udp_receive_setup(); + + /* testing */ + //pdp_dpd_test_setup(); + + /* optional stuff */ + +#ifdef HAVE_PDP_READLINE + pdp_forthconsole_setup(); +#endif + +#ifdef HAVE_PDP_GSL + pdp_histo_setup(); + pdp_cheby_setup(); + pdp_mat_mul_setup(); + pdp_mat_lu_setup(); + pdp_mat_vec_setup(); +#endif + + +#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/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..f751912 --- /dev/null +++ b/system/png/pdp_png.c @@ -0,0 +1,409 @@ + +/* + * 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_packet.h" +#include "pdp_mem.h" +#include "pdp_post.h" +#include "pdp_debug.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) +{ +#define INTERNAL_ERROR 1 + 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: PDP_ASSERT(INTERNAL_ERROR); + } + 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 pdp_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 pdp_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 pdp_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 pdp_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 pdp_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 pdp_post("convert greyscale to 8 bit"); + } + if (x->x_bit_depth == 16){ + D pdp_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 pdp_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 pdp_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 pdp_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 pdp_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 pdp_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 pdp_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) +{ + pdp_post("WARNING: no png support, can't save png file %s", filename); + return 0; +} + +int pdp_packet_bitmap_from_png_file(char *filename) +{ + pdp_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) + pdp_post("usage: %s <png file>", argv[0]); + else + image = load_png(argv[1]); + + pdp_post ("%s", image ? "OK" : "ERROR"); + +} +#endif diff --git a/system/type/Makefile b/system/type/Makefile new file mode 100644 index 0000000..5f45b0c --- /dev/null +++ b/system/type/Makefile @@ -0,0 +1,11 @@ + +OBJECTS = pdp_bitmap.o pdp_image.o $(PDP_OPTTYPES) + + +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..b3e3e07 --- /dev/null +++ b/system/type/pdp_bitmap.c @@ -0,0 +1,628 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include "pdp_packet.h" +#include "pdp_type.h" +#include "pdp_llconv.h" +#include "pdp_internals.h" +#include "pdp_post.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_YV12: return pdp_packet_new_bitmap_yv12(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) +{ + pdp_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) +{ + 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; + + //pdp_post("creating:"); + //pdp_post("%s", type->s_name); + + l = pdp_type_to_list(type); + s = pdp_list_pop(l).w_symbol; // first element is "bitmap" + s = pdp_list_pop(l).w_symbol; + + /* get image type */ + if (s == pdp_gensym("grey")) t = PDP_BITMAP_GREY; + else if (s == pdp_gensym("yv12")) t = PDP_BITMAP_YV12; + else if (s == pdp_gensym("rgb")) t = PDP_BITMAP_RGB; + else goto exit; + + /* get image dimensions and create image */ + s = pdp_list_pop(l).w_symbol; + sscanf(s->s_name, "%dx%d", &w, &h); + p = pdp_packet_new_bitmap(t,w,h); + + 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_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..66fe22b --- /dev/null +++ b/system/type/pdp_image.c @@ -0,0 +1,584 @@ +/* + * 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 <string.h> +#include <stdio.h> + +#include "pdp_internals.h" +#include "pdp_packet.h" +#include "pdp_imageproc.h" +#include "pdp_resample.h" +#include "pdp_list.h" +#include "pdp_post.h" +#include "pdp_type.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)){ + pdp_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){ + //pdp_post("pdp_type_compat_image: not a PDP_IMAGE"); + return 0; + } + if (image0->encoding != image1->encoding){ + //pdp_post("pdp_type_compat_image: encodings differ"); + return 0; + } + if (image0->width != image1->width){ + //pdp_post("pdp_type_compat_image: image withs differ"); + return 0; + } + if (image0->height != image1->height){ + //pdp_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) +{ + pdp_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; + + //pdp_post("creating:"); + //pdp_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 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_matrix.c b/system/type/pdp_matrix.c new file mode 100644 index 0000000..a6fc827 --- /dev/null +++ b/system/type/pdp_matrix.c @@ -0,0 +1,654 @@ + +#include <string.h> +#include "pdp_matrix.h" +#include "pdp_packet.h" +#include "pdp_symbol.h" +#include "pdp_internals.h" // for pdp_packet_new, which is not a proper constructor +#include "pdp_post.h" +#include "pdp_type.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: + pdp_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) +{ + pdp_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) +{ + //pdp_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; // is now solved through (symbol based) constructor + matrix_class->wakeup = _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)){ + pdp_post("pdp_matrix_setup: WARNING: overriding gsl error handler."); + } + +} |