aboutsummaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
authorHans-Christoph Steiner <eighthave@users.sourceforge.net>2005-12-16 01:05:40 +0000
committerHans-Christoph Steiner <eighthave@users.sourceforge.net>2005-12-16 01:05:40 +0000
commitb694c274836ac8b04d644711ac324eac2e9ab83e (patch)
tree36b6a5c17f7e1f414f80697210c2ed3e8005035b /system
parente28a07fba67af0af818dda6afa4cf67c09700816 (diff)
checking in pdp 0.12.4 from http://zwizwa.fartit.com/pd/pdp/pdp-0.12.4.tar.gz
svn path=/trunk/externals/pdp/; revision=4232
Diffstat (limited to 'system')
-rw-r--r--system/CONTENTS9
-rw-r--r--system/Makefile30
-rw-r--r--system/X11/Makefile11
-rw-r--r--system/X11/pdp_xvideo.c201
-rw-r--r--system/X11/pdp_xwindow.c623
-rw-r--r--system/image/Makefile21
-rw-r--r--system/image/pdp_imageproc_common.c610
-rw-r--r--system/image/pdp_imageproc_mmx.c594
-rw-r--r--system/image/pdp_imageproc_portable.c681
-rw-r--r--system/image/pdp_llconv.c574
-rw-r--r--system/image/pdp_llconv_mmx.c55
-rw-r--r--system/image/pdp_llconv_portable.c82
-rw-r--r--system/image/pdp_resample.c204
-rw-r--r--system/kernel/CONTENTS7
-rw-r--r--system/kernel/Makefile16
-rw-r--r--system/kernel/pdp_debug.c21
-rw-r--r--system/kernel/pdp_dpd_command.c87
-rw-r--r--system/kernel/pdp_list.c931
-rw-r--r--system/kernel/pdp_mem.c129
-rw-r--r--system/kernel/pdp_packet.c634
-rw-r--r--system/kernel/pdp_packet2.c623
-rw-r--r--system/kernel/pdp_post.c48
-rw-r--r--system/kernel/pdp_symbol.c196
-rw-r--r--system/kernel/pdp_type.c473
-rw-r--r--system/mmx/Makefile31
-rw-r--r--system/mmx/pdp_mmx_test.c62
-rw-r--r--system/mmx/pixel_add_s16.s55
-rw-r--r--system/mmx/pixel_biquad_dirI_s16.s361
-rw-r--r--system/mmx/pixel_biquad_s16.s451
-rw-r--r--system/mmx/pixel_ca_s1.s189
-rw-r--r--system/mmx/pixel_cascade_s16.s330
-rw-r--r--system/mmx/pixel_cheby_s16.s90
-rw-r--r--system/mmx/pixel_conv_hor_s16.s134
-rw-r--r--system/mmx/pixel_conv_ver_s16.s128
-rw-r--r--system/mmx/pixel_crot_s16.s153
-rw-r--r--system/mmx/pixel_gain.s83
-rw-r--r--system/mmx/pixel_gain_s16.s71
-rw-r--r--system/mmx/pixel_mix_s16.s68
-rw-r--r--system/mmx/pixel_mul_s16.s56
-rw-r--r--system/mmx/pixel_pack_s16u8.s126
-rw-r--r--system/mmx/pixel_rand_s16.s76
-rw-r--r--system/mmx/pixel_randmix_s16.s91
-rw-r--r--system/mmx/pixel_resample_s16.s314
-rw-r--r--system/mmx/pixel_s1.s201
-rw-r--r--system/mmx/pixel_unpack_u8s16.s113
-rw-r--r--system/net/Makefile11
-rw-r--r--system/net/pdp_net.c685
-rw-r--r--system/pdp.c230
-rw-r--r--system/png/Makefile11
-rw-r--r--system/png/pdp_png.c409
-rw-r--r--system/type/Makefile11
-rw-r--r--system/type/pdp_bitmap.c628
-rw-r--r--system/type/pdp_image.c584
-rw-r--r--system/type/pdp_matrix.c654
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.");
+ }
+
+}