aboutsummaryrefslogtreecommitdiff
path: root/externals/gridflow/src
diff options
context:
space:
mode:
Diffstat (limited to 'externals/gridflow/src')
-rw-r--r--externals/gridflow/src/aalib.cxx144
-rw-r--r--externals/gridflow/src/classes1.cxx2342
-rw-r--r--externals/gridflow/src/classes2.cxx1183
-rw-r--r--externals/gridflow/src/dc1394.cxx287
-rw-r--r--externals/gridflow/src/fftw.cxx114
-rw-r--r--externals/gridflow/src/formats.cxx266
-rw-r--r--externals/gridflow/src/gem.cxx202
-rw-r--r--externals/gridflow/src/grid.cxx295
-rw-r--r--externals/gridflow/src/gridflow.cxx961
-rw-r--r--externals/gridflow/src/gridflow.hxx911
-rw-r--r--externals/gridflow/src/jpeg.cxx118
-rw-r--r--externals/gridflow/src/mmx.rb219
-rw-r--r--externals/gridflow/src/mpeg3.cxx83
-rw-r--r--externals/gridflow/src/netpbm.cxx117
-rw-r--r--externals/gridflow/src/number.cxx446
-rw-r--r--externals/gridflow/src/opencv.cxx537
-rw-r--r--externals/gridflow/src/png.cxx143
-rw-r--r--externals/gridflow/src/pwc-ioctl.h292
-rw-r--r--externals/gridflow/src/quartz.m224
-rw-r--r--externals/gridflow/src/quicktimeapple.cxx456
-rw-r--r--externals/gridflow/src/quicktimehw.cxx246
-rw-r--r--externals/gridflow/src/sdl.cxx209
-rw-r--r--externals/gridflow/src/source_filter.rb311
-rw-r--r--externals/gridflow/src/videodev.cxx793
-rw-r--r--externals/gridflow/src/x11.cxx664
25 files changed, 11563 insertions, 0 deletions
diff --git a/externals/gridflow/src/aalib.cxx b/externals/gridflow/src/aalib.cxx
new file mode 100644
index 00000000..771c5eab
--- /dev/null
+++ b/externals/gridflow/src/aalib.cxx
@@ -0,0 +1,144 @@
+/*
+ $Id: aalib.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#define aa_hardwareparams aa_hardware_params
+#include <aalib.h>
+#include <map>
+
+/* MINNOR is a typo in aalib.h, sorry */
+typedef
+#if AA_LIB_MINNOR == 2
+ int
+#else
+ enum aa_attribute
+#endif
+AAAttr;
+
+static std::map<string,const aa_driver *> drivers;
+
+\class FormatAALib : Format {
+ aa_context *context;
+ aa_renderparams *rparams;
+ \attr bool autodraw;
+ bool raw_mode;
+ /* !@#$ varargs missing here */
+ \constructor (t_symbol *mode, string target) {
+ context=0; autodraw=1;
+ argc-=2; argv+=2;
+ char *argv2[argc];
+ for (int i=0; i<argc; i++) argv2[i] = strdup(string(argv[i]).data());
+ if (mode!=gensym("out")) RAISE("write-only, sorry");
+ aa_parseoptions(0,0,&argc,argv2);
+ for (int i=0; i<argc; i++) free(argv2[i]);
+ if (drivers.find(target)==drivers.end()) RAISE("unknown aalib driver '%s'",target.data());
+ const aa_driver *driver = drivers[target];
+ context = aa_init(driver,&aa_defparams,0);
+ rparams = aa_getrenderparams();
+ if (!context) RAISE("opening aalib didn't work");
+ int32 v[]={context->imgheight,context->imgwidth,1};
+ post("aalib image size: %s",(new Dim(3,v))->to_s());
+ }
+ ~FormatAALib () {if (context) aa_close(context);}
+ \decl 0 hidecursor ();
+ \decl 0 print (int y, int x, int a, string text);
+ \decl 0 draw ();
+ \decl 0 dump ();
+ \grin 0 int
+};
+
+GRID_INLET(0) {
+ if (!context) RAISE("boo");
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ switch (in->dim->get(2)) {
+ case 1: raw_mode = false; break;
+ case 2: raw_mode = true; break;
+ default:
+ RAISE("expecting 1 greyscale channel (got %d)",in->dim->get(2));
+ }
+ in->set_chunk(1);
+} GRID_FLOW {
+ int f = in->dim->prod(1);
+ if (raw_mode) {
+ int sx = min(f,aa_scrwidth(context));
+ int y = dex/f;
+ while (n) {
+ if (y>=aa_scrheight(context)) return;
+ for (int x=0; x<sx; x++) {
+ context->textbuffer[y*aa_scrwidth(context)+x]=data[x*2+0];
+ context->attrbuffer[y*aa_scrwidth(context)+x]=data[x*2+1];
+ }
+ y++;
+ n-=f;
+ data+=f;
+ }
+ } else {
+ int sx = min(f,context->imgwidth);
+ int y = dex/f;
+ while (n) {
+ if (y>=context->imgheight) return;
+ for (int x=0; x<sx; x++) aa_putpixel(context,x,y,data[x]);
+ y++;
+ n-=f;
+ data+=f;
+ }
+ }
+} GRID_FINISH {
+ if (!raw_mode) {
+ aa_palette pal;
+ for (int i=0; i<256; i++) aa_setpalette(pal,i,i,i,i);
+ aa_renderpalette(context,pal,rparams,0,0,
+ aa_scrwidth(context),aa_scrheight(context));
+ }
+ if (autodraw==1) aa_flush(context);
+} GRID_END
+
+\def 0 hidecursor () { aa_hidemouse(context); }
+\def 0 draw () { aa_flush(context); }
+\def 0 print (int y, int x, int a, string text) {
+ aa_puts(context,x,y,(AAAttr)a,(char *)text.data());
+ if (autodraw==1) aa_flush(context);
+}
+
+\def 0 dump () {
+ int32 v[] = {aa_scrheight(context), aa_scrwidth(context), 2};
+ GridOutlet out(this,0,new Dim(3,v));
+ for (int y=0; y<aa_scrheight(context); y++) {
+ for (int x=0; x<aa_scrwidth(context); x++) {
+ int32 data[2];
+ data[0] = context->textbuffer[y*aa_scrwidth(context)+x];
+ data[1] = context->attrbuffer[y*aa_scrwidth(context)+x];
+ out.send(2,data);
+ }
+ }
+}
+
+\end class FormatAALib {
+ const aa_driver *const *p = aa_drivers;
+ for (; *p; p++) drivers[(*p)->shortname] = *p;
+ install_format("#io.aalib",2,"");
+}
+void startup_aalib () {
+ \startall
+}
diff --git a/externals/gridflow/src/classes1.cxx b/externals/gridflow/src/classes1.cxx
new file mode 100644
index 00000000..07031e30
--- /dev/null
+++ b/externals/gridflow/src/classes1.cxx
@@ -0,0 +1,2342 @@
+/*
+ $Id: flow_objects.c 4548 2009-10-31 20:26:25Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <errno.h>
+#include "gridflow.hxx.fcs"
+#ifdef DESIRE
+#include "desire.h"
+#else
+extern "C" {
+#include "bundled/g_canvas.h"
+};
+extern "C" t_canvas *canvas_getrootfor(t_canvas *x);
+#endif
+
+//using namespace std; // can't
+
+//#undef GRID_INPUT
+//#define GRID_INPUT(I,V) GRID_INLET(I) {in->buf=V=new Grid(in->dim,NumberTypeE_type_of(data));} GRID_FINISH
+
+/* ---------------------------------------------------------------- */
+
+// BAD HACK: GCC complains: unimplemented (--debug mode only) (i don't remember which GCC this was)
+#ifdef HAVE_DEBUG
+#define SCOPY(a,b,n) COPY(a,b,n)
+#else
+#define SCOPY(a,b,n) SCopy<n>::f(a,b)
+#endif
+
+template <long n> class SCopy {
+public: template <class T> static inline void __attribute__((always_inline)) f(T *a, T *b) {
+ *a=*b; SCopy<n-1>::f(a+1,b+1);}};
+template <> class SCopy<0> {
+public: template <class T> static inline void __attribute__((always_inline)) f(T *a, T *b) {}};
+
+/*template <> class SCopy<4> {
+public: template <class T>
+ static inline void __attribute__((always_inline)) f(T *a, T *b) {
+ *a=*b; SCopy<3>::f(a+1,b+1);}
+ static inline void __attribute__((always_inline)) f(uint8 *a, uint8 *b)
+ { *(int32 *)a=*(int32 *)b; }
+};*/
+
+Numop *op_add, *op_sub, *op_mul, *op_div, *op_mod, *op_shl, *op_and, *op_put;
+
+static void expect_dim_dim_list (P<Dim> d) {
+ if (d->n!=1) RAISE("dimension list should be Dim[n], not %s",d->to_s());}
+//static void expect_min_one_dim (P<Dim> d) {
+// if (d->n<1) RAISE("minimum 1 dimension");}
+static void expect_max_one_dim (P<Dim> d) {
+ if (d->n>1) RAISE("expecting Dim[] or Dim[n], got %s",d->to_s());}
+//static void expect_exactly_one_dim (P<Dim> d) {
+// if (d->n!=1) RAISE("expecting Dim[n], got %s",d->to_s());}
+
+//****************************************************************
+\class GridCast : FObject {
+ \attr NumberTypeE nt;
+ \constructor (NumberTypeE nt) {this->nt = nt;}
+ \grin 0
+};
+GRID_INLET(0) {
+ out = new GridOutlet(this,0,in->dim,nt);
+} GRID_FLOW {
+ out->send(n,data);
+} GRID_END
+\end class {install("#cast",1,1); add_creator("@cast");}
+
+//****************************************************************
+
+GridHandler *stromgol; // remove this asap
+
+//{ ?,Dim[B] -> Dim[*Cs] }
+// out0 nt to be specified explicitly
+\class GridImport : FObject {
+ \attr NumberTypeE cast;
+ \attr P<Dim> dim; // size of grids to send
+ PtrGrid dim_grid;
+ \constructor (...) {
+ dim_grid.constrain(expect_dim_dim_list);
+ this->cast = argc>=2 ? NumberTypeE_find(argv[1]) : int32_e;
+ if (argc>2) RAISE("too many arguments");
+ if (argc>0 && argv[0]!=gensym("per_message")) {
+ dim_grid=new Grid(argv[0]);
+ dim = dim_grid->to_dim();
+ if (!dim->prod()) RAISE("target grid size must not be zero");
+ }
+ }
+ ~GridImport() {}
+ \decl 0 reset();
+ \decl 0 symbol(t_symbol *x);
+ \decl 0 to_ascii(...);
+ \decl 0 bang();
+ //\decl 0 list(...);
+ \decl 1 per_message();
+ \grin 0
+ \grin 1 int32
+ template <class T> void process (long n, T *data) {
+ if (in.size()<=0) in.resize(1);
+ if (!in[0]) in[0]=new GridInlet((FObject *)this,stromgol);
+ while (n) {
+ if (!out || !out->dim) out = new GridOutlet(this,0,dim?dim:in[0]->dim,cast);
+ long n2 = min((long)n,out->dim->prod()-out->dex);
+ out->send(n2,data);
+ n-=n2; data+=n2;
+ }
+ }
+};
+
+GRID_INLET(0) {} GRID_FLOW {process(n,data);} GRID_END
+GRID_INPUT(1,dim_grid) {
+ P<Dim> d = dim_grid->to_dim();
+ if (!d->prod()) RAISE("target grid size must not be zero");
+ dim = d;
+} GRID_END
+
+\def 0 symbol(t_symbol *x) {
+ const char *name = x->s_name;
+ long n = strlen(name);
+ if (!dim) out=new GridOutlet(this,0,new Dim(n));
+ process(n,(uint8 *)name);
+}
+\def 0 to_ascii(...) {
+ std::ostringstream os;
+ pd_oprint(os,argc,argv);
+ string s = os.str();
+ long n = s.length();
+ if (!dim) out=new GridOutlet(this,0,new Dim(n),cast);
+ process(n,(uint8 *)s.data());
+}
+
+\def 0 bang() {_0_list(0,0);}
+\def 0 list(...) {//first two lines are there until grins become strictly initialized.
+ if (in.size()<=0) in.resize(1);
+ if (!in[0]) in[0]=new GridInlet((FObject *)this,stromgol);
+ in[0]->from_list(argc,argv,cast);
+ if (!argc && !dim) out = new GridOutlet(this,0,new Dim(0),cast);
+}
+\def 1 per_message() {dim=0; dim_grid=0;}
+
+\def 0 reset() {int32 foo[1]={0}; if (out) while (out->dim) out->send(1,foo);}
+\end class {install("#import",2,1); add_creator("@import"); stromgol = &GridImport_grid_0_hand;}
+
+//****************************************************************
+/*{ Dim[*As] -> ? }*/
+/* in0: integer nt */
+\class GridToFloat : FObject {
+ \constructor () {}
+ \grin 0
+};
+GRID_INLET(0) {
+} GRID_FLOW {
+ for (int i=0; i<n; i++) outlet_float(bself->outlets[0],data[i]);
+} GRID_END
+\end class {install("#to_float",1,1); add_creator("#export"); add_creator("@export");}
+
+\class GridToSymbol : FObject {
+ \constructor () {}
+ \grin 0
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+ char c[n+1];
+ for (int i=0; i<n; i++) c[i]=(char)data[i];
+ c[n]=0;
+ outlet_symbol(bself->outlets[0],gensym(c));
+} GRID_END
+\end class {install("#to_symbol",1,1); add_creator("#export_symbol"); add_creator("@export_symbol");}
+
+/*{ Dim[*As] -> ? }*/
+/* in0: integer nt */
+\class GridExportList : FObject {
+ \constructor () {}
+ int n;
+ \grin 0
+};
+
+GRID_INLET(0) {
+ long n = in->dim->prod();
+ if (n>1000000) RAISE("list too big (%ld elements, max 1000000)", n);
+ this->n = n;
+ in->set_chunk(0);
+} GRID_FLOW {
+ send_out(0,n,data);
+} GRID_FINISH {
+ if (in->dim->prod()==0) send_out(0,0,data);
+} GRID_END
+
+\end class {install("#to_list",1,1); add_creator("#export_list"); add_creator("@export_list");}
+
+/* **************************************************************** */
+\class GridPrint : FObject {
+ \constructor (t_symbol *name=0) {
+ this->dest = 0;
+ this->name = name;
+ base=10; trunc=70; maxrows=50;
+ }
+ \attr t_symbol *name;
+ \grin 0
+ int base;
+ uint32 trunc;
+ int maxrows;
+ int columns;
+ t_pd *dest;
+ \decl 0 dest (void *p);
+ \decl void end_hook ();
+ \decl 0 base (int x);
+ \decl 0 trunc (int x);
+ \decl 0 maxrows (int y);
+ void puts (const char *s) {
+ if (!dest) post("%s",s);
+ else {
+ int n = strlen(s);
+ t_atom a[n];
+ for (int i=0; i<n; i++) SETFLOAT(a+i,s[i]);
+ //fprintf(stderr,"dest=%p\n",dest);
+ //fprintf(stderr,"*dest={%08x,%08x,%08x,%08x,...}\n",dest[0],dest[1],dest[2],dest[3]);
+ pd_typedmess(dest,gensym("very_long_name_that_nobody_uses"),n,a);
+ }
+ }
+ void puts (std::string s) {puts(s.data());}
+ void puts (std::ostringstream &s) {puts(s.str());}
+ template <class T> void make_columns (int n, T *data);
+ template <class T> void dump(std::ostream &s, int n, T *data, char sep=' ', int trunc=-1) {
+ if (trunc<0) trunc=this->trunc;
+ std::string f = format(NumberTypeE_type_of(data));
+ for (int i=0; i<n; i++) {
+ if (base!=2) oprintf(s,f.data(),data[i]);
+ else {
+ T x = gf_abs(data[i]);
+ int ndigits = 1+highest_bit(uint64(x));
+ for (int j=columns-ndigits-(data[i]!=x); j>=0; j--) s<<' ';
+ if (data[i]!=x) s<<'-';
+ for (int j=ndigits-1; j>=0; j--) {
+ s<<char('0'+(((long)x>>j)&1));
+ }
+ }
+ if (i<n-1) s << sep;
+ if (s.tellp()>trunc) return;
+ }
+ }
+ void dump_dims(std::ostream &s, GridInlet *in) {
+ if (name && name!=&s_) s << name->s_name << ": ";
+ s << "Dim[";
+ for (int i=0; i<in->dim->n; i++) {
+ s << in->dim->v[i];
+ if (i<in->dim->n-1) s << ',';
+ }
+ s << "]";
+ if (in->nt!=int32_e) s << "(" << number_type_table[in->nt].name << ")";
+ s << ": ";
+ }
+ std::string format (NumberTypeE nt) {
+ if (nt==float32_e) return "%6.6f";
+ if (nt==float64_e) return "%14.14f";
+ std::ostringstream r;
+ r << "%";
+ r << columns;
+ //if (nt==int64_e) r << "l";
+ if (base==2) r << "b"; else
+ if (base==8) r << "o"; else
+ if (base==10) r << "d"; else
+ if (base==16) r << "x";
+ return r.str();
+ }
+};
+\def 0 dest (void *p) {dest = (t_pd *)p;}
+\def void end_hook () {}
+\def 0 base (int x) { if (x==2 || x==8 || x==10 || x==16) base=x; else RAISE("base %d not supported",x); }
+\def 0 trunc (int x) {
+ if (x<0 || x>240) RAISE("out of range (not in 0..240 range)");
+ trunc = x;
+}
+\def 0 maxrows (int y) {maxrows = y;}
+template <class T> void GridPrint::make_columns (int n, T *data) {
+ long maxv=0;
+ long minv=0;
+ for (int i=0; i<n; i++) {
+ if (maxv<data[i]) maxv=long(data[i]);
+ if (minv>data[i]) minv=long(data[i]);
+ }
+ int maxd = 1 + (maxv<0) + int(log(max(1.,fabs(maxv)))/log(base));
+ int mind = 1 + (minv<0) + int(log(max(1.,fabs(minv)))/log(base));
+ //fprintf(stderr,"v=(%d,%d) d=(%d,%d)\n",minv,maxv,mind,maxd);
+ columns = max(maxd,mind);
+}
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+ std::ostringstream head;
+ dump_dims(head,in);
+ int ndim = in->dim->n;
+ if (ndim > 3) {
+ head << " (not printed)";
+ puts(head);
+ } else if (ndim < 2) {
+ make_columns(n,data);
+ dump(head,n,data,' ',trunc);
+ puts(head);
+ } else if (ndim == 2) {
+ puts(head);
+ make_columns(n,data);
+ long sy = in->dim->v[0];
+ long sx = n/sy;
+ for (int row=0; row<sy; row++) {
+ std::ostringstream body;
+ dump(body,sx,&data[sx*row],' ',trunc);
+ if (body.tellp()>trunc) body << "...";
+ puts(body);
+ if (row>maxrows) {puts("..."); break;}
+ }
+ } else if (ndim == 3) {
+ puts(head);
+ make_columns(n,data);
+ int sy = in->dim->v[0];
+ int sx = in->dim->v[1];
+ int sz = n/sy;
+ int sz2 = sz/in->dim->v[1];
+ for (int row=0; row<sy; row++) {
+ std::ostringstream str;
+ for (int col=0; col<sx; col++) {
+ str << "(";
+ dump(str,sz2,&data[sz*row+sz2*col],' ',trunc);
+ if (str.tellp()>trunc) {str << "..."; break;} else str << ")";
+ }
+ puts(str);
+ if (row>maxrows) {puts("..."); break;}
+ }
+ }
+ end_hook(0,0);
+} GRID_FINISH {
+ std::ostringstream head;
+ dump_dims(head,in);
+ if (in->dim->prod()==0) puts(head);
+} GRID_END
+\end class {install("#print",1,1); add_creator("@print");}
+
+/* **************************************************************** */
+// [#store] is the class for storing a grid and restituting it on demand.
+// The right inlet receives the grid. The left inlet receives either a bang
+// (which forwards the whole image) or a grid describing what to send.
+//{ Dim[*As,B],Dim[*Cs,*Ds] -> Dim[*As,*Ds] }
+// in0: integer nt
+// in1: whatever nt
+// out0: same nt as in1
+\class GridStore : FObject {
+
+ PtrGrid r; // can't be \attr
+ PtrGrid put_at; // can't be //\attr
+ \attr Numop *op;
+ int32 *wdex ; // temporary buffer, copy of put_at
+ int32 *fromb;
+ int32 *to2 ;
+ int lsd; // lsd = Last Same Dimension (for put_at)
+ int d; // goes with wdex
+ long cs; // chunksize used in put_at
+ \constructor (Grid *r=0) {
+ put_at.constrain(expect_max_one_dim);
+ this->r = r?r:new Grid(new Dim(),int32_e,true);
+ op = op_put;
+ wdex = NEWBUF(int32,Dim::MAX_DIM); // temporary buffer, copy of put_at
+ fromb = NEWBUF(int32,Dim::MAX_DIM);
+ to2 = NEWBUF(int32,Dim::MAX_DIM);
+ }
+ ~GridStore () {
+ DELBUF(wdex);
+ DELBUF(fromb);
+ DELBUF(to2);
+ }
+ \decl 0 bang ();
+ \decl 1 reassign ();
+ \decl 1 put_at (...);
+ \grin 0 int
+ \grin 1
+ template <class T> void compute_indices(T *v, long nc, long nd);
+};
+
+// takes the backstore of a grid and puts it back into place. a backstore
+// is a grid that is filled while the grid it would replace has not
+// finished being used.
+static void snap_backstore (PtrGrid &r) {if (r.next) {r=r.next.p; r.next=0;}}
+
+template <class T> void GridStore::compute_indices(T *v, long nc, long nd) {
+ for (int i=0; i<nc; i++) {
+ uint32 wrap = r->dim->v[i];
+ bool fast = lowest_bit(wrap)==highest_bit(wrap); // is power of two?
+ if (i) {
+ if (fast) op_shl->map(nd,v,(T)highest_bit(wrap));
+ else op_mul->map(nd,v,(T)wrap);
+ }
+ if (fast) op_and->map(nd,v+nd*i,(T)(wrap-1));
+ else op_mod->map(nd,v+nd*i,(T)(wrap));
+ if (i) op_add->zip(nd,v,v+nd*i);
+ }
+}
+
+// !@#$ i should ensure that n is not exceedingly large
+// !@#$ worse: the size of the foo buffer may still be too large
+GRID_INLET(0) {
+ // snap_backstore must be done before *anything* else
+ snap_backstore(r);
+ int na = in->dim->n;
+ int nb = r->dim->n;
+ int32 v[Dim::MAX_DIM];
+ if (na<1) RAISE("must have at least 1 dimension.",na,1,1+nb);
+ long nc = in->dim->get(na-1);
+ if (nc>nb) RAISE("got %d elements in last dimension, expecting <= %d", nc, nb);
+ long nnc = r->dim->prod(nc);
+ int lastindexable = nnc ? r->dim->prod()/nnc-1 : 0; // SIGFPE happened when r was especially empty (nnc==0)
+ int ngreatest = nt_greatest((T *)0);
+ if (lastindexable > ngreatest) RAISE("lastindexable=%d > ngreatest=%d (ask matju)",lastindexable,ngreatest);
+ int nd = nb-nc+na-1;
+ COPY(v,in->dim->v,na-1);
+ COPY(v+na-1,r->dim->v+nc,nb-nc);
+ out=new GridOutlet(this,0,new Dim(nd,v),r->nt);
+ if (nc>0) in->set_chunk(na-1);
+} GRID_FLOW {
+ int na = in->dim->n;
+ int nc = in->dim->get(na-1);
+ long size = r->dim->prod(nc);
+ long nd = n/nc;
+ T w[n];
+ T *v=w;
+ if (sizeof(T)==1 && nc==1 && r->dim->v[0]<=256) {
+ // bug? shouldn't modulo be done here?
+ v=data;
+ } else {
+ COPY(v,data,n);
+ for (long k=0,i=0; i<nc; i++) for (long j=0; j<n; j+=nc) v[k++] = data[i+j];
+ compute_indices(v,nc,nd);
+ }
+#define FOO(type) { \
+ type *p = (type *)*r; \
+ if (size<=16) { \
+ type tada[nd*size]; \
+ type *foo = tada; \
+ long i=0; \
+ switch (size) { \
+ case 1: for (; i<(nd&-4); i+=4, foo+=4) { \
+ foo[0] = p[v[i+0]]; \
+ foo[1] = p[v[i+1]]; \
+ foo[2] = p[v[i+2]]; \
+ foo[3] = p[v[i+3]]; \
+ } break; \
+ case 2: for (; i<nd; i++, foo+=2) SCOPY(foo,p+2*v[i],2); break; \
+ case 3: for (; i<nd; i++, foo+=3) SCOPY(foo,p+3*v[i],3); break; \
+ case 4: for (; i<nd; i++, foo+=4) SCOPY(foo,p+4*v[i],4); break; \
+ default:; }; \
+ for (; i<nd; i++, foo+=size) COPY(foo,p+size*v[i],size); \
+ out->send(size*nd,tada); \
+ } else { \
+ for (int i=0; i<nd; i++) out->send(size,p+size*v[i]); \
+ } \
+}
+ TYPESWITCH(r->nt,FOO,)
+#undef FOO
+} GRID_FINISH {
+ if (in->dim->prod()==0) {
+ long n = in->dim->prod(0,-2);
+ long size = r->dim->prod();
+#define FOO(T) while (n--) out->send(size,(T *)*r);
+ TYPESWITCH(r->nt,FOO,)
+#undef FOO
+ }
+} GRID_END
+
+GRID_INLET(1) {
+ NumberTypeE nt = NumberTypeE_type_of(data);
+ if (!put_at) { // reassign
+ if (in[0].dim) r.next = new Grid(in->dim,nt);
+ else r = new Grid(in->dim,nt);
+ return;
+ }
+ // put_at ( ... )
+ snap_backstore(r);
+ SAME_TYPE(in,r);
+ //!@#$ should check types. if (r->nt!=in->nt) RAISE("shoo");
+ long nn=r->dim->n, na=put_at->dim->v[0], nb=in->dim->n;
+ int32 sizeb[nn];
+ for (int i=0; i<nn; i++) { fromb[i]=0; sizeb[i]=1; }
+ COPY(wdex ,(int32 *)*put_at ,put_at->dim->prod());
+ COPY(fromb+nn-na,(int32 *)*put_at ,na);
+ COPY(sizeb+nn-nb,(int32 *)in->dim->v,nb);
+ for (int i=0; i<nn; i++) to2[i] = fromb[i]+sizeb[i];
+ d=0;
+ // find out when we can skip computing indices
+ //!@#$ should actually also stop before blowing up packet size
+ lsd=nn;
+ while (lsd>=nn-in->dim->n) {
+ lsd--;
+ //int cs = in->dim->prod(lsd-nn+in->dim->n);
+ if (/*cs*(number_type_table[in->nt].size/8)>GridOutlet::MAX_PACKET_SIZE ||*/
+ fromb[lsd]!=0 || sizeb[lsd]!=r->dim->v[lsd]) break;
+ }
+ lsd++;
+ long chunk = lsd-nn+in->dim->n;
+ in->set_chunk( chunk);
+ cs = in->dim->prod(chunk);
+} GRID_FLOW {
+ //fprintf(stderr,"d=%d\n",d);
+ if (!put_at) { // reassign
+ COPY(((T *)*(r.next ? r.next.p : &*r.p))+dex, data, n);
+ return;
+ }
+ // put_at (...)
+ int32 v[lsd];
+ int32 *x = wdex;
+ while (n) {
+ // here d is the dim# to reset; d=n for none
+ for(;d<lsd;d++) x[d]=fromb[d];
+ COPY(v,x,lsd);
+ compute_indices(v,lsd,1);
+ op->zip(cs,(T *)*r+v[0]*cs,data);
+ data+=cs;
+ n-=cs;
+ // find next set of indices; here d is the dim# to increment
+ for(;;) {d--; if (d<0) return; x[d]++; if (x[d]<to2[d]) break;}
+ d++;
+ }
+} GRID_END
+\def 0 bang () {
+ t_atom a[2];
+ SETFLOAT(a+0,0);
+ SETSYMBOL(a+1,gensym("#"));
+ pd_list((t_pd *)bself,&s_list,2,a);
+}
+\def 1 reassign () {put_at=0;}
+\def 1 put_at (...) {
+ if (argv[0].a_type==A_LIST) put_at=convert(argv[0],(Grid **)0);
+ else {
+ put_at=new Grid(new Dim(argc),int32_e);
+ int32 *v = (int32 *)*put_at;
+ for (int i=0; i<argc; i++) v[i]=convert(argv[i],(int32 *)0);
+ }
+}
+\end class {install("#store",2,1); add_creator("@store");}
+
+//****************************************************************
+//{ Dim[*As]<T> -> Dim[*As]<T> } or
+//{ Dim[*As]<T>,Dim[*Bs]<T> -> Dim[*As]<T> }
+\class GridOp : FObject {
+ \attr Numop *op;
+ PtrGrid r;
+ \constructor (Numop *op, Grid *r=0) {
+ this->op=op;
+ this->r=r?r:new Grid(new Dim(),int32_e,true);
+ }
+ \grin 0
+ \grin 1
+};
+
+GRID_INLET(0) {
+ snap_backstore(r);
+ SAME_TYPE(in,r);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ if (op->size>1 && (in->dim->get(in->dim->n-1)!=op->size || r->dim->get(r->dim->n-1)!=op->size))
+ RAISE("using %s requires Dim(...,%d) in both inlets but got: left=%s right=%s",
+ op->name,op->size,in->dim->to_s(),r->dim->to_s());
+ //if (out->inlets.size()==1) post("[#]: 1 receiver with bugger size %s",out->inlets[0]->dim->to_s());
+} GRID_FLOW {
+ T *rdata = (T *)*r;
+ long loop = r->dim->prod();
+ T tada[n];
+ COPY(tada,data,n);
+ if (loop>1) {
+ if (dex+n <= loop) {
+ op->zip(n/op->size,tada,rdata+dex);
+ } else {
+ // !@#$ should prebuild and reuse this array when "loop" is small
+ T data2[n];
+ long ii = mod(dex,loop);
+ long m = min(loop-ii,n);
+ COPY(data2,rdata+ii,m);
+ long nn = m+((n-m)/loop)*loop;
+ for (long i=m; i<nn; i+=loop) COPY(data2+i,rdata,loop);
+ if (n>nn) COPY(data2+nn,rdata,n-nn);
+ op->zip(n/op->size,tada,data2);
+ }
+ } else op->map(n,tada,*rdata);
+ out->send(n,tada);
+} GRID_END
+
+GRID_INPUT2(1,r) {} GRID_END
+\end class {install("#",2,1); add_creator("@");}
+
+//****************************************************************
+\class GridFold : FObject {
+ \attr Numop *op;
+ \attr PtrGrid seed;
+ \constructor (Numop *op) {this->op=op;}
+ \grin 0
+};
+
+GRID_INLET(0) {
+ //{ Dim[*As,B,*Cs]<T>,Dim[*Cs]<T> -> Dim[*As,*Cs]<T> }
+ if (seed) SAME_TYPE(in,seed);
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ if (an<=bn) RAISE("minimum 1 more dimension than the seed (%d vs %d)",an,bn);
+ int32 v[an-1];
+ int yi = an-bn-1;
+ COPY(v,in->dim->v,yi);
+ COPY(v+yi,in->dim->v+an-bn,bn);
+ if (seed) SAME_DIM(an-(yi+1),in->dim,(yi+1),seed->dim,0);
+ out=new GridOutlet(this,0,new Dim(an-1,v),in->nt);
+ in->set_chunk(yi);
+ if (in->dim->prod(yi)==0) {
+ long n = out->dim->prod();
+ T x=0; op->on(x)->neutral(&x,at_left);
+ for(long i=0; i<n; i++) out->send(1,&x);
+ }
+} GRID_FLOW {
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ long yn = in->dim->v[an-bn-1];
+ long zn = in->dim->prod(an-bn);
+ T buf[n/yn];
+ long nn=n;
+ long yzn=yn*zn;
+ for (long i=0; n; i+=zn, data+=yzn, n-=yzn) {
+ if (seed) COPY(buf+i,((T *)*seed),zn);
+ else {T neu; op->on(*buf)->neutral(&neu,at_left); op_put->map(zn,buf+i,neu);}
+ op->fold(zn,yn,buf+i,data);
+ }
+ out->send(nn/yn,buf);
+} GRID_FINISH {
+} GRID_END
+
+\end class {install("#fold",1,1);}
+
+\class GridScan : FObject {
+ \attr Numop *op;
+ \attr PtrGrid seed;
+ \constructor (Numop *op) {this->op = op;}
+ \grin 0
+};
+
+GRID_INLET(0) {
+ //{ Dim[*As,B,*Cs]<T>,Dim[*Cs]<T> -> Dim[*As,B,*Cs]<T> }
+ if (seed) SAME_TYPE(in,seed);
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ if (an<=bn) RAISE("minimum 1 more dimension than the right hand");
+ if (seed) SAME_DIM(bn,in->dim,an-bn,seed->dim,0);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_chunk(an-bn-1);
+} GRID_FLOW {
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ long yn = in->dim->v[an-bn-1];
+ long zn = in->dim->prod(an-bn);
+ long factor = yn*zn;
+ T buf[n];
+ COPY(buf,data,n);
+ if (seed) {
+ for (long i=0; i<n; i+=factor) op->scan(zn,yn,(T *)*seed,buf+i);
+ } else {
+ T neu; op->on(*buf)->neutral(&neu,at_left);
+ T seed[zn]; op_put->map(zn,seed,neu);
+ for (long i=0; i<n; i+=factor) op->scan(zn,yn, seed,buf+i);
+ }
+ out->send(n,buf);
+} GRID_END
+
+\end class {install("#scan",1,1);}
+
+//****************************************************************
+// L is a Dim[*si,sj, *ss]<T>
+// R is a Dim[ sj,*sk,*ss]<T>
+// Seed is a Dim[ *ss]<T>
+// result is a Dim[*si, *sk,*ss]<T>
+// Currently *ss can only be = Dim[]
+\class GridInner : FObject {
+ \attr Numop *op;
+ \attr Numop *fold;
+ \attr PtrGrid seed;
+ PtrGrid r;
+ PtrGrid r2; // temporary
+ bool use_dot;
+ \constructor (Grid *r=0) {
+ this->op = op_mul;
+ this->fold = op_add;
+ this->seed = new Grid(new Dim(),int32_e,true);
+ this->r = r ? r : new Grid(new Dim(),int32_e,true);
+ }
+ \grin 0
+ \grin 1
+};
+
+// let's see this as a matrix product like L[i,j]*R[j,k] in Einstein notation
+// L: matrix of size si by sj
+// R: matrix of size sj by sk
+// LR: matrix of size si by sk
+template <class T> void inner_child_a (T *as, T *bs, int sj, int sk, int chunk) {
+ for (int j=0; j<chunk; j++, as+=sk, bs+=sj) op_put->map(sk,as,*bs);}
+template <class T, int sk> void inner_child_b (T *as, T *bs, int sj, int chunk) {
+ for (int j=0; j<chunk; j++, as+=sk, bs+=sj) op_put->map(sk,as,*bs);}
+
+// Inner product in a Module on the (+,*) Ring
+// | BBBBB
+// j BBBBB
+// | BBBBB
+// --j--*---k---
+// AAAAA CCCCC
+template <class T> void dot_add_mul (long sk, long sj, T *cs, T *as, T *bs) {
+ for (long k=0; k<sk; k++) {T c=0; for (long j=0; j<sj; j++) {c+=as[j]*bs[j*sk+k];} *cs++=c;}}
+template <class T, long sj> void dot_add_mul (long sk, T *cs, T *as, T *bs) {
+ for (long k=0; k<sk; k++) {T c=0; for (long j=0; j<sj; j++) {c+=as[j]*bs[j*sk+k];} *cs++=c;}}
+template <class T, long sj, long sk> void dot_add_mul (T *cs, T *as, T *bs) {
+ for (long k=0; k<sk; k++) {T c=0; for (long j=0; j<sj; j++) {c+=as[j]*bs[j*sk+k];} *cs++=c;}}
+
+GRID_INLET(0) {
+ SAME_TYPE(in,r);
+ SAME_TYPE(in,seed);
+ P<Dim> a=in->dim, b=r->dim;
+ if (a->n<1) RAISE("a: minimum 1 dimension");
+ if (b->n<1) RAISE("b: minimum 1 dimension");
+ if (seed->dim->n != 0) RAISE("seed must be a scalar");
+ int n = a->n+b->n-2;
+ SAME_DIM(1,a,a->n-1,b,0);
+ int32 v[n];
+ COPY(v,a->v,a->n-1);
+ COPY(v+a->n-1,b->v+1,b->n-1);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+ in->set_chunk(a->n-1);
+ long sjk=r->dim->prod(), sj=in->dim->prod(a->n-1), sk=sjk/sj;
+ long chunk = max(1L,GridOutlet::MAX_PACKET_SIZE/sjk);
+ T *rdata = (T *)*r;
+ r2=new Grid(new Dim(chunk*sjk),r->nt);
+ T *buf3 = (T *)*r2;
+ for (long i=0; i<sj; i++)
+ for (long j=0; j<chunk; j++)
+ COPY(buf3+(j+i*chunk)*sk,rdata+i*sk,sk);
+ use_dot = op==op_mul && fold==op_add && seed->dim->n==0 && *(T *)*seed==0;
+} GRID_FLOW {
+ long sjk=r->dim->prod(), sj=in->dim->prod(in->dim->n-1), sk=sjk/sj;
+ long chunk = max(1L,GridOutlet::MAX_PACKET_SIZE/sjk), off=chunk;
+ T buf [chunk*sk];
+ T buf2[chunk*sk];
+ if (use_dot) {
+ while (n) {
+ if (chunk*sj>n) chunk=n/sj;
+ if (sj<=4 && sk<=4) switch ((sj-1)*4+(sk-1)) {
+#define DOT_ADD_MUL_3(sj,sk) for (int i=0; i<chunk; i++) dot_add_mul<T,sj,sk>( buf2+sk*i,data+sj*i,(T *)*r);
+ case 0: DOT_ADD_MUL_3(1,1); break;
+ case 1: DOT_ADD_MUL_3(1,2); break;
+ case 2: DOT_ADD_MUL_3(1,3); break;
+ case 3: DOT_ADD_MUL_3(1,4); break;
+ case 4: DOT_ADD_MUL_3(2,1); break;
+ case 5: DOT_ADD_MUL_3(2,2); break;
+ case 6: DOT_ADD_MUL_3(2,3); break;
+ case 7: DOT_ADD_MUL_3(2,4); break;
+ case 8: DOT_ADD_MUL_3(3,1); break;
+ case 9: DOT_ADD_MUL_3(3,2); break;
+ case 10: DOT_ADD_MUL_3(3,3); break;
+ case 11: DOT_ADD_MUL_3(3,4); break;
+ case 12: DOT_ADD_MUL_3(4,1); break;
+ case 13: DOT_ADD_MUL_3(4,2); break;
+ case 14: DOT_ADD_MUL_3(4,3); break;
+ case 15: DOT_ADD_MUL_3(4,4); break;
+ } else switch (sj) {
+#define DOT_ADD_MUL_2(sj) for (int i=0; i<chunk; i++) dot_add_mul<T,sj>(sk,buf2+sk*i,data+sj*i,(T *)*r);
+ case 1: DOT_ADD_MUL_2(1); break;
+ case 2: DOT_ADD_MUL_2(2); break;
+ case 3: DOT_ADD_MUL_2(3); break;
+ case 4: DOT_ADD_MUL_2(4); break;
+ default:for (int i=0; i<chunk; i++) dot_add_mul(sk,sj,buf2+sk*i,data+sj*i,(T *)*r);
+ }
+ out->send(chunk*sk,buf2);
+ n-=chunk*sj;
+ data+=chunk*sj;
+ }
+ } else {
+ while (n) {
+ if (chunk*sj>n) chunk=n/sj;
+ op_put->map(chunk*sk,buf2,*(T *)*seed);
+ for (long i=0; i<sj; i++) {
+ switch (sk) {
+ case 1: inner_child_b<T,1>(buf,data+i,sj,chunk); break;
+ case 2: inner_child_b<T,2>(buf,data+i,sj,chunk); break;
+ case 3: inner_child_b<T,3>(buf,data+i,sj,chunk); break;
+ case 4: inner_child_b<T,4>(buf,data+i,sj,chunk); break;
+ default: inner_child_a(buf,data+i,sj,sk,chunk);
+ }
+ op->zip(chunk*sk,buf,(T *)*r2+i*off*sk);
+ fold->zip(chunk*sk,buf2,buf);
+ }
+ out->send(chunk*sk,buf2);
+ n-=chunk*sj;
+ data+=chunk*sj;
+ }
+ }
+} GRID_FINISH {
+ r2=0;
+} GRID_END
+
+GRID_INPUT(1,r) {} GRID_END
+
+\end class {install("#inner",2,1);}
+
+/* **************************************************************** */
+/*{ Dim[*As]<T>,Dim[*Bs]<T> -> Dim[*As,*Bs]<T> }*/
+\class GridOuter : FObject {
+ \attr Numop *op;
+ PtrGrid r;
+ \constructor (Numop *op, Grid *r=0) {
+ this->op = op;
+ this->r = r ? r : new Grid(new Dim(),int32_e,true);
+ }
+ \grin 0
+ \grin 1
+};
+
+GRID_INLET(0) {
+ SAME_TYPE(in,r);
+ P<Dim> a = in->dim;
+ P<Dim> b = r->dim;
+ int n = a->n+b->n;
+ int32 v[n];
+ COPY(v,a->v,a->n);
+ COPY(v+a->n,b->v,b->n);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ long b_prod = r->dim->prod();
+ if (!b_prod) return; /* nothing to do... and avoid deadly divisions by zero */
+ if (b_prod > 4) {
+ T buf[b_prod];
+ while (n) {
+ for (long j=0; j<b_prod; j++) buf[j] = *data;
+ op->zip(b_prod,buf,(T *)*r);
+ out->send(b_prod,buf);
+ data++; n--;
+ }
+ return;
+ }
+ n*=b_prod;
+ T buf[n];
+ T buf2[b_prod*64];
+ for (int i=0; i<64; i++) COPY(buf2+i*b_prod,(T *)*r,b_prod);
+ switch (b_prod) {
+ #define Z buf[k++]=data[i]
+ case 1: for (long i=0,k=0; k<n; i++) {Z;} break;
+ case 2: for (long i=0,k=0; k<n; i++) {Z;Z;} break;
+ case 3: for (long i=0,k=0; k<n; i++) {Z;Z;Z;} break;
+ case 4: for (long i=0,k=0; k<n; i++) {Z;Z;Z;Z;} break;
+ default:for (long i=0,k=0; k<n; i++) for (int j=0; j<b_prod; j++, k++) Z;
+ }
+ #undef Z
+ int ch=64*b_prod;
+ int nn=(n/ch)*ch;
+ for (int j=0; j<nn; j+=ch) op->zip(ch,buf+j,buf2);
+ op->zip(n-nn,buf+nn,buf2);
+ out->send(n,buf);
+} GRID_END
+
+GRID_INPUT(1,r) {} GRID_END
+
+\end class {install("#outer",2,1); add_creator("@outer");}
+
+//****************************************************************
+//{ Dim[]<T>,Dim[]<T>,Dim[]<T> -> Dim[A]<T> } or
+//{ Dim[B]<T>,Dim[B]<T>,Dim[B]<T> -> Dim[*As,B]<T> }
+\class GridFor : FObject {
+ \attr PtrGrid from;
+ \attr PtrGrid to;
+ \attr PtrGrid step;
+ \constructor (Grid *from, Grid *to, Grid *step) {
+ this->from.constrain(expect_max_one_dim);
+ this->to .constrain(expect_max_one_dim);
+ this->step.constrain(expect_max_one_dim);
+ this->from=from;
+ this->to =to;
+ this->step=step;
+ }
+ \decl 0 set (Grid *r=0);
+ \decl 0 bang ();
+ \grin 0 int
+ \grin 1 int
+ \grin 2 int
+ template <class T> void trigger (T bogus);
+};
+
+template <class T>
+void GridFor::trigger (T bogus) {
+ int n = from->dim->prod();
+ int32 nn[n+1];
+ T x[64*n];
+ T *fromb = (T *)*from;
+ T * tob = (T *)*to ;
+ T *stepb = (T *)*step;
+ T to2[n];
+
+ for (int i=step->dim->prod()-1; i>=0; i--)
+ if (!stepb[i]) RAISE("step must not contain zeroes");
+ for (int i=0; i<n; i++) {
+ nn[i] = (tob[i] - fromb[i] + stepb[i] - cmp(stepb[i],(T)0)) / stepb[i];
+ if (nn[i]<0) nn[i]=0;
+ to2[i] = fromb[i]+stepb[i]*nn[i];
+ }
+ P<Dim> d;
+ if (from->dim->n==0) { d = new Dim(*nn); }
+ else { nn[n]=n; d = new Dim(n+1,nn); }
+ int total = d->prod();
+ out=new GridOutlet(this,0,d,from->nt);
+ if (total==0) return;
+ int k=0;
+ for(int d=0;;d++) {
+ // here d is the dim# to reset; d=n for none
+ for(;d<n;d++) x[k+d]=fromb[d];
+ k+=n;
+ if (k==64*n) {out->send(k,x); k=0; COPY(x,x+63*n,n);}
+ else { COPY(x+k,x+k-n,n);}
+ d--;
+ // here d is the dim# to increment
+ for(;;d--) {
+ if (d<0) goto end;
+ x[k+d]+=stepb[d];
+ if (x[k+d]!=to2[d]) break;
+ }
+ }
+ end: if (k) out->send(k,x);
+}
+
+\def 0 bang () {
+ SAME_TYPE(from,to);
+ SAME_TYPE(from,step);
+ if (!from->dim->equal(to->dim) || !to->dim->equal(step->dim))
+ RAISE("dimension mismatch: from:%s to:%s step:%s",
+ from->dim->to_s(),to->dim->to_s(),step->dim->to_s());
+#define FOO(T) trigger((T)0);
+ TYPESWITCH_JUSTINT(from->nt,FOO,);
+#undef FOO
+}
+
+\def 0 set (Grid *r) { from=new Grid(argv[0]); }
+GRID_INPUT(2,step) {} GRID_END
+GRID_INPUT(1,to) {} GRID_END
+GRID_INPUT(0,from) {_0_bang(0,0);} GRID_END
+\end class {install("#for",3,1); add_creator("@for");}
+
+//****************************************************************
+\class GridFinished : FObject {
+ \constructor () {}
+ \grin 0
+};
+GRID_INLET(0) {
+ //in->set_mode(0);
+} GRID_FINISH {
+ outlet_bang(bself->outlets[0]);
+} GRID_END
+\end class {install("#finished",1,1); add_creator("@finished");}
+\class GridDim : FObject {
+ \constructor () {}
+ \grin 0
+};
+GRID_INLET(0) {
+ GridOutlet out(this,0,new Dim(in->dim->n));
+ out.send(in->dim->n,in->dim->v);
+ //in->set_mode(0);
+} GRID_END
+\end class {install("#dim",1,1); add_creator("@dim");}
+\class GridType : FObject {
+ \constructor () {}
+ \grin 0
+};
+GRID_INLET(0) {
+ outlet_symbol(bself->outlets[0],gensym(const_cast<char *>(number_type_table[in->nt].name)));
+ /*in->set_mode(0);*/
+} GRID_END
+\end class {install("#type",1,1); add_creator("@type");}
+
+//****************************************************************
+//{ Dim[*As]<T>,Dim[B] -> Dim[*Cs]<T> }
+\class GridRedim : FObject {
+ \attr P<Dim> dim;
+ PtrGrid dim_grid;
+ PtrGrid temp; // temp->dim is not of the same shape as dim
+ ~GridRedim() {}
+ \constructor (Grid *d) {
+ dim_grid.constrain(expect_dim_dim_list);
+ dim_grid=d;
+ dim = dim_grid->to_dim();
+ // if (!dim->prod()) RAISE("target grid size must not be zero");
+ }
+ \grin 0
+ \grin 1 int32
+};
+
+GRID_INLET(0) {
+ long a=in->dim->prod(), b=dim->prod();
+ if (a<b) temp=new Grid(new Dim(a),in->nt);
+ out=new GridOutlet(this,0,dim,in->nt);
+} GRID_FLOW {
+ long i = dex;
+ if (!temp) {
+ long n2 = min(n,dim->prod()-i);
+ if (n2>0) out->send(n2,data);
+ // discard other values if any
+ } else {
+ long n2 = min(n,in->dim->prod()-i);
+ COPY((T *)*temp+i,data,n2);
+ if (n2>0) out->send(n2,data);
+ }
+} GRID_FINISH {
+ if (!!temp) {
+ long a = in->dim->prod(), b = dim->prod();
+ if (a) {
+ for (long i=a; i<b; i+=a) out->send(min(a,b-i),(T *)*temp);
+ } else {
+ T foo[1]={0}; for (long i=0; i<b; i++) out->send(1,foo);
+ }
+ }
+ temp=0;
+} GRID_END
+
+GRID_INPUT(1,dim_grid) {
+ P<Dim> d = dim_grid->to_dim();
+// if (!d->prod()) RAISE("target grid size must not be zero"); else post("d->prod=%d",d->prod());
+ dim = d;
+} GRID_END
+
+\end class {install("#redim",2,1); add_creator("@redim");}
+
+//****************************************************************
+\class GridJoin : FObject {
+ \attr int which_dim;
+ PtrGrid r;
+ \grin 0
+ \grin 1
+ \constructor (int which_dim=-1, Grid *r=0) {
+ this->which_dim = which_dim;
+ this->r=r;
+ }
+};
+
+GRID_INLET(0) {
+ NOTEMPTY(r);
+ SAME_TYPE(in,r);
+ P<Dim> d = in->dim;
+ if (d->n != r->dim->n) RAISE("wrong number of dimensions");
+ int w = which_dim;
+ if (w<0) w+=d->n;
+ if (w<0 || w>=d->n)
+ RAISE("can't join on dim number %d on %d-dimensional grids",
+ which_dim,d->n);
+ int32 v[d->n];
+ for (int i=0; i<d->n; i++) {
+ v[i] = d->get(i);
+ if (i==w) {
+ v[i]+=r->dim->v[i];
+ } else {
+ if (v[i]!=r->dim->v[i]) RAISE("dimensions mismatch: dim #%i, left is %d, right is %d",i,v[i],r->dim->v[i]);
+ }
+ }
+ out=new GridOutlet(this,0,new Dim(d->n,v),in->nt);
+ in->set_chunk(w);
+} GRID_FLOW {
+ int w = which_dim;
+ if (w<0) w+=in->dim->n;
+ long a = in->dim->prod(w);
+ long b = r->dim->prod(w);
+ T *data2 = (T *)*r + dex*b/a;
+ if (a==3 && b==1) {
+ int m = n+n*b/a;
+ T data3[m];
+ T *data4 = data3;
+ while (n) {
+ SCOPY(data4,data,3); SCOPY(data4+3,data2,1);
+ n-=3; data+=3; data2+=1; data4+=4;
+ }
+ out->send(m,data3);
+ } else if (a+b<=16) {
+ int m = n+n*b/a;
+ T data3[m];
+ int i=0;
+ while (n) {
+ COPY(data3+i,data,a); data+=a; i+=a; n-=a;
+ COPY(data3+i,data2,b); data2+=b; i+=b;
+ }
+ out->send(m,data3);
+ } else {
+ while (n) {
+ out->send(a,data);
+ out->send(b,data2);
+ data+=a; data2+=b; n-=a;
+ }
+ }
+} GRID_FINISH {
+ if (in->dim->prod()==0) out->send(r->dim->prod(),(T *)*r);
+} GRID_END
+
+GRID_INPUT(1,r) {} GRID_END
+
+\end class {install("#join",2,1); add_creator("@join");}
+
+//****************************************************************
+\class GridGrade : FObject {
+ \constructor () {}
+ \grin 0
+};
+
+typedef int (*comparator_t)(const void *, const void *);
+
+template <class T> struct GradeFunction {
+ static int comparator (T **a, T **b) {return **a-**b;}};
+#define FOO(T) \
+template <> struct GradeFunction<T> { \
+ static int comparator (T **a, T **b) {T x = **a-**b; return x<0 ? -1 : x>0;}};
+FOO(int64)
+FOO(float32)
+FOO(float64)
+#undef FOO
+
+GRID_INLET(0) {
+ out=new GridOutlet(this,0,in->dim);
+ in->set_chunk(in->dim->n-1);
+} GRID_FLOW {
+ long m = in->dim->prod(in->dim->n-1);
+ T *foo[m];
+ T bar[m];
+ for (; n; n-=m,data+=m) {
+ for (int i=0; i<m; i++) foo[i] = &data[i];
+ qsort(foo,m,sizeof(T *),(comparator_t)GradeFunction<T>::comparator);
+ for (int i=0; i<m; i++) bar[i] = foo[i]-(T *)data;
+ out->send(m,bar);
+ }
+} GRID_END
+
+\end class {install("#grade",1,1); add_creator("@grade");}
+
+//****************************************************************
+//\class GridMedian : FObject
+//****************************************************************
+
+\class GridTranspose : FObject {
+ \attr int dim1;
+ \attr int dim2;
+ int d1,d2,na,nb,nc,nd; // temporaries
+ \constructor (int dim1=0, int dim2=1) {
+ this->dim1 = dim1;
+ this->dim2 = dim2;
+ }
+ \decl 1 float (int dim1);
+ \decl 2 float (int dim2);
+ \grin 0
+};
+
+\def 1 float (int dim1) { this->dim1=dim1; }
+\def 2 float (int dim2) { this->dim2=dim2; }
+
+GRID_INLET(0) {
+ int32 v[in->dim->n];
+ COPY(v,in->dim->v,in->dim->n);
+ d1=dim1; d2=dim2;
+ if (d1<0) d1+=in->dim->n;
+ if (d2<0) d2+=in->dim->n;
+ if (d1>=in->dim->n || d2>=in->dim->n || d1<0 || d2<0)
+ RAISE("would swap dimensions %d and %d but this grid has only %d dimensions", dim1,dim2,in->dim->n);
+ memswap(v+d1,v+d2,1);
+ if (d1==d2) {
+ out=new GridOutlet(this,0,new Dim(in->dim->n,v), in->nt);
+ } else {
+ nd = in->dim->prod(1+max(d1,d2));
+ nc = in->dim->v[max(d1,d2)];
+ nb = nc&&nd ? in->dim->prod(1+min(d1,d2))/nc/nd : 0;
+ na = in->dim->v[min(d1,d2)];
+ out=new GridOutlet(this,0,new Dim(in->dim->n,v), in->nt);
+ in->set_chunk(min(d1,d2));
+ }
+ // Turns a Grid[*,na,*nb,nc,*nd] into a Grid[*,nc,*nb,na,*nd].
+} GRID_FLOW {
+ //T res[na*nb*nc*nd];
+ T *res = NEWBUF(T,na*nb*nc*nd);
+ if (dim1==dim2) { out->send(n,data); return; }
+ int prod = na*nb*nc*nd;
+ for (; n; n-=prod, data+=prod) {
+ for (long a=0; a<na; a++)
+ for (long b=0; b<nb; b++)
+ for (long c=0; c<nc; c++)
+ COPY(res +((c*nb+b)*na+a)*nd,
+ data+((a*nb+b)*nc+c)*nd,nd);
+ out->send(na*nb*nc*nd,res);
+ }
+ DELBUF(res); //!@#$ if an exception was thrown by out->send, this never gets done
+} GRID_END
+
+\end class {install("#transpose",3,1); add_creator("@transpose");}
+
+//****************************************************************
+\class GridReverse : FObject {
+ \attr int dim1; // dimension to act upon
+ int d; // temporaries
+ \constructor (int dim1=0) {this->dim1 = dim1;}
+ \decl 1 float (int dim1);
+ \grin 0
+};
+
+\def 1 float (int dim1) { this->dim1=dim1; }
+
+GRID_INLET(0) {
+ d=dim1;
+ if (d<0) d+=in->dim->n;
+ if (d>=in->dim->n || d<0)
+ RAISE("would reverse dimension %d but this grid has only %d dimensions",
+ dim1,in->dim->n);
+ out=new GridOutlet(this,0,new Dim(in->dim->n,in->dim->v), in->nt);
+ in->set_chunk(d);
+} GRID_FLOW {
+ long f1=in->dim->prod(d), f2=in->dim->prod(d+1);
+ while (n) {
+ long hf1=f1/2;
+ T *data2 = data+f1-f2;
+ for (long i=0; i<hf1; i+=f2) memswap(data+i,data2-i,f2);
+ out->send(f1,data);
+ data+=f1; n-=f1;
+ }
+} GRID_END
+
+\end class {install("#reverse",2,1);}
+
+//****************************************************************
+\class GridCentroid : FObject {
+ \constructor () {}
+ \grin 0 int
+ int sumx,sumy,sum,y; // temporaries
+};
+
+GRID_INLET(0) {
+ if (in->dim->n != 3) RAISE("expecting 3 dims");
+ if (in->dim->v[2] != 1) RAISE("expecting 1 channel");
+ in->set_chunk(1);
+ out=new GridOutlet(this,0,new Dim(2), in->nt);
+ sumx=0; sumy=0; sum=0; y=0;
+} GRID_FLOW {
+ int sx = in->dim->v[1];
+ while (n) {
+ for (int x=0; x<sx; x++) {
+ sumx+=x*data[x];
+ sumy+=y*data[x];
+ sum += data[x];
+ }
+ n-=sx;
+ data+=sx;
+ y++;
+ }
+} GRID_FINISH {
+ int32 blah[2];
+ blah[0] = sum ? sumy/sum : 0;
+ blah[1] = sum ? sumx/sum : 0;
+ out->send(2,blah);
+ outlet_float(bself->outlets[1],blah[0]);
+ outlet_float(bself->outlets[2],blah[1]);
+} GRID_END
+
+\end class {install("#centroid",1,3);}
+
+//****************************************************************
+static void expect_pair (P<Dim> dim) {if (dim->prod()!=2) RAISE("expecting only two numbers. Dim(2)");}
+
+\class GridMoment : FObject {
+ \constructor (int order=1) {
+ offset.constrain(expect_pair);
+ //t_atom2 a[2] = {t_atom2(0),t_atom2(0)};
+ t_atom a[2]; SETFLOAT(a,0); SETFLOAT(a+1,0);
+ offset=new Grid(2,a,int32_e);
+ if (order!=1 && order!=2) RAISE("supports only orders 1 and 2 for now");
+ this->order=order;
+ }
+ \grin 0 int
+ \grin 1 int
+ \attr int order; // order
+ \attr PtrGrid offset;
+ int64 sumy,sumxy,sumx,sum,y; // temporaries
+};
+
+GRID_INLET(0) {
+ if (in->dim->n != 3) RAISE("expecting 3 dims");
+ if (in->dim->v[2] != 1) RAISE("expecting 1 channel");
+ in->set_chunk(1);
+ switch (order) {
+ case 1: out=new GridOutlet(this,0,new Dim(2 ), in->nt); break;
+ case 2: out=new GridOutlet(this,0,new Dim(2,2), in->nt); break;
+ default: RAISE("supports only orders 1 and 2 for now");
+ }
+ sumx=0; sumy=0; sumxy=0; sum=0; y=0;
+} GRID_FLOW {
+ int sx = in->dim->v[1];
+ int oy = ((int*)*offset)[0];
+ int ox = ((int*)*offset)[1];
+ while (n) {
+ switch (order) {
+ case 1:
+ for (int x=0; x<sx; x++) {
+ sumy+=y*data[x];
+ sumx+=x*data[x];
+ sum += data[x];
+ }
+ break;
+ case 2:
+ for (int x=0; x<sx; x++) {
+ int ty=y-oy;
+ int tx=x-ox;
+ sumy +=ty*ty*data[x];
+ sumxy+=tx*ty*data[x];
+ sumx +=tx*tx*data[x];
+ sum += data[x];
+ }
+ }
+ n-=sx;
+ data+=sx;
+ y++;
+ }
+} GRID_FINISH {
+ int32 blah[4];
+ switch (order) {
+ case 1: /* centroid vector */
+ blah[0] = sum ? sumy/sum : 0;
+ blah[1] = sum ? sumx/sum : 0;
+ out->send(2,blah);
+ break;
+ case 2: /* covariance matrix */
+ blah[0] = sum ? sumy/sum : 0;
+ blah[1] = sum ? sumxy/sum : 0;
+ blah[2] = sum ? sumxy/sum : 0;
+ blah[3] = sum ? sumx/sum : 0;
+ out->send(4,blah);
+ break;
+ }
+} GRID_END
+
+GRID_INPUT(1,offset) {} GRID_END
+
+\end class {install("#moment",2,1);}
+
+//****************************************************************
+\class GridLabelling : FObject {
+ \grin 0
+ \attr int form();
+ \attr int form_val;
+ \constructor (int form=0) {form_val=form; initialize3();}
+ void initialize3() {bself->noutlets_set(form_val ? 2 : 4);}
+};
+struct Stats {
+ int64 yy,yx,xx,y,x,area;
+ int64 x1,x2;
+ Stats() {yy=yx=xx=y=x=area=0;}
+};
+#define AT(y,x) dat[(y)*sx+(x)]
+template <class T> void flood_fill(T *dat, int sy, int sx, int y, int x, Stats *stat, int label, int form) {
+ /* find x1,x2 such that all the x of that horizontal segment are x1<=x<x2 */
+ int x2; for (x2=x; x2<sx; x2++) if (AT(y,x2)!=1) break;
+ int x1; for (x1=x; x1>=0; x1--) if (AT(y,x1)!=1) break;
+ x1++;
+ if (form==0) {
+ for (x=x1; x<x2; x++) {
+ AT(y,x)=label;
+ stat->yy += y*y; stat->y += y;
+ stat->yx += y*x; stat->area++;
+ stat->xx += x*x; stat->x += x;
+ }
+ for (x=x1; x<x2; x++) {
+ if (y>0 && AT(y-1,x)==1) flood_fill(dat,sy,sx,y-1,x,stat,label,form);
+ if (y<sy-1 && AT(y+1,x)==1) flood_fill(dat,sy,sx,y+1,x,stat,label,form);
+ }
+ } else {
+ for (x=x1; x<x2; x++) {
+ AT(y,x)=label;
+ }
+ stat->y=y;
+ stat->x1=x1;
+ stat->x2=x2;
+ }
+}
+
+GRID_INLET(0) {
+ if (in->dim->n<2 || in->dim->prod(2)!=1) RAISE("requires dim (y,x) or (y,x,1)");
+ in->set_chunk(0);
+} GRID_FLOW {
+ int sy=in->dim->v[0], sx=in->dim->v[1];
+ T *dat = NEWBUF(T,n);
+ for (int i=0; i<n; i++) dat[i]=data[i];
+ int y,x=0,label=2;
+ for (y=0; y<sy; y++) for (x=0; x<sx; x++) {
+ if (dat[y*sx+x]!=1) continue;
+ Stats s;
+ flood_fill(dat,sy,sx,y,x,&s,label,form_val);
+ if (form_val==0) {
+ float32 cooked[6] = {
+ (s.yy-s.y*s.y/s.area)/s.area,
+ (s.yx-s.y*s.x/s.area)/s.area,
+ (s.yx-s.y*s.x/s.area)/s.area,
+ (s.xx-s.x*s.x/s.area)/s.area,
+ s.y/s.area,
+ s.x/s.area};
+ float a[] = {s.area};
+ send_out(3,1,a);
+ GridOutlet o2(this,2,new Dim(2)); o2.send(2,cooked+4);
+ GridOutlet o1(this,1,new Dim(2,2)); o1.send(4,cooked);
+ } else {
+ float32 cooked[4] = {s.y,s.x1,s.y,s.x2};
+ GridOutlet o1(this,1,new Dim(2,2)); o1.send(4,cooked);
+ }
+ label++;
+ }
+ out = new GridOutlet(this,0,new Dim(sy,sx,1),in->nt);
+ out->send(n,dat);
+ DELBUF(dat);
+} GRID_END
+
+\def int form() {return form_val;}
+\def 0 form(int form) {
+ if (form<0 || form>1) RAISE("form must be 0 or 1, not %d",form);
+ form_val=form;
+ initialize3();
+}
+\end class {install("#labelling",1,0); add_creator("#labeling");}
+
+//****************************************************************
+\class GridPerspective : FObject {
+ \attr int32 z;
+ \grin 0
+ \constructor (int32 z=256) {this->z=z;}
+};
+GRID_INLET(0) {
+ int n = in->dim->n;
+ int32 v[n];
+ COPY(v,in->dim->v,n);
+ v[n-1]--;
+ in->set_chunk(in->dim->n-1);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ int m = in->dim->prod(in->dim->n-1);
+ for (; n; n-=m,data+=m) {
+ op_mul->map(m-1,data,(T)z);
+ op_div->map(m-1,data,data[m-1]);
+ out->send(m-1,data);
+ }
+} GRID_END
+\end class {install("#perspective",1,1); add_creator("@perspective");}
+
+//****************************************************************
+\class GridBorder : FObject {
+ \attr P<Dim> diml;
+ \attr P<Dim> dimr;
+ PtrGrid diml_grid;
+ PtrGrid dimr_grid;
+ \grin 0
+ \grin 1 int
+ \grin 2 int
+ \constructor (Grid *dl=0, Grid *dr=0) {
+ t_atom a[2]; SETFLOAT(a+0,1); SETFLOAT(a+1,1); SETFLOAT(a+2,0);
+ diml_grid=dl?dl:new Grid(3,a,int32_e);
+ dimr_grid=dr?dr:new Grid(3,a,int32_e);
+ diml = diml_grid->to_dim();
+ dimr = dimr_grid->to_dim();
+ }
+};
+
+GRID_INLET(0) {
+ int n = in->dim->n;
+ if (n!=3) RAISE("only 3 dims supported for now");
+ if (diml->n != n) RAISE("diml mismatch");
+ if (dimr->n != n) RAISE("dimr mismatch");
+ if (diml->v[2] || dimr->v[2]) RAISE("can't augment channels (todo)");
+ int32 v[n];
+ for (int i=0; i<n; i++) v[i]=in->dim->v[i]+diml->v[i]+dimr->v[i];
+ in->set_chunk(0);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ int sy = in->dim->v[0];
+ int sx = in->dim->v[1]; int zx = sx+diml->v[1]+dimr->v[1];
+ int sc = in->dim->v[2]; int zc = sc+diml->v[2]+dimr->v[2];
+ int sxc = sx*sc; int zxc = zx*zc;
+ int32 duh[zxc];
+ for (int x=0; x<zxc; x++) duh[x]=0;
+ for (int y=0; y<diml->v[0]; y++) out->send(zxc,duh);
+ for (int y=0; y<sy; y++) {
+ out->send(diml->v[1]*sc,duh);
+ out->send(sxc,data+y*sxc);
+ out->send(dimr->v[1]*sc,duh);
+ }
+ for (int i=0; i<dimr->v[0]; i++) out->send(zxc,duh);
+} GRID_END
+
+GRID_INPUT(1,diml_grid) {diml = diml_grid->to_dim();} GRID_END
+GRID_INPUT(2,dimr_grid) {dimr = dimr_grid->to_dim();} GRID_END
+
+\end class {install("#border",3,1);}
+
+static void expect_picture (P<Dim> d) {
+ if (d->n!=3) RAISE("(height,width,chans) dimensions please");}
+static void expect_rgb_picture (P<Dim> d) {
+ expect_picture(d);
+ if (d->get(2)!=3) RAISE("(red,green,blue) channels please");}
+static void expect_rgba_picture (P<Dim> d) {
+ expect_picture(d);
+ if (d->get(2)!=4) RAISE("(red,green,blue,alpha) channels please");}
+
+//****************************************************************
+//{ Dim[A,B,*Cs]<T>,Dim[D,E]<T> -> Dim[A,B,*Cs]<T> }
+
+static void expect_convolution_matrix (P<Dim> d) {
+ if (d->n != 2) RAISE("only exactly two dimensions allowed for now (got %d)",
+ d->n);
+}
+
+// entry in a compiled convolution kernel
+struct PlanEntry {long y,x; bool neutral;};
+
+\class GridConvolve : FObject {
+ \attr Numop *op;
+ \attr Numop *fold;
+ \attr PtrGrid seed;
+ \attr PtrGrid b;
+ \attr bool wrap;
+ \attr bool anti;
+ PtrGrid a;
+ int plann;
+ PlanEntry *plan;
+ int margx,margy; // margins
+ \constructor (Grid *r=0) {
+ plan=0;
+ b.constrain(expect_convolution_matrix); plan=0;
+ this->op = op_mul;
+ this->fold = op_add;
+ this->seed = new Grid(new Dim(),int32_e,true);
+ this->b= r ? r : new Grid(new Dim(1,1),int32_e,true);
+ this->wrap = true;
+ this->anti = true;
+ }
+ \grin 0
+ \grin 1
+ template <class T> void copy_row (T *buf, long sx, long y, long x);
+ template <class T> void make_plan (T bogus);
+ ~GridConvolve () {if (plan) delete[] plan;}
+};
+
+template <class T> void GridConvolve::copy_row (T *buf, long sx, long y, long x) {
+ long day = a->dim->get(0), dax = a->dim->get(1), dac = a->dim->prod(2);
+ y=mod(y,day); x=mod(x,dax);
+ T *ap = (T *)*a + y*dax*dac;
+ while (sx) {
+ long sx1 = min(sx,dax-x);
+ COPY(buf,ap+x*dac,sx1*dac);
+ x=0;
+ buf += sx1*dac;
+ sx -= sx1;
+ }
+}
+
+template <class T> void GridConvolve::make_plan (T bogus) {
+ P<Dim> da = a->dim, db = b->dim;
+ long dby = db->get(0);
+ long dbx = db->get(1);
+ if (plan) delete[] plan;
+ plan = new PlanEntry[dbx*dby];
+ long i=0;
+ for (long y=0; y<dby; y++) {
+ for (long x=0; x<dbx; x++) {
+ long k = anti ? y*dbx+x : (dby-1-y)*dbx+(dbx-1-x);
+ T rh = ((T *)*b)[k];
+ bool neutral = op->on(rh)->is_neutral( rh,at_right);
+ bool absorbent = op->on(rh)->is_absorbent(rh,at_right);
+ T foo[1]={0};
+ if (absorbent) {
+ op->map(1,foo,rh);
+ absorbent = fold->on(rh)->is_neutral(foo[0],at_right);
+ }
+ if (absorbent) continue;
+ plan[i].y = y;
+ plan[i].x = x;
+ plan[i].neutral = neutral;
+ i++;
+ }
+ }
+ plann = i;
+}
+
+GRID_INLET(0) {
+ SAME_TYPE(in,b);
+ SAME_TYPE(in,seed);
+ P<Dim> da=in->dim, db=b->dim;
+ if (!db) RAISE("right inlet has no grid");
+ if (!seed) RAISE("seed missing");
+ if (db->n != 2) RAISE("right grid must have two dimensions");
+ if (da->n < 2) RAISE("left grid has less than two dimensions");
+ if (seed->dim->n != 0) RAISE("seed must be scalar");
+ if (da->get(0) < db->get(0)) RAISE("grid too small (y): %d < %d", da->get(0), db->get(0));
+ if (da->get(1) < db->get(1)) RAISE("grid too small (x): %d < %d", da->get(1), db->get(1));
+ margy = (db->get(0)-1)/2;
+ margx = (db->get(1)-1)/2;
+ //if (a) post("for %p, a->dim=%s da=%s",this,a->dim->to_s(),da->to_s());
+ if (!a || !a->dim->equal(da)) a=new Grid(da,in->nt); // with this condition it's 2% faster on Linux but takes more RAM.
+ //a=new Grid(da,in->nt); // with this condition it's 2% faster but takes more RAM.
+ int v[da->n]; COPY(v,da->v,da->n);
+ if (!wrap) {v[0]-=db->v[0]-1; v[1]-=db->v[1]-1;}
+ out=new GridOutlet(this,0,new Dim(da->n,v),in->nt);
+} GRID_FLOW {
+ COPY((T *)*a+dex, data, n);
+} GRID_FINISH {
+ make_plan((T)0);
+ long dbx = b->dim->get(1);
+ long dby = b->dim->get(0);
+ long day = out->dim->get(0);
+ long n = out->dim->prod(1);
+ long sx = out->dim->get(1)+dbx-1;
+ long sxc = out->dim->prod(2)*sx;
+ T buf[n];
+ T buf2[sxc];
+ T orh=0;
+ for (long ay=0; ay<day; ay++) {
+ op_put->map(n,buf,*(T *)*seed);
+ for (long i=0; i<plann; i++) {
+ long by = plan[i].y;
+ long bx = plan[i].x;
+ long k = anti ? by*dbx+bx : (dby-1-by)*dbx+(dbx-1-bx);
+ T rh = ((T *)*b)[k];
+ if (i==0 || by!=plan[i-1].y || orh!=rh) {
+ if (wrap) copy_row(buf2,sx,ay+by-margy,-margx);
+ else copy_row(buf2,sx,ay+by,0);
+ if (!plan[i].neutral) op->map(sxc,buf2,rh);
+ }
+ fold->zip(n,buf,buf2+bx*out->dim->prod(2));
+ orh=rh;
+ }
+ out->send(n,buf);
+ }
+ //a=0; // comment this out when trying to recycle a (use the dim->equal above)
+} GRID_END
+
+GRID_INPUT(1,b) {} GRID_END
+
+\end class {install("#convolve",2,1);}
+
+/* ---------------------------------------------------------------- */
+/* "#scale_by" does quick scaling of pictures by integer factors */
+/*{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }*/
+
+static void expect_scale_factor (P<Dim> dim) {
+ if (dim->prod()!=1 && dim->prod()!=2)
+ RAISE("expecting only one or two numbers");
+}
+
+\class GridScaleBy : FObject {
+ \attr PtrGrid scale; // integer scale factor
+ int scaley;
+ int scalex;
+ \constructor (Grid *factor=0) {
+ scale.constrain(expect_scale_factor);
+ t_atom a[1]; SETFLOAT(a,2);
+ scale = factor?factor:new Grid(1,a,int32_e);
+ prepare_scale_factor();
+ }
+ \grin 0
+ \grin 1
+ void prepare_scale_factor () {
+ scaley = ((int32 *)*scale)[0];
+ scalex = ((int32 *)*scale)[scale->dim->prod()==1 ? 0 : 1];
+ if (scaley<1) scaley=2;
+ if (scalex<1) scalex=2;
+ }
+};
+
+GRID_INLET(0) {
+ P<Dim> a = in->dim;
+ expect_picture(a);
+ out=new GridOutlet(this,0,new Dim(a->get(0)*scaley,a->get(1)*scalex,a->get(2)),in->nt);
+ in->set_chunk(1);
+} GRID_FLOW {
+ int rowsize = in->dim->prod(1);
+ T buf[rowsize*scalex];
+ int chans = in->dim->get(2);
+ #define Z(z) buf[p+z]=data[i+z]
+ for (; n>0; data+=rowsize, n-=rowsize) {
+ int p=0;
+ #define LOOP(z) \
+ for (int i=0; i<rowsize; i+=z) \
+ for (int k=0; k<scalex; k++, p+=z)
+ switch (chans) {
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default: LOOP(chans) {for (int c=0; c<chans; c++) Z(c);}
+ }
+ #undef LOOP
+ for (int j=0; j<scaley; j++) out->send(rowsize*scalex,buf);
+ }
+ #undef Z
+} GRID_END
+
+GRID_INPUT(1,scale) {prepare_scale_factor();} GRID_END
+
+\end class {install("#scale_by",2,1); add_creator("@scale_by");}
+
+// ----------------------------------------------------------------
+//{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }
+\class GridDownscaleBy : FObject {
+ \attr PtrGrid scale;
+ \attr bool smoothly;
+ int scaley;
+ int scalex;
+ PtrGrid temp;
+ \constructor (Grid *factor=0, t_symbol *option=0) {
+ scale.constrain(expect_scale_factor);
+ t_atom a[1]; SETFLOAT(a,2);
+ scale = factor?factor:new Grid(1,a,int32_e);
+ prepare_scale_factor();
+ smoothly = option==gensym("smoothly");
+ }
+ \grin 0
+ \grin 1
+ void prepare_scale_factor () {
+ scaley = ((int32 *)*scale)[0];
+ scalex = ((int32 *)*scale)[scale->dim->prod()==1 ? 0 : 1];
+ if (scaley<1) scaley=2;
+ if (scalex<1) scalex=2;
+ }
+};
+
+GRID_INLET(0) {
+ P<Dim> a = in->dim;
+ if (a->n!=3) RAISE("(height,width,chans) please");
+ out=new GridOutlet(this,0,new Dim(a->get(0)/scaley,a->get(1)/scalex,a->get(2)),in->nt);
+ in->set_chunk(1);
+ // i don't remember why two rows instead of just one.
+ temp=new Grid(new Dim(2,in->dim->get(1)/scalex,in->dim->get(2)),in->nt);
+} GRID_FLOW {
+ int rowsize = in->dim->prod(1);
+ int rowsize2 = temp->dim->prod(1);
+ T *buf = (T *)*temp; //!@#$ maybe should be something else than T ?
+ int xinc = in->dim->get(2)*scalex;
+ int y = dex / rowsize;
+ int chans=in->dim->get(2);
+ #define Z(z) buf[p+z]+=data[i+z]
+ if (smoothly) {
+ while (n>0) {
+ if (y%scaley==0) CLEAR(buf,rowsize2);
+ #define LOOP(z) \
+ for (int i=0,p=0; p<rowsize2; p+=z) \
+ for (int j=0; j<scalex; j++,i+=z)
+ switch (chans) {
+ case 1: LOOP(1) {Z(0);} break;
+ case 2: LOOP(2) {Z(0);Z(1);} break;
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);} break;
+ }
+ #undef LOOP
+ y++;
+ if (y%scaley==0 && out->dim) {
+ op_div->map(rowsize2,buf,(T)(scalex*scaley));
+ out->send(rowsize2,buf);
+ CLEAR(buf,rowsize2);
+ }
+ data+=rowsize;
+ n-=rowsize;
+ }
+ #undef Z
+ } else {
+ #define Z(z) buf[p+z]=data[i+z]
+ for (; n>0 && out->dim; data+=rowsize, n-=rowsize,y++) {
+ if (y%scaley!=0) continue;
+ #define LOOP(z) for (int i=0,p=0; p<rowsize2; i+=xinc, p+=z)
+ switch(in->dim->get(2)) {
+ case 1: LOOP(1) {Z(0);} break;
+ case 2: LOOP(2) {Z(0);Z(1);} break;
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);}break;
+ }
+ #undef LOOP
+ out->send(rowsize2,buf);
+ }
+ }
+ #undef Z
+} GRID_END
+
+GRID_INPUT(1,scale) {prepare_scale_factor();} GRID_END
+
+\end class {install("#downscale_by",2,1); add_creator("@downscale_by");}
+
+//****************************************************************
+\class GridLayer : FObject {
+ PtrGrid r;
+ \constructor () {r.constrain(expect_rgb_picture);}
+ \grin 0 int
+ \grin 1 int
+};
+
+GRID_INLET(0) {
+ NOTEMPTY(r);
+ SAME_TYPE(in,r);
+ P<Dim> a = in->dim;
+ expect_rgba_picture(a);
+ if (a->get(1)!=r->dim->get(1)) RAISE("same width please");
+ if (a->get(0)!=r->dim->get(0)) RAISE("same height please");
+ in->set_chunk(2);
+ out=new GridOutlet(this,0,r->dim);
+} GRID_FLOW {
+ T *rr = ((T *)*r) + dex*3/4;
+ T foo[n*3/4];
+#define COMPUTE_ALPHA(c,a) \
+ foo[j+c] = (data[i+c]*data[i+a] + rr[j+c]*(256-data[i+a])) >> 8
+ for (int i=0,j=0; i<n; i+=4,j+=3) {
+ COMPUTE_ALPHA(0,3);
+ COMPUTE_ALPHA(1,3);
+ COMPUTE_ALPHA(2,3);
+ }
+#undef COMPUTE_ALPHA
+ out->send(n*3/4,foo);
+} GRID_END
+
+GRID_INPUT(1,r) {} GRID_END
+
+\end class {install("#layer",2,1); add_creator("@layer");}
+
+// ****************************************************************
+// pad1,pad2 only are there for 32-byte alignment
+struct Line {int32 y1,x1,y2,x2,x,m,ox,pad2;};
+
+static void expect_polygon (P<Dim> d) {
+ if (d->n!=2 || d->get(1)!=2) RAISE("expecting Dim[n,2] polygon");
+}
+
+enum DrawMode {DRAW_FILL,DRAW_LINE,DRAW_POINT};
+enum OmitMode {OMIT_NONE,OMIT_LAST,OMIT_ODD};
+DrawMode convert(const t_atom &x, DrawMode *foo) {
+ t_symbol *s = convert(x,(t_symbol **)0);
+ if (s==gensym("fill")) return DRAW_FILL;
+ if (s==gensym("line")) return DRAW_LINE;
+ if (s==gensym("point")) return DRAW_POINT;
+ RAISE("unknown DrawMode '%s' (want fill or line)",s->s_name);
+}
+OmitMode convert(const t_atom &x, OmitMode *foo) {
+ t_symbol *s = convert(x,(t_symbol **)0);
+ if (s==gensym("none")) return OMIT_NONE;
+ if (s==gensym("last")) return OMIT_LAST;
+ if (s==gensym("odd")) return OMIT_ODD;
+ RAISE("unknown OmitMode '%s' (want none or last or odd)",s->s_name);
+}
+\class DrawPolygon : FObject {
+ \attr Numop *op;
+ \attr PtrGrid color;
+ \attr PtrGrid polygon;
+ \attr DrawMode draw;
+ \attr OmitMode omit;
+ PtrGrid color2;
+ PtrGrid lines;
+ int lines_start;
+ int lines_stop;
+ \constructor (Numop *op=op_put, Grid *color=0, Grid *polygon=0) {
+ draw=DRAW_FILL;
+ omit=OMIT_NONE;
+ this->color.constrain(expect_max_one_dim);
+ this->polygon.constrain(expect_polygon);
+ this->op = op;
+ if (color) this->color=color;
+ if (polygon) {this->polygon=polygon; init_lines();}
+ }
+ \grin 0
+ \grin 1
+ \grin 2 int32
+ void init_lines();
+ void changed(t_symbol *s) {init_lines();}
+};
+void DrawPolygon::init_lines () {
+ if (!polygon) return;
+ int tnl = polygon->dim->get(0);
+ int nl = omit==OMIT_LAST ? tnl-1 : omit==OMIT_ODD ? (tnl+1)/2 : tnl;
+ lines=new Grid(new Dim(nl,8), int32_e);
+ Line *ld = (Line *)(int32 *)*lines;
+ int32 *pd = *polygon;
+ for (int i=0,j=0; i<nl; i++) {
+ ld[i].y1 = pd[j+0];
+ ld[i].x1 = pd[j+1];
+ j=(j+2)%(2*tnl);
+ ld[i].y2 = pd[j+0];
+ ld[i].x2 = pd[j+1];
+ if (omit==OMIT_ODD) j=(j+2)%(2*tnl);
+ if (draw!=DRAW_POINT) if (ld[i].y1>ld[i].y2) memswap((int32 *)(ld+i)+0,(int32 *)(ld+i)+2,2);
+ long dy = ld[i].y2-ld[i].y1;
+ long dx = ld[i].x2-ld[i].x1;
+ ld[i].m = dy ? (dx<<16)/dy : 0;
+ }
+}
+
+static int order_by_starting_scanline (const void *a, const void *b) {
+ return ((Line *)a)->y1 - ((Line *)b)->y1;
+}
+
+static int order_by_column (const void *a, const void *b) {
+ return ((Line *)a)->x - ((Line *)b)->x;
+}
+
+GRID_INLET(0) {
+ NOTEMPTY(color);
+ NOTEMPTY(polygon);
+ NOTEMPTY(lines);
+ SAME_TYPE(in,color);
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions");
+ if (in->dim->get(2)!=color->dim->get(0)) RAISE("image does not have same number of channels as stored color");
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ lines_start = lines_stop = 0;
+ in->set_chunk(1);
+ int nl = lines->dim->get(0);
+ qsort((int32 *)*lines,nl,sizeof(Line),order_by_starting_scanline);
+ int cn = color->dim->prod();
+ color2=new Grid(new Dim(cn*16), color->nt);
+ for (int i=0; i<16; i++) COPY((T *)*color2+cn*i,(T *)*color,cn);
+} GRID_FLOW {
+ int nl = lines->dim->get(0);
+ Line *ld = (Line *)(int32 *)*lines;
+ int f = in->dim->prod(1);
+ int y = dex/f;
+ int cn = color->dim->prod();
+ T *cd = (T *)*color2;
+ while (n) {
+ while (lines_stop != nl && ld[lines_stop].y1<=y) {
+ Line &l = ld[lines_stop];
+ l.x = l.x1 + (((y-l.y1)*l.m)>>16);
+ lines_stop++;
+ }
+ if (draw!=DRAW_POINT) {
+ int fudge = draw==DRAW_FILL?0:1;
+ for (int i=lines_start; i<lines_stop; i++) {
+ if (ld[i].y2<=y-fudge) {memswap(ld+i,ld+lines_start,1); lines_start++;}
+ }
+ }
+ if (lines_start == lines_stop) {
+ out->send(f,data);
+ } else {
+ int32 xl = in->dim->get(1);
+ T data2[f];
+ COPY(data2,data,f);
+ for (int i=lines_start; i<lines_stop; i++) {
+ Line &l = ld[i];
+ l.ox = l.x;
+ l.x = l.x1 + (((y-l.y1)*l.m)>>16);
+ }
+ if (draw!=DRAW_POINT) qsort(ld+lines_start,lines_stop-lines_start,sizeof(Line),order_by_column);
+ if (draw==DRAW_FILL) {
+ for (int i=lines_start; i<lines_stop-1; i+=2) {
+ int xs = max(ld[i].x,(int32)0);
+ int xe = min(ld[i+1].x,xl);
+ if (xs>=xe) continue; /* !@#$ WHAT? */
+ while (xe-xs>=16) {op->zip(16*cn,data2+cn*xs,cd); xs+=16;}
+ op->zip((xe-xs)*cn,data2+cn*xs,cd);
+ }
+ } else if (draw==DRAW_LINE) {
+ for (int i=lines_start; i<lines_stop; i++) {
+ if (ld[i].y1==ld[i].y2) ld[i].ox=ld[i].x2;
+ int xs = min(ld[i].x,ld[i].ox);
+ int xe = max(ld[i].x,ld[i].ox);
+ if (xs==xe) xe++;
+ if ((xs<0 && xe<0) || (xs>=xl && xe>=xl)) continue;
+ xs = max(0,xs);
+ xe = min(xl,xe);
+ while (xe-xs>=16) {op->zip(16*cn,data2+cn*xs,cd); xs+=16;}
+ op->zip((xe-xs)*cn,data2+cn*xs,cd);
+ }
+ } else {
+ for (int i=lines_start; i<lines_stop; i++) {
+ if (y!=ld[i].y1) continue;
+ int xs=ld[i].x1;
+ int xe=xs+1;
+ if (xs<0 || xs>=xl) continue;
+ op->zip((xe-xs)*cn,data2+cn*xs,cd);
+ }
+ lines_start=lines_stop;
+ }
+ out->send(f,data2);
+ }
+ n-=f;
+ data+=f;
+ y++;
+ }
+} GRID_END
+
+
+GRID_INPUT(1,color) {} GRID_END
+GRID_INPUT(2,polygon) {init_lines();} GRID_END
+
+\end class {install("#draw_polygon",3,1); add_creator("@draw_polygon");}
+
+//****************************************************************
+static void expect_position(P<Dim> d) {
+ if (d->n!=1) RAISE("position should have 1 dimension, not %d", d->n);
+ if (d->v[0]!=2) RAISE("position dim 0 should have 2 elements, not %d", d->v[0]);
+}
+
+\class DrawImage : FObject {
+ \attr Numop *op;
+ \attr PtrGrid image;
+ \attr PtrGrid position;
+ \attr bool alpha;
+ \attr bool tile;
+ \constructor (Numop *op=op_put, Grid *image=0, Grid *position=0) {
+ alpha=false; tile=false;
+ this->op = op;
+ this->position.constrain(expect_position);
+ this->image.constrain(expect_picture);
+ if (image) this->image=image;
+ if (position) this->position=position;
+ else this->position=new Grid(new Dim(2),int32_e,true);
+ }
+ \grin 0
+ \grin 1
+ \grin 2 int32
+ // draw row # ry of right image in row buffer buf, starting at xs
+ // overflow on both sides has to be handled automatically by this method
+ template <class T> void draw_segment(T *obuf, T *ibuf, int ry, int x0);
+};
+
+#define COMPUTE_ALPHA(c,a) obuf[j+(c)] = ibuf[j+(c)] + (rbuf[a])*(obuf[j+(c)]-ibuf[j+(c)])/256;
+#define COMPUTE_ALPHA4(b) \
+ COMPUTE_ALPHA(b+0,b+3); \
+ COMPUTE_ALPHA(b+1,b+3); \
+ COMPUTE_ALPHA(b+2,b+3); \
+ obuf[b+3] = rbuf[b+3] + (255-rbuf[b+3])*(ibuf[j+b+3])/256;
+
+template <class T> void DrawImage::draw_segment(T *obuf, T *ibuf, int ry, int x0) {
+ if (ry<0 || ry>=image->dim->get(0)) return; // outside of image
+ int sx = in[0]->dim->get(1), rsx = image->dim->get(1);
+ int sc = in[0]->dim->get(2), rsc = image->dim->get(2);
+ T *rbuf = (T *)*image + ry*rsx*rsc;
+ if (x0>sx || x0<=-rsx) return; // outside of buffer
+ int n=rsx;
+ if (x0+n>sx) n=sx-x0;
+ if (x0<0) { rbuf-=rsc*x0; n+=x0; x0=0; }
+ if (alpha && rsc==4 && sc==3) { // RGB by RGBA //!@#$ optimise
+ int j=sc*x0;
+ for (; n; n--, rbuf+=4, j+=3) {
+ op->zip(sc,obuf+j,rbuf); COMPUTE_ALPHA(0,3); COMPUTE_ALPHA(1,3); COMPUTE_ALPHA(2,3);
+ }
+ } else if (alpha && rsc==4 && sc==4) { // RGBA by RGBA
+ op->zip(n*rsc,obuf+x0*rsc,rbuf);
+ int j=sc*x0;
+ for (; n>=4; n-=4, rbuf+=16, j+=16) {
+ COMPUTE_ALPHA4(0);COMPUTE_ALPHA4(4);
+ COMPUTE_ALPHA4(8);COMPUTE_ALPHA4(12);
+ }
+ for (; n; n--, rbuf+=4, j+=4) {
+ COMPUTE_ALPHA4(0);
+ }
+ } else { // RGB by RGB, etc
+ op->zip(n*rsc,obuf+sc*x0,rbuf);
+ }
+}
+
+GRID_INLET(0) {
+ NOTEMPTY(image);
+ NOTEMPTY(position);
+ SAME_TYPE(in,image);
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions");
+ if (image->dim->n!=3) RAISE("expecting 3 dimensions in right_hand");
+ int lchan = in->dim->get(2);
+ int rchan = image->dim->get(2);
+ if (alpha && rchan!=4) {
+ RAISE("alpha mode works only with 4 channels in right_hand");
+ }
+ if (lchan != rchan-(alpha?1:0) && lchan != rchan) {
+ RAISE("right_hand has %d channels, alpha=%d, left_hand has %d, expecting %d or %d",
+ rchan, alpha?1:0, lchan, rchan-(alpha?1:0), rchan);
+ }
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_chunk(1);
+} GRID_FLOW {
+ int f = in->dim->prod(1);
+ int y = dex/f;
+ if (position->nt != int32_e) RAISE("position has to be int32");
+ int py = ((int32*)*position)[0], rsy = image->dim->v[0];
+ int px = ((int32*)*position)[1], rsx = image->dim->v[1], sx=in->dim->get(1);
+ for (; n; y++, n-=f, data+=f) {
+ int ty = div2(y-py,rsy);
+ if (tile || ty==0) {
+ T data2[f];
+ COPY(data2,data,f);
+ if (tile) {
+ for (int x=px-div2(px+rsx-1,rsx)*rsx; x<sx; x+=rsx) {
+ draw_segment(data2,data,mod(y-py,rsy),x);
+ }
+ } else {
+ draw_segment(data2,data,y-py,px);
+ }
+ out->send(f,data2);
+ } else {
+ out->send(f,data);
+ }
+ }
+} GRID_END
+
+GRID_INPUT(1,image) {} GRID_END
+GRID_INPUT(2,position) {} GRID_END
+
+\end class {install("#draw_image",3,1); add_creator("@draw_image");}
+
+//****************************************************************
+// Dim[*A],Dim[*B],Dim[C,size(A)-size(B)] -> Dim[*A]
+
+/* NOT FINISHED */
+\class GridDrawPoints : FObject {
+ \attr Numop *op;
+ \attr PtrGrid color;
+ \attr PtrGrid points;
+ \grin 0
+ \grin 1 int32
+ \grin 2 int32
+ \constructor (Numop *op=op_put, Grid *color=0, Grid *points=0) {
+ this->op = op;
+ if (color) this->color=color;
+ if (points) this->points=points;
+ }
+};
+
+GRID_INPUT(1,color) {} GRID_END
+GRID_INPUT(2,points) {} GRID_END
+
+GRID_INLET(0) {
+ NOTEMPTY(color);
+ NOTEMPTY(points);
+ SAME_TYPE(in,color);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ if (points->dim->n!=2) RAISE("points should be a 2-D grid");
+ if (points->dim->v[1] != in->dim->n - color->dim->n)
+ RAISE("wrong number of dimensions");
+ in->set_chunk(0);
+} GRID_FLOW {
+ long m = points->dim->v[1];
+ long cn = in->dim->prod(-color->dim->n); /* size of color (RGB=3, greyscale=1, ...) */
+ int32 *pdata = (int32 *)points->data;
+ T *cdata = (T *)color->data;
+ for (long i=0; i<n; i++) {
+ long off = 0;
+ for (long k=0; k>m; k++) off = off*in->dim->v[k] + pdata[i*points->dim->v[1]+k];
+ off *= cn;
+ for (long j=0; j<cn; j++) data[off+j] = cdata[j];
+ }
+// out->send(data);
+} GRID_END
+\end class {install("#draw_points",3,1);}
+
+//****************************************************************
+\class GridNoiseGateYuvs : FObject {
+ \grin 0
+ int thresh;
+ \decl 1 float(int v);
+ \constructor (int v=0) {thresh=v;}
+};
+
+GRID_INLET(0) {
+ if (in->dim->n!=3) RAISE("requires 3 dimensions: dim(y,x,3)");
+ if (in->dim->v[2]!=3) RAISE("requires 3 channels");
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_chunk(2);
+} GRID_FLOW {
+ T tada[n];
+ for (long i=0; i<n; i+=3) {
+ if (data[i+0]<=thresh) {
+ tada[i+0]=0; tada[i+1]=0; tada[i+2]=0;
+ } else {
+ tada[i+0]=data[i+0]; tada[i+1]=data[i+1]; tada[i+2]=data[i+2];
+ }
+ }
+ out->send(n,tada);
+} GRID_END
+
+\def 1 float(int v) {thresh=v;}
+\end class {install("#noise_gate_yuvs",2,1);}
+
+//****************************************************************
+
+\class GridPack : FObject {
+ int n;
+ PtrGrid a;
+ \constructor (int n=2, NumberTypeE nt=int32_e) {
+ if (n<1) RAISE("n=%d must be at least 1",n);
+ if (n>32) RAISE("n=%d is too many?",n);
+ a = new Grid(new Dim(n),nt,true);
+ this->n=n;
+ bself->ninlets_set(this->n);
+ }
+ \decl void _n_float (int inlet, float f);
+ \decl void _n_list (int inlet, float f);
+ \decl 0 bang ();
+ //\grin 0
+};
+\def void _n_float (int inlet, float f) {
+#define FOO(T) ((T *)*a)[inlet] = (T)f;
+TYPESWITCH(a->nt,FOO,);
+#undef FOO
+ _0_bang(argc,argv);
+}
+\def void _n_list (int inlet, float f) {_n_float(argc,argv,inlet,f);}
+\def 0 bang () {
+ out=new GridOutlet(this,0,a->dim,a->nt);
+#define FOO(T) out->send(n,(T *)*a);
+TYPESWITCH(a->nt,FOO,);
+#undef FOO
+}
+\end class {install("#pack",1,1); add_creator("@pack");}
+
+\class GridUnpack : FObject {
+ int n;
+ \constructor (int n=2) {
+ if (n<1) RAISE("n=%d must be at least 1",n);
+ if (n>32) RAISE("n=%d is too many?",n);
+ this->n=n;
+ bself->noutlets_set(this->n);
+ }
+ \grin 0
+};
+GRID_INLET(0) {
+ if (in->dim->n!=1) RAISE("expect one dimension");
+ if (in->dim->v[0]!=this->n) RAISE("expecting dim(%ld), got dim(%ld)",this->n,in->dim->v[0]);
+ in->set_chunk(0);
+} GRID_FLOW {
+ for (int i=n-1; i>=0; i--) outlet_float(bself->outlets[i],(t_float)data[i]);
+} GRID_END
+\end class {install("#unpack",1,0);}
+
+//****************************************************************
+
+\class GridRotatificator : FObject {
+ int angle;
+ int from, to, n;
+ \decl 0 float (int scale);
+ \decl 0 axis (int from, int to, int n);
+ \constructor (int from=0, int to=1, int n=2) {
+ angle=0;
+ _0_axis(0,0,from,to,n);
+ }
+ \decl 1 float(int angle);
+};
+\def 0 float (int scale) {
+ int32 rotator[n*n];
+ for (int i=0; i<n; i++) for (int j=0; j<n; j++) rotator[i*n+j] = scale * (i==j);
+ float th = angle * M_PI / 18000;
+ for (int i=0; i<2; i++) for (int j=0; j<2; j++)
+ rotator[(i?to:from)*n+(j?to:from)] = (int32)round(scale*cos(th+(j-i)*M_PI/2));
+ GridOutlet out(this,0,new Dim(n,n),int32_e);
+ out.send(n*n,rotator);
+}
+\def 0 axis(int from, int to, int n) {
+ if (n<0) RAISE("n-axis number incorrect");
+ if (from<0 || from>=n) RAISE("from-axis number incorrect");
+ if (to <0 || to >=n) RAISE( "to-axis number incorrect");
+ this->from = from;
+ this-> to = to;
+ this-> n = n;
+}
+\def 1 float(int angle) {this->angle = angle;}
+\end class {install("#rotatificator",2,1);}
+
+static void expect_min_one_dim (P<Dim> d) {
+ if (d->n<1) RAISE("expecting at least one dimension, got %s",d->to_s());}
+
+#define OP(x) op_dict[string(#x)]
+\class GridClusterAvg : FObject {
+ \attr int numClusters;
+ \attr PtrGrid r;
+ \attr PtrGrid sums;
+ \attr PtrGrid counts;
+ \constructor (int v) {_1_float(0,0,v); r.constrain(expect_min_one_dim);}
+ \decl 1 float (int v);
+ \grin 0 int32
+ \grin 2
+ template <class T> void make_stats (long n, int32 *ldata, T *rdata) {
+ int32 chans = r->dim->v[r->dim->n-1];
+ T *sdata = (T *)*sums;
+ int32 *cdata = (int32 *)*counts;
+ for (int i=0; i<n; i++, ldata++, rdata+=chans) {
+ if (*ldata<0 || *ldata>=numClusters) RAISE("value out of range in left grid");
+ OP(+)->zip(chans,sdata+(*ldata)*chans,rdata);
+ cdata[*ldata]++;
+ }
+ for (int i=0; i<numClusters; i++) OP(/)->map(chans,sdata+i*chans,(T)cdata[i]);
+ out = new GridOutlet(this,1,counts->dim,counts->nt);
+ out->send(counts->dim->prod(),(int32 *)*counts);
+ out = new GridOutlet(this,0,sums->dim,sums->nt);
+ out->send(sums->dim->prod(),(T *)*sums);
+ }
+};
+
+GRID_INLET(0) {
+ NOTEMPTY(r);
+ int32 v[r->dim->n];
+ COPY(v,r->dim->v,r->dim->n-1);
+ v[r->dim->n-1]=1;
+ P<Dim> t = new Dim(r->dim->n,v);
+ if (!t->equal(in->dim)) RAISE("left %s must be equal to right %s except last dimension should be 1",in->dim->to_s(),r->dim->to_s());
+ in->set_chunk(0);
+ int32 w[2] = {numClusters,r->dim->v[r->dim->n-1]};
+ sums = new Grid(new Dim(2,w),r->nt, true);
+ counts = new Grid(new Dim(1,w),int32_e,true);
+} GRID_FLOW {
+ #define FOO(U) make_stats(n,data,(U *)*r);
+ TYPESWITCH(r->nt,FOO,)
+ #undef FOO
+} GRID_END
+\def 1 float (int v) {numClusters = v;}
+GRID_INPUT(2,r) {
+} GRID_END
+
+\end class {install("#cluster_avg",3,2);}
+
+//****************************************************************
+
+void startup_flow_objects () {
+ op_add = OP(+);
+ op_sub = OP(-);
+ op_mul = OP(*);
+ op_shl = OP(<<);
+ op_mod = OP(%);
+ op_and = OP(&);
+ op_div = OP(/);
+ op_put = OP(put);
+ \startall
+}
diff --git a/externals/gridflow/src/classes2.cxx b/externals/gridflow/src/classes2.cxx
new file mode 100644
index 00000000..6ca9a62a
--- /dev/null
+++ b/externals/gridflow/src/classes2.cxx
@@ -0,0 +1,1183 @@
+/*
+ $Id: flow_objects.c 4097 2008-10-03 19:49:03Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#ifdef DESIRE
+#include "desire.h"
+#else
+extern "C" {
+#include "bundled/g_canvas.h"
+#include "bundled/m_imp.h"
+extern t_class *text_class;
+};
+#endif
+#include <algorithm>
+#include <errno.h>
+#include <sys/time.h>
+#include <string>
+
+typedef int (*comparator_t)(const void *, const void *);
+
+#ifndef DESIREDATA
+struct _outconnect {
+ struct _outconnect *next;
+ t_pd *to;
+};
+struct _outlet {
+ t_object *owner;
+ struct _outlet *next;
+ t_outconnect *connections;
+ t_symbol *sym;
+};
+#endif
+
+//****************************************************************
+
+struct ArgSpec {
+ t_symbol *name;
+ t_symbol *type;
+ t_atom defaultv;
+};
+
+\class Args : FObject {
+ ArgSpec *sargv;
+ int sargc;
+ \constructor (...) {
+ sargc = argc;
+ sargv = new ArgSpec[argc];
+ for (int i=0; i<argc; i++) {
+ if (argv[i].a_type==A_LIST) {
+ t_binbuf *b = (t_binbuf *)argv[i].a_gpointer;
+ int bac = binbuf_getnatom(b);
+ t_atom *bat = binbuf_getvec(b);
+ sargv[i].name = atom_getsymbolarg(0,bac,bat);
+ sargv[i].type = atom_getsymbolarg(1,bac,bat);
+ if (bac<3) SETNULL(&sargv[i].defaultv); else sargv[i].defaultv = bat[2];
+ } else if (argv[i].a_type==A_SYMBOL) {
+ sargv[i].name = argv[i].a_symbol;
+ sargv[i].type = gensym("a");
+ SETNULL(&sargv[i].defaultv);
+ } else RAISE("expected symbol or nested list");
+ }
+ bself->noutlets_set(sargc);
+ }
+ ~Args () {delete[] sargv;}
+ \decl 0 bang ();
+ \decl 0 loadbang ();
+ void process_args (int argc, t_atom *argv);
+};
+/* get the owner of the result of canvas_getenv */
+static t_canvas *canvas_getabstop(t_canvas *x) {
+ while (!x->gl_env) if (!(x = x->gl_owner)) bug("t_canvasenvironment", x);
+ return x;
+}
+\def 0 bang () {post("%s shouldn't bang [args] anymore.",canvas_getabstop(bself->mom)->gl_name->s_name);}
+void outlet_anything2 (t_outlet *o, int argc, t_atom *argv) {
+ if (!argc) outlet_bang(o);
+ else if (argv[0].a_type==A_SYMBOL) outlet_anything(o,argv[0].a_symbol,argc-1,argv+1);
+ else if (argv[0].a_type==A_FLOAT && argc==1) outlet_float(o,argv[0].a_float);
+ else outlet_anything(o,&s_list,argc,argv);
+}
+void pd_anything2 (t_pd *o, int argc, t_atom *argv) {
+ if (!argc) pd_bang(o);
+ else if (argv[0].a_type==A_SYMBOL) pd_typedmess(o,argv[0].a_symbol,argc-1,argv+1);
+ else if (argv[0].a_type==A_FLOAT && argc==1) pd_float(o,argv[0].a_float);
+ else pd_typedmess(o,&s_list,argc,argv);
+}
+\def 0 loadbang () {
+ t_canvasenvironment *env = canvas_getenv(bself->mom);
+ int ac = env->ce_argc;
+ t_atom av[ac];
+ for (int i=0; i<ac; i++) av[i] = env->ce_argv[i];
+ //ac = handle_braces(ac,av);
+ t_symbol *comma = gensym(",");
+ int j;
+ for (j=0; j<ac; j++) if (av[j].a_type==A_SYMBOL && av[j].a_symbol==comma) break;
+ int jj = handle_braces(j,av);
+ process_args(jj,av);
+ while (j<ac) {
+ j++;
+ int k=j;
+ for (; j<ac; j++) if (av[j].a_type==A_SYMBOL && av[j].a_symbol==comma) break;
+ //outlet_anything2(bself->outlets[sargc],j-k,av+k);
+ t_text *t = (t_text *)canvas_getabstop(bself->mom);
+ if (!t->te_inlet) RAISE("can't send init-messages, because object has no [inlet]");
+ pd_anything2((t_pd *)t->te_inlet,j-k,av+k);
+ }
+}
+void Args::process_args (int argc, t_atom *argv) {
+ t_canvas *canvas = canvas_getrootfor(bself->mom);
+ t_symbol *wildcard = gensym("*");
+ for (int i=sargc-1; i>=0; i--) {
+ t_atom *v;
+ if (i>=argc) {
+ if (sargv[i].defaultv.a_type != A_NULL) {
+ v = &sargv[i].defaultv;
+ } else if (sargv[i].name!=wildcard) {
+ pd_error(canvas,"missing argument $%d named \"%s\"", i+1,sargv[i].name->s_name);
+ continue;
+ }
+ } else v = &argv[i];
+ if (sargv[i].name==wildcard) {
+ if (argc-i>0) outlet_list(bself->outlets[i],&s_list,argc-i,argv+i);
+ else outlet_bang(bself->outlets[i]);
+ } else {
+ if (v->a_type==A_LIST) {
+ t_binbuf *b = (t_binbuf *)v->a_gpointer;
+ outlet_list(bself->outlets[i],&s_list,binbuf_getnatom(b),binbuf_getvec(b));
+ } else if (v->a_type==A_SYMBOL) outlet_symbol(bself->outlets[i],v->a_symbol);
+ else outlet_anything2(bself->outlets[i],1,v);
+ }
+ }
+ if (argc>sargc && sargv[sargc-1].name!=wildcard) pd_error(canvas,"warning: too many args (got %d, want %d)", argc, sargc);
+}
+\end class {install("args",1,1);}
+
+//****************************************************************
+
+namespace {
+template <class T> void swap (T &a, T &b) {T c; c=a; a=b; b=c;}
+};
+
+\class ListReverse : FObject {
+ \constructor () {}
+ \decl 0 list(...);
+};
+\def 0 list (...) {
+ for (int i=(argc-1)/2; i>=0; i--) swap(argv[i],argv[argc-i-1]);
+ outlet_list(bself->te_outlet,&s_list,argc,argv);
+}
+\end class {install("listreverse",1,1);}
+
+\class ListFlatten : FObject {
+ std::vector<t_atom2> contents;
+ \constructor () {}
+ \decl 0 list(...);
+ void traverse (int argc, t_atom2 *argv) {
+ for (int i=0; i<argc; i++) {
+ if (argv[i].a_type==A_LIST) traverse(binbuf_getnatom(argv[i]),(t_atom2 *)binbuf_getvec(argv[i]));
+ else contents.push_back(argv[i]);
+ }
+ }
+};
+\def 0 list (...) {
+ traverse(argc,argv);
+ outlet_list(bself->te_outlet,&s_list,contents.size(),&contents[0]);
+ contents.clear();
+
+}
+\end class {install("listflatten",1,1);}
+
+// does not do recursive comparison of lists.
+static bool atom_eq (t_atom &a, t_atom &b) {
+ if (a.a_type!=b.a_type) return false;
+ if (a.a_type==A_FLOAT) return a.a_float ==b.a_float;
+ if (a.a_type==A_SYMBOL) return a.a_symbol ==b.a_symbol;
+ if (a.a_type==A_POINTER) return a.a_gpointer==b.a_gpointer;
+ if (a.a_type==A_LIST) return a.a_gpointer==b.a_gpointer;
+ RAISE("don't know how to compare elements of type %d",a.a_type);
+}
+
+\class ListFind : FObject {
+ int ac;
+ t_atom *at;
+ ~ListFind() {if (at) delete[] at;}
+ \constructor (...) {ac=0; at=0; _1_list(argc,argv);}
+ \decl 0 list(...);
+ \decl 1 list(...);
+ \decl 0 float(float f);
+ \decl 0 symbol(t_symbol *s);
+};
+\def 1 list (...) {
+ if (at) delete[] at;
+ ac = argc;
+ at = new t_atom[argc];
+ for (int i=0; i<argc; i++) at[i] = argv[i];
+}
+\def 0 list (...) {
+ if (argc<1) RAISE("empty input");
+ int i=0; for (; i<ac; i++) if (atom_eq(at[i],argv[0])) break;
+ outlet_float(bself->outlets[0],i==ac?-1:i);
+}
+\def 0 float (float f) {
+ int i=0; for (; i<ac; i++) if (atom_eq(at[i],argv[0])) break;
+ outlet_float(bself->outlets[0],i==ac?-1:i);
+}
+\def 0 symbol (t_symbol *s) {
+ int i=0; for (; i<ac; i++) if (atom_eq(at[i],argv[0])) break;
+ outlet_float(bself->outlets[0],i==ac?-1:i);
+}
+//doc:_1_list,"list to search into"
+//doc:_0_float,"float to find in that list"
+//doc_out:_0_float,"position of the incoming float in the stored list"
+\end class {install("listfind",2,1);}
+
+void outlet_atom (t_outlet *self, t_atom *av) {
+ if (av->a_type==A_FLOAT) outlet_float( self,av->a_float); else
+ if (av->a_type==A_SYMBOL) outlet_symbol( self,av->a_symbol); else
+ if (av->a_type==A_POINTER) outlet_pointer(self,av->a_gpointer); else
+ outlet_list(self,gensym("list"),1,av);
+}
+
+\class ListRead : FObject { /* sounds like tabread */
+ int ac;
+ t_atom *at;
+ ~ListRead() {if (at) delete[] at;}
+ \constructor (...) {ac=0; at=0; _1_list(argc,argv);}
+ \decl 0 float(float f);
+ \decl 1 list(...);
+};
+\def 0 float(float f) {
+ int i = int(f);
+ if (i<0) i+=ac;
+ if (i<0 || i>=ac) {outlet_bang(bself->outlets[0]); return;} /* out-of-range */
+ outlet_atom(bself->outlets[0],&at[i]);
+}
+\def 1 list (...) {
+ if (at) delete[] at;
+ ac = argc;
+ at = new t_atom[argc];
+ for (int i=0; i<argc; i++) at[i] = argv[i];
+}
+\end class {install("listread",2,1);}
+
+\class Range : FObject {
+ t_float *mosusses;
+ int nmosusses;
+ \constructor (...) {
+ nmosusses = argc;
+ for (int i=0; i<argc; i++) if (argv[i].a_type!=A_FLOAT) RAISE("$%d: expected float",i+1);
+ mosusses = new t_float[argc];
+ for (int i=0; i<argc; i++) mosusses[i]=argv[i].a_float;
+ bself-> ninlets_set(1+nmosusses);
+ bself->noutlets_set(1+nmosusses);
+ }
+ ~Range () {delete[] mosusses;}
+ \decl 0 float(float f);
+ \decl 0 list(float f);
+ \decl void _n_float(int i, float f);
+};
+\def 0 list(float f) {_0_float(argc,argv,f);}
+\def 0 float(float f) {
+ int i; for (i=0; i<nmosusses; i++) if (f<mosusses[i]) break;
+ outlet_float(bself->outlets[i],f);
+}
+ // precedence problem in dispatcher... does this problem still exist?
+\def void _n_float(int i, float f) {if (!i) _0_float(argc,argv,f); else mosusses[i-1] = f;}
+\end class {install("range",1,1);}
+
+//****************************************************************
+
+string ssprintf(const char *fmt, ...) {
+ std::ostringstream os;
+ va_list va;
+ va_start(va,fmt);
+ voprintf(os,fmt,va);
+ va_end(va);
+ return os.str();
+}
+
+\class GFPrint : FObject {
+ t_symbol *prefix;
+ t_pd *gp;
+ //t_symbol *rsym;
+ \constructor (t_symbol *s=0) {
+ //rsym = gensym(const_cast<char *>(ssprintf("gf.print:%08x",this).data())); // not in use atm.
+ prefix=s?s:gensym("print");
+ t_atom a[1];
+ SETSYMBOL(a,prefix);
+ pd_typedmess(&pd_objectmaker,gensym("#print"),1,a);
+ gp = pd_newest();
+ SETPOINTER(a,(t_gpointer *)bself);
+ //pd_typedmess(gp,gensym("dest"),1,a);
+ }
+ ~GFPrint () {
+ //pd_unbind((t_pd *)bself,rsym);
+ pd_free(gp);
+ }
+ \decl 0 grid(...);
+ \decl void anything (...);
+};
+std::ostream &operator << (std::ostream &self, const t_atom &a) {
+ switch (a.a_type) {
+ case A_FLOAT: self << a.a_float; break;
+ case A_SYMBOL: self << a.a_symbol->s_name; break; // i would rather show backslashes here...
+ case A_DOLLSYM: self << a.a_symbol->s_name; break; // for real, it's the same thing as A_SYMBOL in pd >= 0.40
+ case A_POINTER: self << "\\p(0x" << std::hex << a.a_gpointer << std::dec << ")"; break;
+ case A_COMMA: self << ","; break;
+ case A_SEMI: self << ";"; break;
+ case A_DOLLAR: self << "$" << a.a_w.w_index; break;
+ case A_LIST: {
+ t_list *b = (t_list *)a.a_gpointer;
+ int argc = binbuf_getnatom(b);
+ t_atom *argv = binbuf_getvec(b);
+ self << "(";
+ for (int i=0; i<argc; i++) self << argv[i] << " )"[i==argc-1];
+ break;
+ }
+ default: self << "\\a(" << a.a_type << " " << std::hex << a.a_gpointer << std::dec << ")"; break;
+ }
+ return self;
+}
+\def 0 grid(...) {pd_typedmess(gp,gensym("grid"),argc,argv);}
+\def void anything(...) {
+ std::ostringstream text;
+ text << prefix->s_name << ":";
+ if (argv[0]==gensym("_0_list") && argc>=2 && argv[1].a_type==A_FLOAT) {
+ // don't show the selector.
+ } else if (argv[0]==gensym("_0_list") && argc==2 && argv[1].a_type==A_SYMBOL) {
+ text << " symbol";
+ } else if (argv[0]==gensym("_0_list") && argc==2 && argv[1].a_type==A_POINTER) {
+ text << " pointer";
+ } else if (argv[0]==gensym("_0_list") && argc==1) {
+ text << " bang";
+ } else {
+ text << " " << argv[0].a_symbol->s_name+3; // as is
+ }
+ for (int i=1; i<argc; i++) {text << " " << argv[i];}
+ post("%s",text.str().data());
+}
+\end class {install("gf.print",1,0); add_creator3(fclass,"print");}
+
+#ifdef HAVE_DESIREDATA
+t_glist *glist_getcanvas(t_glist *foo) {return foo;}//dummy
+void canvas_fixlinesfor(t_glist *foo,t_text *) {}//dummy
+#endif
+
+//#ifdef HAVE_DESIREDATA
+static void display_update(void *x);
+\class Display : FObject {
+ bool selected;
+ t_glist *canvas;
+ t_symbol *rsym;
+ int y,x,sy,sx;
+ bool vis;
+ std::ostringstream text;
+ t_clock *clock;
+ t_pd *gp;
+ \constructor () {
+ selected=false; canvas=0; y=0; x=0; sy=16; sx=80; vis=false; clock=0;
+ std::ostringstream os;
+ rsym = gensym(const_cast<char *>(ssprintf("display:%08x",this).data()));
+ pd_typedmess(&pd_objectmaker,gensym("#print"),0,0);
+ gp = pd_newest();
+ t_atom a[1];
+ SETFLOAT(a,20);
+ pd_typedmess(gp,gensym("maxrows"),1,a);
+ text << "...";
+ pd_bind((t_pd *)bself,rsym);
+ SETPOINTER(a,(t_gpointer *)bself);
+ pd_typedmess(gp,gensym("dest"),1,a);
+ clock = clock_new((void *)this,(void(*)())display_update);
+ }
+ ~Display () {
+ pd_unbind((t_pd *)bself,rsym);
+ pd_free(gp);
+ if (clock) clock_free(clock);
+ }
+ \decl void anything (...);
+ \decl 0 set_size(int sy, int sx);
+ \decl 0 grid(...);
+ \decl 0 very_long_name_that_nobody_uses(...);
+ void show() {
+ std::ostringstream quoted;
+ // def quote(text) "\"" + text.gsub(/["\[\]\n\$]/m) {|x| if x=="\n" then "\\n" else "\\"+x end } + "\"" end
+ std::string ss = text.str();
+ const char *s = ss.data();
+ int n = ss.length();
+ for (int i=0;i<n;i++) {
+ if (s[i]=='\n') quoted << "\\n";
+ else if (strchr("\"[]$",s[i])) quoted << "\\" << (char)s[i];
+ else quoted << (char)s[i];
+ }
+ //return if not canvas or not @vis # can't show for now...
+ /* we're not using quoting for now because there's a bug in it. */
+ /* btw, this quoting is using "", but we're gonna use {} instead for now, because of newlines */
+ sys_vgui("display_update %s %d %d #000000 #cccccc %s {Courier -12} .x%x.c {%s}\n",
+ rsym->s_name,bself->te_xpix,bself->te_ypix,selected?"#0000ff":"#000000",canvas,ss.data());
+ }
+};
+static void display_getrectfn(t_gobj *x, t_glist *glist, int *x1, int *y1, int *x2, int *y2) {
+ BFObject *bself = (BFObject*)x; Display *self = (Display *)bself->self; self->canvas = glist;
+ *x1 = bself->te_xpix-1;
+ *y1 = bself->te_ypix-1;
+ *x2 = bself->te_xpix+1+self->sx;
+ *y2 = bself->te_ypix+1+self->sy;
+}
+static void display_displacefn(t_gobj *x, t_glist *glist, int dx, int dy) {
+ BFObject *bself = (BFObject*)x; Display *self = (Display *)bself->self; self->canvas = glist;
+ bself->te_xpix+=dx;
+ bself->te_ypix+=dy;
+ self->canvas = glist_getcanvas(glist);
+ self->show();
+ canvas_fixlinesfor(glist, (t_text *)x);
+}
+static void display_selectfn(t_gobj *x, t_glist *glist, int state) {
+ BFObject *bself = (BFObject*)x; Display *self = (Display *)bself->self; self->canvas = glist;
+ self->selected=!!state;
+ sys_vgui(".x%x.c itemconfigure %s -outline %s\n",glist_getcanvas(glist),self->rsym->s_name,self->selected?"#0000ff":"#000000");
+}
+static void display_deletefn(t_gobj *x, t_glist *glist) {
+ BFObject *bself = (BFObject*)x; Display *self = (Display *)bself->self; self->canvas = glist;
+ if (self->vis) sys_vgui(".x%x.c delete %s %sTEXT\n",glist_getcanvas(glist),self->rsym->s_name,self->rsym->s_name);
+ canvas_deletelinesfor(glist, (t_text *)x);
+}
+static void display_visfn(t_gobj *x, t_glist *glist, int flag) {
+ BFObject *bself = (BFObject*)x; Display *self = (Display *)bself->self; self->canvas = glist;
+ self->vis = !!flag;
+ display_update(self);
+}
+static void display_update(void *x) {
+ Display *self = (Display *)x;
+ if (self->vis) self->show();
+}
+\def 0 set_size(int sy, int sx) {this->sy=sy; this->sx=sx;}
+\def void anything (...) {
+ string sel = string(argv[0]).data()+3;
+ text.str("");
+ if (sel != "float") {text << sel; if (argc>1) text << " ";}
+ long col = text.str().length();
+ char buf[MAXPDSTRING];
+ for (int i=1; i<argc; i++) {
+ atom_string(&argv[i],buf,MAXPDSTRING);
+ text << buf;
+ col += strlen(buf);
+ if (i!=argc-1) {
+ text << " ";
+ col++;
+ if (col>56) {text << "\\\\\n"; col=0;}
+ }
+ }
+ clock_delay(clock,0);
+}
+\def 0 grid(...) {
+ text.str("");
+ pd_typedmess(gp,gensym("grid"),argc,argv);
+ clock_delay(clock,0);
+}
+\def 0 very_long_name_that_nobody_uses(...) {
+ if (text.str().length()) text << "\n";
+ for (int i=0; i<argc; i++) text << (char)INT(argv[i]);
+}
+\end class {
+#ifndef DESIRE
+ install("display",1,0);
+ t_class *qlass = fclass->bfclass;
+ t_widgetbehavior *wb = new t_widgetbehavior;
+ wb->w_getrectfn = display_getrectfn;
+ wb->w_displacefn = display_displacefn;
+ wb->w_selectfn = display_selectfn;
+ wb->w_activatefn = 0;
+ wb->w_deletefn = display_deletefn;
+ wb->w_visfn = display_visfn;
+ wb->w_clickfn = 0;
+ class_setwidget(qlass,wb);
+ sys_gui("proc display_update {self x y fg bg outline font canvas text} { \n\
+ $canvas delete ${self}TEXT \n\
+ $canvas create text [expr $x+2] [expr $y+2] -fill $fg -font $font -text $text -anchor nw -tag ${self}TEXT \n\
+ foreach {x1 y1 x2 y2} [$canvas bbox ${self}TEXT] {} \n\
+ incr x -1 \n\
+ incr y -1 \n\
+ set sx [expr $x2-$x1+2] \n\
+ set sy [expr $y2-$y1+4] \n\
+ $canvas delete ${self} \n\
+ $canvas create rectangle $x $y [expr $x+$sx] [expr $y+$sy] -fill $bg -tags $self -outline $outline \n\
+ $canvas create rectangle $x $y [expr $x+7] $y -fill red -tags $self -outline $outline \n\
+ $canvas lower $self ${self}TEXT \n\
+ pd \"$self set_size $sy $sx;\" \n\
+ }\n");
+#endif
+}
+//#endif // ndef HAVE_DESIREDATA
+
+//****************************************************************
+
+\class UnixTime : FObject {
+ \constructor () {}
+ \decl 0 bang ();
+};
+\def 0 bang () {
+ timeval tv;
+ gettimeofday(&tv,0);
+ time_t t = time(0);
+ struct tm *tmp = localtime(&t);
+ if (!tmp) RAISE("localtime: %s",strerror(errno));
+ char tt[MAXPDSTRING];
+ strftime(tt,MAXPDSTRING,"%a %b %d %H:%M:%S %Z %Y",tmp);
+ t_atom a[6];
+ SETFLOAT(a+0,tmp->tm_year+1900);
+ SETFLOAT(a+1,tmp->tm_mon-1);
+ SETFLOAT(a+2,tmp->tm_mday);
+ SETFLOAT(a+3,tmp->tm_hour);
+ SETFLOAT(a+4,tmp->tm_min);
+ SETFLOAT(a+5,tmp->tm_sec);
+ t_atom b[3];
+ SETFLOAT(b+0,tv.tv_sec/86400);
+ SETFLOAT(b+1,mod(tv.tv_sec,86400));
+ SETFLOAT(b+2,tv.tv_usec);
+ outlet_anything(bself->outlets[2],&s_list,6,a);
+ outlet_anything(bself->outlets[1],&s_list,3,b);
+ send_out(0,strlen(tt),tt);
+}
+
+\end class UnixTime {install("unix_time",1,3);}
+
+
+//****************************************************************
+
+/* if using a DB-25 female connector as found on a PC, then the pin numbering is like:
+ 13 _____ 1
+ 25 \___/ 14
+ 1 = STROBE = the clock line is a square wave, often at 9600 Hz,
+ which determines the data rate in usual circumstances.
+ 2..9 = D0..D7 = the eight ordinary data bits
+ 10 = -ACK (status bit 6 ?)
+ 11 = BUSY (status bit 7)
+ 12 = PAPER_END (status bit 5)
+ 13 = SELECT (status bit 4 ?)
+ 14 = -AUTOFD
+ 15 = -ERROR (status bit 3 ?)
+ 16 = -INIT
+ 17 = -SELECT_IN
+ 18..25 = GROUND
+*/
+
+//#include <linux/parport.h>
+#define LPCHAR 0x0601
+#define LPCAREFUL 0x0609 /* obsoleted??? wtf? */
+#define LPGETSTATUS 0x060b /* return LP_S(minor) */
+#define LPGETFLAGS 0x060e /* get status flags */
+
+#include <sys/ioctl.h>
+
+struct ParallelPort;
+void ParallelPort_call(ParallelPort *self);
+\class ParallelPort : FObject {
+ FILE *f;
+ int fd;
+ int status;
+ int flags;
+ bool manually;
+ t_clock *clock;
+ ~ParallelPort () {if (clock) clock_free(clock); if (f) fclose(f);}
+ \constructor (string port, bool manually=0) {
+ f = fopen(port.data(),"r+");
+ if (!f) RAISE("open %s: %s",port.data(),strerror(errno));
+ fd = fileno(f);
+ status = 0xdeadbeef;
+ flags = 0xdeadbeef;
+ this->manually = manually;
+ clock = manually ? 0 : clock_new(this,(void(*)())ParallelPort_call);
+ clock_delay(clock,0);
+ }
+ void call ();
+ \decl 0 float (float x);
+ \decl 0 bang ();
+};
+\def 0 float (float x) {
+ uint8 foo = (uint8) x;
+ fwrite(&foo,1,1,f);
+ fflush(f);
+}
+void ParallelPort_call(ParallelPort *self) {self->call();}
+void ParallelPort::call() {
+ int flags;
+ if (ioctl(fd,LPGETFLAGS,&flags)<0) post("ioctl: %s",strerror(errno));
+ if (this->flags!=flags) outlet_float(bself->outlets[2],flags);
+ this->flags = flags;
+ int status;
+ if (ioctl(fd,LPGETSTATUS,&status)<0) post("ioctl: %s",strerror(errno));
+ if (this->status!=status) outlet_float(bself->outlets[1],status);
+ this->status = status;
+ if (clock) clock_delay(clock,2000);
+}
+\def 0 bang () {status = flags = 0xdeadbeef; call();}
+//outlet 0 reserved (future use)
+\end class {install("parallel_port",1,3);}
+
+//****************************************************************
+
+\class Route2 : FObject {
+ int nsels;
+ t_symbol **sels;
+ ~Route2() {if (sels) delete[] sels;}
+ \constructor (...) {nsels=0; sels=0; _1_list(argc,argv); bself->noutlets_set(1+nsels);}
+ \decl void anything(...);
+ \decl 1 list(...);
+};
+\def void anything(...) {
+ t_symbol *sel = gensym(argv[0].a_symbol->s_name+3);
+ int i=0;
+ for (i=0; i<nsels; i++) if (sel==sels[i]) break;
+ outlet_anything(bself->outlets[i],sel,argc-1,argv+1);
+}
+\def 1 list(...) {
+ for (int i=0; i<argc; i++) if (argv[i].a_type!=A_SYMBOL) {delete[] sels; RAISE("$%d: expected symbol",i+1);}
+ if (sels) delete[] sels;
+ nsels = argc;
+ sels = new t_symbol*[argc];
+ for (int i=0; i<argc; i++) sels[i] = argv[i].a_symbol;
+}
+\end class {install("route2",1,1);}
+
+template <class T> int sgn(T a, T b=0) {return a<b?-1:a>b;}
+
+\class Shunt : FObject {
+ int n;
+ \attr int index;
+ \attr int mode;
+ \attr int hi;
+ \attr int lo;
+ \constructor (int n=2, int i=0) {
+ this->n=n;
+ this->hi=n-1;
+ this->lo=0;
+ this->mode=0;
+ this->index=i;
+ bself->noutlets_set(n);
+ }
+ \decl void anything(...);
+ \decl 1 float(int i);
+};
+\def void anything(...) {
+ t_symbol *sel = gensym(argv[0].a_symbol->s_name+3);
+ outlet_anything(bself->outlets[index],sel,argc-1,argv+1);
+ if (mode) {
+ index += sgn(mode);
+ if (index<lo || index>hi) {
+ int k = max(hi-lo+1,0);
+ int m = gf_abs(mode);
+ if (m==1) index = mod(index-lo,k)+lo; else {mode=-mode; index+=mode;}
+ }
+ }
+}
+\def 1 float(int i) {index = mod(i,n);}
+\end class {install("shunt",2,0);}
+
+struct Receives;
+struct ReceivesProxy {
+ t_pd x_pd;
+ Receives *parent;
+ t_symbol *suffix;
+};
+t_class *ReceivesProxy_class;
+
+\class Receives : FObject {
+ int ac;
+ ReceivesProxy **av;
+ t_symbol *prefix;
+ t_symbol *local (t_symbol *suffix) {return gensym((string(prefix->s_name) + string(suffix->s_name)).data());}
+ \constructor (t_symbol *prefix=&s_, ...) {
+ this->prefix = prefix==gensym("empty") ? &s_ : prefix;
+ int n = min(1,argc);
+ do_bind(argc-n,argv+n);
+ }
+ \decl 0 bang ();
+ \decl 0 symbol (t_symbol *s);
+ \decl 0 list (...);
+ void do_bind (int argc, t_atom2 *argv) {
+ ac = argc;
+ av = new ReceivesProxy *[argc];
+ for (int i=0; i<ac; i++) {
+ av[i] = (ReceivesProxy *)pd_new(ReceivesProxy_class);
+ av[i]->parent = this;
+ av[i]->suffix = argv[i];
+ pd_bind( (t_pd *)av[i],local(av[i]->suffix));
+ }
+ }
+ void do_unbind () {
+ for (int i=0; i<ac; i++) {
+ pd_unbind((t_pd *)av[i],local(av[i]->suffix));
+ pd_free((t_pd *)av[i]);
+ }
+ delete[] av;
+ }
+ ~Receives () {do_unbind();}
+};
+\def 0 bang () {_0_list(0,0);}
+\def 0 symbol (t_symbol *s) {t_atom2 a[1]; SETSYMBOL(a,s); _0_list(1,a);}
+\def 0 list (...) {
+ do_unbind();
+ do_bind(argc,argv);
+}
+void ReceivesProxy_anything (ReceivesProxy *self, t_symbol *s, int argc, t_atom *argv) {
+ outlet_symbol( self->parent->bself->outlets[1],self->suffix);
+ outlet_anything(self->parent->bself->outlets[0],s,argc,argv);
+}
+\end class {
+ install("receives",1,2);
+ ReceivesProxy_class = class_new(gensym("receives.proxy"),0,0,sizeof(ReceivesProxy),CLASS_PD|CLASS_NOINLET, A_NULL);
+ class_addanything(ReceivesProxy_class,(t_method)ReceivesProxy_anything);
+}
+
+/* this can't report on bang,float,symbol,pointer,list because zgetfn can't either */
+\class ClassExists : FObject {
+ \constructor () {}
+ \decl void _0_symbol(t_symbol *s);
+};
+\def void _0_symbol(t_symbol *s) {
+ outlet_float(bself->outlets[0],!!zgetfn(&pd_objectmaker,s));
+}
+\end class {install("class_exists",1,1);}
+
+\class ListEqual : FObject {
+ t_list *list;
+ \constructor (...) {list=0; _1_list(argc,argv);}
+ \decl 0 list (...);
+ \decl 1 list (...);
+};
+\def 1 list (...) {
+ if (list) list_free(list);
+ list = list_new(argc,argv);
+}
+\def 0 list (...) {
+ if (binbuf_getnatom(list) != argc) {outlet_float(bself->outlets[0],0); return;}
+ t_atom2 *at = (t_atom2 *)binbuf_getvec(list);
+ for (int i=0; i<argc; i++) if (!atom_eq(at[i],argv[i])) {outlet_float(bself->outlets[0],0); return;}
+ outlet_float(bself->outlets[0],1);
+}
+\end class {install("list.==",2,1);}
+
+//****************************************************************
+//#ifdef UNISTD
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/param.h>
+#include <unistd.h>
+//#endif
+#if defined (__APPLE__) || defined (__FreeBSD__)
+#define HZ CLK_TCK
+#endif
+
+uint64 cpu_hertz;
+int uint64_compare(uint64 &a, uint64 &b) {return a<b?-1:a>b;}
+
+\class UserTime : FObject {
+ clock_t time;
+ \constructor () {_0_bang(argc,argv);}
+ \decl 0 bang ();
+ \decl 1 bang ();
+};
+\def 0 bang () {struct tms t; times(&t); time = t.tms_utime;}
+\def 1 bang () {struct tms t; times(&t); outlet_float(bself->outlets[0],(t.tms_utime-time)*1000/HZ);}
+\end class {install("usertime",2,1);}
+\class SystemTime : FObject {
+ clock_t time;
+ \constructor () {_0_bang(argc,argv);}
+ \decl 0 bang ();
+ \decl 1 bang ();
+};
+\def 0 bang () {struct tms t; times(&t); time = t.tms_stime;}
+\def 1 bang () {struct tms t; times(&t); outlet_float(bself->outlets[0],(t.tms_stime-time)*1000/HZ);}
+\end class {install("systemtime",2,1);}
+\class TSCTime : FObject {
+ uint64 time;
+ \constructor () {_0_bang(argc,argv);}
+ \decl 0 bang ();
+ \decl 1 bang ();
+};
+\def 0 bang () {time=rdtsc();}
+\def 1 bang () {outlet_float(bself->outlets[0],(rdtsc()-time)*1000.0/cpu_hertz);}
+\end class {install("tsctime",2,1);
+ struct timeval t0,t1;
+ uint64 u0,u1;
+ uint64 estimates[3];
+ for (int i=0; i<3; i++) {
+ u0=rdtsc(); gettimeofday(&t0,0); usleep(10000);
+ u1=rdtsc(); gettimeofday(&t1,0);
+ uint64 t = (t1.tv_sec-t0.tv_sec)*1000000+(t1.tv_usec-t0.tv_usec);
+ estimates[i] = (u1-u0)*1000000/t;
+ }
+ qsort(estimates,3,sizeof(uint64),(comparator_t)uint64_compare);
+ cpu_hertz = estimates[1];
+}
+
+\class GFError : FObject {
+ string format;
+ \constructor (...) {
+ std::ostringstream o;
+ char buf[MAXPDSTRING];
+ for (int i=0; i<argc; i++) {
+ atom_string(&argv[i],buf,MAXPDSTRING);
+ o << buf;
+ if (i!=argc-1) o << ' ';
+ }
+ format = o.str();
+ }
+ \decl 0 bang ();
+ \decl 0 float (float f);
+ \decl 0 symbol (t_symbol *s);
+ \decl 0 list (...);
+};
+\def 0 bang () {_0_list(0,0);}
+\def 0 float (float f) {_0_list(argc,argv);}
+\def 0 symbol (t_symbol *s) {_0_list(argc,argv);}
+
+\def 0 list (...) {
+ std::ostringstream o;
+ pd_oprintf(o,format.data(),argc,argv);
+ t_canvas *canvas = canvas_getrootfor(bself->mom);
+ string s = o.str();
+ pd_error(canvas,"%s",s.data());
+}
+\end class {install("gf.error",1,0);}
+
+//****************************************************************
+\class ForEach : FObject {
+ \constructor () {}
+ \decl 0 list (...);
+};
+\def 0 list (...) {
+ t_outlet *o = bself->outlets[0];
+ for (int i=0; i<argc; i++) {
+ if (argv[i].a_type==A_FLOAT) outlet_float( o,argv[i]);
+ else if (argv[i].a_type==A_SYMBOL) outlet_symbol(o,argv[i]);
+ else RAISE("oops. unsupported.");
+ }
+}
+\end class {install("foreach",1,1);}
+
+//****************************************************************
+
+#define MOM \
+ t_canvas *mom = bself->mom; \
+ for (int i=0; i<n; i++) {mom = mom->gl_owner; if (!mom) RAISE("no such canvas");}
+
+\class GFCanvasFileName : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 bang ();
+};
+\def 0 bang () {MOM; outlet_symbol(bself->outlets[0],mom->gl_name ? mom->gl_name : gensym("empty"));}
+\end class {install("gf/canvas_filename",1,1);}
+\class GFCanvasDollarZero : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 bang ();
+};
+\def 0 bang () {MOM; outlet_float(bself->outlets[0],canvas_getenv(mom)->ce_dollarzero);}
+\end class {install("gf/canvas_dollarzero",1,1);}
+\class GFCanvasGetPos : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 bang ();
+};
+\def 0 bang () {MOM;
+ t_atom a[2];
+ SETFLOAT(a+0,mom->gl_obj.te_xpix);
+ SETFLOAT(a+1,mom->gl_obj.te_ypix);
+ outlet_list(bself->outlets[0],&s_list,2,a);
+}
+\end class {install("gf/canvas_getpos",1,1);}
+\class GFCanvasSetPos : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 list (...);
+};
+\def 0 list (...) {
+ MOM;
+ if (argc!=2) RAISE("wrong number of args");
+ mom->gl_obj.te_xpix = atom_getfloatarg(0,argc,argv);
+ mom->gl_obj.te_ypix = atom_getfloatarg(1,argc,argv);
+ t_canvas *granny = mom->gl_owner;
+ if (!granny) RAISE("no such canvas");
+#ifdef DESIREDATA
+ gobj_changed(mom);
+#else
+ gobj_vis((t_gobj *)mom,granny,0);
+ gobj_vis((t_gobj *)mom,granny,1);
+ canvas_fixlinesfor(glist_getcanvas(granny), (t_text *)mom);
+#endif
+}
+\end class {install("gf/canvas_setpos",1,0);}
+\class GFCanvasEditMode : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 bang ();
+};
+\def 0 bang () {MOM;
+ t_atom a[1]; SETFLOAT(a+0,0);
+ outlet_float(bself->outlets[0],mom->gl_edit);
+}
+\end class {install("gf/canvas_edit_mode",1,1);}
+extern "C" void canvas_setgraph(t_glist *x, int flag, int nogoprect);
+\class GFCanvasSetGOP : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 float (float gop);
+};
+\def 0 float (float gop) {MOM; t_atom a[1]; SETFLOAT(a+0,0); canvas_setgraph(mom,gop,0);}
+\end class {install("gf/canvas_setgop",1,0);}
+\class GFCanvasXID : FObject {
+ int n;
+ t_symbol *name;
+ \constructor (int n_) {
+ n=n_;
+ name=symprintf("gf/canvas_xid:%lx",bself);
+ pd_bind((t_pd *)bself,name);
+ }
+ ~GFCanvasXID () {pd_unbind((t_pd *)bself,name);}
+ \decl 0 bang ();
+ \decl 0 xid (t_symbol *t, t_symbol *u);
+};
+\def 0 bang () {
+ t_canvas *mom = bself->mom;
+ for (int i=0; i<n; i++) {mom = mom->gl_owner; if (!mom) RAISE("no such canvas");}
+ sys_vgui("pd %s xid [winfo id .x%lx.c] [winfo id .x%lx]\\;\n",name->s_name,long(mom));
+}
+\def 0 xid (t_symbol *t, t_symbol *u) {
+ outlet_symbol(bself->outlets[0],t);
+ outlet_symbol(bself->outlets[1],u);
+}
+\end class {install("gf/canvas_xid",1,2);}
+
+\class GFCanvasHeHeHe : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 float (float y);
+};
+\def 0 float (float y) {MOM;
+ // was 568
+ mom->gl_screenx2 = mom->gl_screenx1 + 600;
+ if (mom->gl_screeny2-mom->gl_screeny1 < y) mom->gl_screeny2 = mom->gl_screeny1+y;
+ sys_vgui("wm geometry .x%lx %dx%d\n",long(mom),
+ int(mom->gl_screenx2-mom->gl_screenx1),
+ int(mom->gl_screeny2-mom->gl_screeny1));
+}
+\end class {install("gf/canvas_hehehe",1,1);}
+
+#define DASHRECT "-outline #80d4b2 -dash {2 6 2 6}"
+
+\class GFCanvasHoHoHo : FObject {
+ int n;
+ t_canvas *last;
+ \constructor (int n) {this->n=n; last=0;}
+ void hide () {if (last) sys_vgui(".x%lx.c delete %lxRECT\n",long(last),bself);}
+ ~GFCanvasHoHoHo () {hide();}
+ \decl 0 list (int x1, int y1, int x2, int y2);
+};
+\def 0 list (int x1, int y1, int x2, int y2) {
+ hide();
+ MOM;
+ last = mom;
+ sys_vgui(".x%lx.c create rectangle %d %d %d %d "DASHRECT" -tags %lxRECT\n",long(last),x1,y1,x2,y2,bself);
+}
+\end class {install("gf/canvas_hohoho",1,0);}
+
+#define canvas_each(y,x) for (t_gobj *y=x->gl_list; y; y=y->g_next)
+\class GFCanvasCount : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 bang ();
+};
+\def 0 bang () {MOM; int k=0; canvas_each(y,mom) k++; outlet_float(bself->outlets[0],k);}
+\end class {install("gf/canvas_count",1,1);}
+
+\class GFCanvasLoadbang : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 float (float m);
+};
+\def 0 float (float m) {MOM;
+ int k=0;
+ canvas_each(y,mom) {
+ k++;
+ if (k>=m && pd_class((t_pd *)y)==canvas_class) canvas_loadbang((t_canvas *)y);
+ }
+
+}
+\end class {
+ install("gf/canvas_loadbang",1,0);
+};
+
+\class GFLOL : FObject {
+ int n;
+ \constructor (int n) {this->n=n;}
+ \decl 0 wire_dotted (int r, int g, int b);
+ \decl 0 wire_hide ();
+ \decl 0 box_dotted (int r, int g, int b);
+ \decl 0 box_align (t_symbol *s, int x_start, int y_start, int incr);
+};
+#define BEGIN \
+ t_outlet *ouch = ((t_object *)bself->mom)->te_outlet; \
+ t_canvas *can = bself->mom->gl_owner; \
+ if (!can) RAISE("no such canvas"); \
+ for (int i=0; i<n; i++) {ouch = ouch->next; if (!ouch) {RAISE("no such outlet");}}
+#define wire_each(wire,ouchlet) for (t_outconnect *wire = ouchlet->connections; wire; wire=wire->next)
+\def 0 wire_dotted (int r, int g, int b) {
+#ifndef DESIREDATA
+ BEGIN
+ wire_each(wire,ouch) {
+ sys_vgui(".x%lx.c itemconfigure l%lx -fill #%02x%02x%02x -dash {3 3 3 3}\n",long(can),long(wire),r,g,b);
+ }
+#else
+ post("doesn't work with DesireData");
+#endif
+}
+\def 0 wire_hide () {
+#ifndef DESIREDATA
+ BEGIN
+ wire_each(wire,ouch) sys_vgui(".x%lx.c delete l%lx\n",long(can),long(wire));
+#else
+ post("doesn't work with DesireData");
+#endif
+}
+\def 0 box_dotted (int r, int g, int b) {
+#ifndef DESIREDATA
+ BEGIN
+ wire_each(wire,ouch) {
+ t_object *t = (t_object *)wire->to;
+ int x1,y1,x2,y2;
+ gobj_getrect((t_gobj *)wire->to,can,&x1,&y1,&x2,&y2);
+ // was #00aa66 {3 5 3 5}
+ sys_vgui(".x%lx.c delete %lxRECT; .x%lx.c create rectangle %d %d %d %d "DASHRECT" -tags %lxRECT\n",
+ long(can),long(t),long(can),x1,y1,x2,y2,long(t));
+ }
+#else
+ post("doesn't work with DesireData");
+#endif
+}
+bool comment_sort_y_lt(t_object * const &a, t_object * const &b) /* is a StrictWeakOrdering */ {
+ return a->te_ypix < b->te_ypix;
+}
+#define foreach(ITER,COLL) for(typeof(COLL.begin()) ITER = COLL.begin(); ITER != (COLL).end(); ITER++)
+static t_class *inlet_class, *floatinlet_class, *symbolinlet_class, *pointerinlet_class;
+static bool ISINLET(t_pd *o) {
+ t_class *c=pd_class(o);
+ return c==inlet_class || c==floatinlet_class || c==symbolinlet_class || c==pointerinlet_class;
+}
+struct _inlet {
+ t_pd pd;
+ struct _inlet *next;
+ t_object *owner;
+ t_pd *dest;
+ t_symbol *symfrom;
+ //union inletunion un;
+};
+\def 0 box_align (t_symbol *dir, int x_start, int y_start, int incr) {
+ int x=x_start, y=y_start;
+ bool horiz;
+ if (dir==&s_x) horiz=false; else
+ if (dir==&s_y) horiz=true; else RAISE("$1 must be x or y");
+#ifndef DESIREDATA
+ std::vector<t_object *> v;
+ BEGIN
+ wire_each(wire,ouch) {
+ //post("wire to object of class %s ISINLET=%d",pd_class(wire->to)->c_name->s_name,ISINLET(wire->to));
+ t_object *to = ISINLET(wire->to) ? ((t_inlet *)wire->to)->owner : (t_object *)wire->to;
+ v.push_back(to);
+ }
+ sort(v.begin(),v.end(),comment_sort_y_lt);
+ foreach(tt,v) {
+ t_object *t = *tt;
+ if (t->te_xpix!=x || t->te_ypix!=y) {
+ gobj_vis((t_gobj *)t,can,0);
+ t->te_xpix=x;
+ t->te_ypix=y;
+ gobj_vis((t_gobj *)t,can,1);
+ canvas_fixlinesfor(can,t);
+ }
+ int x1,y1,x2,y2;
+ gobj_getrect((t_gobj *)t,can,&x1,&y1,&x2,&y2);
+ if (horiz) x += x2-x1+incr;
+ else y += y2-y1+incr;
+ }
+ if (horiz) outlet_float(bself->outlets[0],x-x_start);
+ else outlet_float(bself->outlets[0],y-y_start);
+#else
+ post("doesn't work with DesireData");
+#endif
+}
+
+extern t_widgetbehavior text_widgetbehavior;
+t_widgetbehavior text_widgetbehavi0r;
+
+/* i was gonna use gobj_shouldvis but it's only for >= 0.42 */
+
+static int text_chou_de_vis(t_text *x, t_glist *glist) {
+ return (glist->gl_havewindow ||
+ (x->te_pd != canvas_class && x->te_pd->c_wb != &text_widgetbehavior) ||
+ (x->te_pd == canvas_class && (((t_glist *)x)->gl_isgraph)) ||
+ (glist->gl_goprect && (x->te_type == T_TEXT)));
+}
+
+static void text_visfn_hax0r (t_gobj *o, t_canvas *can, int vis) {
+ text_widgetbehavior.w_visfn(o,can,vis);
+ //if (vis) return; // if you want to see #X text inlets uncomment this line
+ t_rtext *y = glist_findrtext(can,(t_text *)o);
+ if (text_chou_de_vis((t_text *)o,can)) glist_eraseiofor(can,(t_object *)o,rtext_gettag(y));
+}
+\end class {
+ install("gf/lol",1,1);
+#ifndef DESIREDATA
+ class_setpropertiesfn(text_class,(t_propertiesfn)0xDECAFFED);
+ unsigned long *lol = (unsigned long *)text_class;
+ int i=0;
+ while (lol[i]!=0xDECAFFED) i++;
+ *((char *)(lol+i+1) + 6) = 1;
+ class_setpropertiesfn(text_class,0);
+ t_object *bogus = (t_object *)pd_new(text_class);
+ inlet_class = pd_class((t_pd *) inlet_new(bogus,0,0,0));
+ floatinlet_class = pd_class((t_pd *) floatinlet_new(bogus,0));
+ symbolinlet_class = pd_class((t_pd *) symbolinlet_new(bogus,0));
+ pointerinlet_class = pd_class((t_pd *)pointerinlet_new(bogus,0));
+ memcpy(&text_widgetbehavi0r,&text_widgetbehavior,sizeof(t_widgetbehavior));
+ text_widgetbehavi0r.w_visfn = text_visfn_hax0r;
+ class_setwidget(text_class,&text_widgetbehavi0r);
+#endif
+}
+
+\class GFStringReplace : FObject {
+ t_symbol *from;
+ t_symbol *to;
+ \constructor (t_symbol *from, t_symbol *to=&s_) {this->from=from; this->to=to;}
+ \decl 0 symbol (t_symbol *victim);
+};
+\def 0 symbol (t_symbol *victim) {
+ string a = string(victim->s_name);
+ string b = string(from->s_name);
+ string c = string(to->s_name);
+ for (size_t i=0;;) {
+ i = a.find(b,i);
+ if (i==string::npos) break;
+ a = a.replace(i,b.length(),c);
+ i += c.length();
+ }
+ outlet_symbol(bself->outlets[0],gensym(a.c_str()));
+}
+\end class {install("gf/string_replace",1,1);}
+
+\class GFStringLessThan : FObject {
+ t_symbol *than;
+ \constructor (t_symbol *than=&s_) {this->than=than;}
+ \decl 0 symbol (t_symbol *it);
+ \decl 1 symbol (t_symbol *than);
+};
+\def 0 symbol (t_symbol *it) {outlet_float(bself->outlets[0],strcmp(it->s_name,than->s_name)<0);}
+\def 1 symbol (t_symbol *than) {this->than=than;}
+\end class {install("gf/string_<",2,1);}
+
+void startup_flow_objects2 () {
+ \startall
+}
diff --git a/externals/gridflow/src/dc1394.cxx b/externals/gridflow/src/dc1394.cxx
new file mode 100644
index 00000000..e2361a8e
--- /dev/null
+++ b/externals/gridflow/src/dc1394.cxx
@@ -0,0 +1,287 @@
+/*
+ $Id: dc1394.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define DC1394_INCLUDE_HERE
+#include <libraw1394/raw1394.h>
+#include "gridflow.hxx.fcs"
+
+/* speeds are numbered 0 to 5, worth 100<<speednum */
+/* framerates are numbers 32 to 39, worth 1.875<<(frameratenum-32) */
+
+#define MODE(x,y,palette) /* nothing for now */
+
+static std::map<int,string> feature_names;
+
+static void setup_modes () {
+ int i=64; // format 0
+ MODE(160,120,YUV444);
+ MODE(320,240,YUV422);
+ MODE(640,480,YUV411);
+ MODE(640,480,YUV422);
+ MODE(640,480,RGB);
+ MODE(640,480,MONO);
+ MODE(640,480,MONO16);
+ i=96; // format 1
+ MODE(800,600,YUV422);
+ MODE(800,600,RGB);
+ MODE(800,600,MONO);
+ MODE(1024,768,YUV422);
+ MODE(1024,768,RGB);
+ MODE(1024,768,MONO);
+ MODE(800,600,MONO16);
+ MODE(1024,768,MONO16);
+ i=128; // format 2
+ MODE(1280,960,YUV422);
+ MODE(1280,960,RGB);
+ MODE(1280,960,MONO);
+ MODE(1600,1200,YUV422);
+ MODE(1600,1200,RGB);
+ MODE(1600,1200,MONO);
+ MODE(1280,960,MONO16);
+ MODE(1600,1200,MONO16);
+ i=256; // format 6
+ // MODE_EXIF= 256
+ i=288; // format 7
+ //MODE_FORMAT7_0,
+ //MODE_FORMAT7_1,
+ //MODE_FORMAT7_2,
+ //MODE_FORMAT7_3,
+ //MODE_FORMAT7_4,
+ //MODE_FORMAT7_5,
+ //MODE_FORMAT7_6,
+ //MODE_FORMAT7_7
+
+// format7 color modes start at #320 and are MONO8 YUV411 YUV422 YUV444 RGB8 MONO16 RGB16 MONO16S RGB16S RAW8 RAW16
+// trigger modes start at #352 and are 0 1 2 3
+// image formats start at #384 and are VGA_NONCOMPRESSED SVGA_NONCOMPRESSED_1 SVGA_NONCOMPRESSED_2
+// and continue at #390 and are STILL_IMAGE FORMAT_SCALABLE_IMAGE_SIZE
+
+#define FEATURE(foo) feature_names[i++] = #foo;
+
+ i=416;
+ FEATURE(BRIGHTNESS);
+ FEATURE(EXPOSURE);
+ FEATURE(SHARPNESS);
+ FEATURE(WHITE_BALANCE);
+ FEATURE(HUE);
+ FEATURE(SATURATION);
+ FEATURE(GAMMA);
+ FEATURE(SHUTTER);
+ FEATURE(GAIN);
+ FEATURE(IRIS);
+ FEATURE(FOCUS);
+ FEATURE(TEMPERATURE);
+ FEATURE(TRIGGER);
+ FEATURE(TRIGGER_DELAY);
+ FEATURE(WHITE_SHADING);
+ FEATURE(FRAME_RATE);
+ i+=16;/* 16 reserved features */
+ FEATURE(ZOOM);
+ FEATURE(PAN);
+ FEATURE(TILT);
+ FEATURE(OPTICAL_FILTER);
+ i+=12;/* 12 reserved features */
+ FEATURE(CAPTURE_SIZE);
+ FEATURE(CAPTURE_QUALITY);
+ i+=14;/* 14 reserved features */
+
+ i=480; // operation modes
+ //OPERATION_MODE_LEGACY
+ //OPERATION_MODE_1394B
+
+ i=512; // sensor layouts
+ //RGGB
+ //GBRG,
+ //GRBG,
+ //BGGR
+
+ i=544; // IIDC_VERSION
+#if 0
+ IIDC_VERSION(1_04);
+ IIDC_VERSION(1_20);
+ IIDC_VERSION(PTGREY);
+ IIDC_VERSION(1_30);
+ IIDC_VERSION(1_31);
+ IIDC_VERSION(1_32);
+ IIDC_VERSION(1_33);
+ IIDC_VERSION(1_34);
+ IIDC_VERSION(1_35);
+ IIDC_VERSION(1_36);
+ IIDC_VERSION(1_37);
+ IIDC_VERSION(1_38);
+ IIDC_VERSION(1_39);
+#endif
+
+// Return values are SUCCESS FAILURE NO_FRAME NO_CAMERA
+
+// Parameter flags for dc1394_setup_format7_capture()
+//#define QUERY_FROM_CAMERA -1
+//#define USE_MAX_AVAIL -2
+//#define USE_RECOMMENDED -3
+
+// The video1394 policy: blocking (wait for a frame forever) or polling (returns if no frames in buffer
+// WAIT=0 POLL=1
+};
+
+typedef raw1394handle_t RH;
+typedef nodeid_t NID;
+
+#define IO(func,args...) if (func(rh,usenode,args)!=DC1394_SUCCESS) RAISE(#func " failed");
+
+\class FormatDC1394 : Format {
+ RH rh;
+ int useport;
+ int usenode;
+ int framerate_e;
+ int height;
+ int width;
+ dc1394_cameracapture camera;
+ dc1394_feature_set features;
+ std::map<int,int> feature_index;
+ \constructor (t_symbol *mode) {
+ bool gotone=false;
+ post("DC1394: hello world");
+ rh = raw1394_new_handle();
+ if (!rh) RAISE("could not get a handle for /dev/raw1394 and /dev/video1394");
+ int numPorts = raw1394_get_port_info(rh,0,0);
+ raw1394_destroy_handle(rh);
+ post("there are %d Feuerweuer ports",numPorts);
+ if (mode!=gensym("in")) RAISE("sorry, read-only");
+ for(int port=0; port<numPorts; port++) {
+ post("trying port #%d...",port);
+ RH rh = dc1394_create_handle(port);
+ int numCameras=0xDEADBEEF;
+ NID *nodes = dc1394_get_camera_nodes(rh,&numCameras,0);
+ post("port #%d has %d cameras",port,numCameras);
+ for (int i=0; i<numCameras; i++) {
+ post("camera at node #%d",nodes[i]);
+ if (!gotone) {gotone=true; useport=port; usenode=nodes[i];}
+ }
+ dc1394_destroy_handle(rh);
+ }
+ if (!gotone) RAISE("no cameras available");
+ this->rh = dc1394_create_handle(useport);
+ IO(dc1394_get_camera_feature_set,&features);
+ dc1394_print_feature_set(&features);
+ post("NUM_FEATURES=%d",NUM_FEATURES);
+ for (int i=0; i<NUM_FEATURES; i++) {
+ dc1394_feature_info &f = features.feature[i];
+ int id = f.feature_id;
+ string name = feature_names.find(id)==feature_names.end() ? "(unknown)" : feature_names[id];
+ bool is_there = f.available;
+ post(" feature %d '%s' is %s",id,name.data(),is_there?"present":"absent");
+ if (!is_there) continue;
+ post(" min=%u max=%u abs_min=%u abs_max=%u",f.min,f.max,f.abs_min,f.abs_max);
+ }
+ framerate_e = FRAMERATE_30;
+ height = 480;
+ width = 640;
+ setup();
+ }
+ \decl 0 bang ();
+ \attr float framerate();
+ \attr unsigned brightness();
+ \attr unsigned hue();
+ \attr unsigned colour();
+ //\attr uint16 contrast();
+ //\attr uint16 whiteness();
+ void setup ();
+ \decl 0 get (t_symbol *s=0);
+ \decl 0 size (int height, int width);
+};
+
+\def 0 get (t_symbol *s=0) {
+ FObject::_0_get(argc,argv,s);
+ t_atom a[2];
+ if (!s) {
+ SETFLOAT(a+0,camera.frame_height);
+ SETFLOAT(a+1,camera.frame_width);
+ outlet_anything(bself->outlets[0],gensym("size"),2,a); // abnormal (does not use nested list)
+ unsigned int width,height;
+ IO(dc1394_query_format7_max_image_size,MODE_FORMAT7_0,&width,&height);
+ SETFLOAT(a+0,height);
+ SETFLOAT(a+1,width);
+ outlet_anything(bself->outlets[0],gensym("maxsize"),2,a); // abnormal (does not use nested list)
+ }
+}
+\def 0 size (int height, int width) {
+ IO(dc1394_set_format7_image_size,MODE_FORMAT7_0,width,height);
+ this->height = height;
+ this->width = width;
+ setup();
+}
+
+\def unsigned brightness () {unsigned value; dc1394_get_brightness(rh,usenode,&value); return value;}
+\def 0 brightness (unsigned value) {dc1394_set_brightness(rh,usenode, value);}
+\def unsigned hue () {unsigned value; dc1394_get_hue( rh,usenode,&value); return value;}
+\def 0 hue (unsigned value) {dc1394_set_hue( rh,usenode, value);}
+\def unsigned colour () {unsigned value; dc1394_get_saturation(rh,usenode,&value); return value;}
+\def 0 colour (unsigned value) {dc1394_set_saturation(rh,usenode, value);}
+
+void FormatDC1394::setup () {
+ //dc1394_set_format7_image_size(rh,usenode,0,width,height);
+ IO(dc1394_setup_capture,0,FORMAT_VGA_NONCOMPRESSED,MODE_640x480_MONO,SPEED_400,framerate_e,&camera);
+ //IO(dc1394_setup_format7_capture,0,MODE_FORMAT7_0,SPEED_400,QUERY_FROM_CAMERA,0,0,width,height,&camera);
+ if (dc1394_set_trigger_mode(rh,usenode,TRIGGER_MODE_0) != DC1394_SUCCESS) RAISE("dc1394_set_trigger_mode error");
+ if (dc1394_start_iso_transmission(rh,usenode)!=DC1394_SUCCESS) RAISE("dc1394_start_iso_transmission error");
+}
+
+\def float framerate() {
+ return 1.875 * (1<<(framerate_e-FRAMERATE_1_875));
+}
+
+\def 0 framerate(float framerate) {
+ framerate_e = FRAMERATE_1_875;
+ while (framerate>=1.875 && framerate_e <= FRAMERATE_240) {framerate/=2; framerate_e++;}
+ setup();
+}
+
+static volatile int timeout=0;
+static void rien (int) {timeout=1; post("timeout2");}
+
+\def 0 bang () {
+ //struct itimerval tval;
+ //tval.it_interval.tv_sec = 1;
+ //tval.it_interval.tv_usec = 0;
+ //tval.it_value = tval.it_interval;
+ //setitimer(ITIMER_REAL,&tval,0);
+ //signal(SIGALRM,rien);
+ if (dc1394_single_capture(rh,&camera)!=DC1394_SUCCESS) RAISE("dc1394_single_capture error");
+ //setitimer(ITIMER_REAL,0,0);
+ out=new GridOutlet(this,0,new Dim(height,width,1));
+ //out->send(out->dim->prod(),(uint8 *)camera.capture_buffer);
+ for (int i=0; i<height; i++) out->send(out->dim->prod(1),(uint8 *)camera.capture_buffer+640*i);
+ //if (dc1394_stop_iso_transmission(rh,usenode)!=DC1394_SUCCESS) RAISE("dc1394_stop_iso_transmission error");
+ //post("frame_height=%d",camera.frame_height);
+ //post("frame_width=%d" ,camera.frame_width);
+ //post("quadlets_per_frame=%d" ,camera.quadlets_per_frame);
+ //post("quadlets_per_packet=%d" ,camera.quadlets_per_packet);
+}
+
+\end class FormatDC1394 {
+ install_format("#io.dc1394",4,"");
+ setup_modes();
+}
+void startup_dc1394 () {
+ \startall
+}
diff --git a/externals/gridflow/src/fftw.cxx b/externals/gridflow/src/fftw.cxx
new file mode 100644
index 00000000..7aa3f44c
--- /dev/null
+++ b/externals/gridflow/src/fftw.cxx
@@ -0,0 +1,114 @@
+/*
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <fftw3.h>
+
+#define C(x) ((fftwf_complex *)x)
+
+\class GridFFT : FObject {
+ fftwf_plan plan;
+ P<Dim> lastdim; /* of last input (for plan cache) */
+ long lastchans; /* of last input (for plan cache) */
+ \attr int sign; /* -1 or +1 */
+ \attr int skip; /* 0 (y and x) or 1 (x only) */
+ \attr bool real;
+ bool lastreal;
+ \constructor () {sign=-1; plan=0; lastdim=0; lastchans=0; skip=0; real=false;}
+ \grin 0 float32
+};
+\def 0 sign (int sign) {
+ if (sign!=-1 && sign!=1) RAISE("sign should be -1 or +1");
+ this->sign=sign;
+ fftwf_destroy_plan(plan);
+}
+\def 0 skip (int skip) {
+ if (skip<0 || skip>1) RAISE("skip should be 0 or 1");
+ this->skip=skip;
+ if (plan) {fftwf_destroy_plan(plan); plan=0;}
+}
+GRID_INLET(0) {
+ if (in->nt != float32_e) RAISE("expecting float32");
+ if (real && sign==-1) {
+ if (in->dim->n != 2 && in->dim->n != 3) RAISE("expecting 2 or 3 dimensions: rows,columns,channels?");
+ } else {
+ if (in->dim->n != 3 && in->dim->n != 4) RAISE("expecting 3 or 4 dimensions: rows,columns,channels?,complex");
+ if (in->dim->get(in->dim->n-1)!=2) RAISE("expecting Dim(...,2): real,imaginary (got %d)",in->dim->get(2));
+ }
+ in->set_chunk(0);
+} GRID_FLOW {
+ if (skip==1 && !real) RAISE("can't do 1-D FFT in real mode, sorry");
+ Dim *dim;
+ if (!real) dim = in->dim;
+ else if (sign==-1) {
+ int v[Dim::MAX_DIM];
+ for (int i=0; i<in->dim->n; i++) v[i]=in->dim->v[i];
+ v[in->dim->n] = 2;
+ dim = new Dim(in->dim->n+1,v);
+ } else dim = new Dim(in->dim->n-1,in->dim->v);
+ GridOutlet out(this,0,dim,in->nt);
+ float32 *tada = (float32 *)memalign(16,dim->prod()*sizeof(float32));
+ long chans = in->dim->n>=3 ? in->dim->get(2) : 1;
+ CHECK_ALIGN16(data,in->nt)
+ CHECK_ALIGN16(tada,in->nt)
+ if (plan && lastdim && lastdim!=in->dim && chans!=lastchans && real==lastreal) {fftwf_destroy_plan(plan); plan=0;}
+ int v[] = {in->dim->v[0],in->dim->v[1],in->dim->n>2?in->dim->v[2]:1};
+// if (chans==1) {
+// if (skip==0) plan = fftwf_plan_dft_2d(v[0],v[1],data,tada,sign,0);
+// if (skip==1) plan = fftwf_plan_many_dft(1,&v[1],v[0],data,0,1,v[1],tada,0,1,v[1],sign,0);
+// }
+ if (skip==0) {
+ //plan = fftwf_plan_dft_2d(v[0],v[1],data,tada,sign,0);
+ if (!plan) {
+ int embed[] = {dim->v[0],dim->v[1]};
+ if (!real) {plan=fftwf_plan_many_dft( 2,&v[0],chans,C(data),0 ,chans,1,C(tada),0 ,chans,1,sign,0);}
+ else if (sign==-1) {plan=fftwf_plan_many_dft_r2c(2,&v[0],chans, data ,embed,chans,1,C(tada),embed,chans,1,0);}
+ else {plan=fftwf_plan_many_dft_c2r(2,&v[0],chans,C(data),embed,chans,1, tada ,embed,chans,1,0);}
+ }
+ if (!real) fftwf_execute_dft( plan,C(data),C(tada));
+ else if (sign==-1) fftwf_execute_dft_r2c(plan, data ,C(tada));
+ else fftwf_execute_dft_c2r(plan,C(data), tada );
+ }
+ if (skip==1) {
+ if (!plan) plan=fftwf_plan_many_dft(1,&v[1],chans,C(data),0,chans,1,C(tada),0,chans,1,sign,0);
+ //plan = fftwf_plan_many_dft(1,&v[1],v[0],C(data),0,1,v[1],C(tada),0,1,v[1],sign,0);
+ long incr = v[1]*chans;
+ for (int i=0; i<v[0]; i++) fftwf_execute_dft(plan,C(data)+i*incr,C(tada)+i*incr);
+ }
+ if (real && sign==-1) {
+ for (int i=0; i<v[0]; i++) {
+ int h = mod(-i,v[0]);
+ T *tada2 = tada + (h*v[1]+v[1]/2)*v[2]*2;
+ T *tada3 = tada + (i*v[1]+v[1]/2)*v[2]*2;
+ for (int j=1+v[1]/2; j<v[1]; j++) {
+ tada2-=v[2]*2; tada3+=v[2]*2;
+ for (int k=0; k<v[2]; k++) {tada3[k+k]=tada2[k+k]; tada3[k+k+1]=-tada2[k+k+1];}
+ }
+ }
+ }
+ out.send(out.dim->prod(),tada);
+ free(tada);
+ lastdim=in->dim; lastchans=chans; lastreal=real;
+} GRID_END
+\end class {install("#fft",1,1);}
+void startup_fftw () {
+ \startall
+}
diff --git a/externals/gridflow/src/formats.cxx b/externals/gridflow/src/formats.cxx
new file mode 100644
index 00000000..50574285
--- /dev/null
+++ b/externals/gridflow/src/formats.cxx
@@ -0,0 +1,266 @@
+/*
+ $Id$
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <string>
+#include <map>
+#include <errno.h>
+#define L _L_
+
+/* API (version 0.9.3)
+ mode is :in or :out
+ def initialize(mode,*args) :
+ open a file handler (do it via .new of class)
+ attr_reader :description :
+ a _literal_ (constant) string describing the format handler
+ def self.info() optional :
+ return a string describing the format handler differently
+ than self.description(). in particular, it can list
+ compile-time options and similar things. for example,
+ quicktime returns a list of codecs.
+ def 0 bang() :
+ read one frame, send through outlet 0
+ return values :
+ Integer >= 0 : frame number of frame read.
+ false : no frame was read : end of sequence.
+ nil : a frame was read, but can't say its number.
+ note that trying to read a nonexistent frame should no longer
+ rewind automatically (@in handles that part), nor re-read the
+ last frame (mpeg/quicktime used to do this)
+ def 0 seek(Integer i) : select one frame to be read next (by number)
+ def 0 grid() : frame to write
+ def 0 get (optional Symbol s) : get one attribute value or all of them
+ def 0 ...() : options
+ outlet 0 grid() frame just read
+ outlet 0 ...() everything else
+ destructor : close a handler
+*/
+
+std::map<std::string,std::string> suffix_table;
+void suffixes_are (const char *name, const char *suffixes) {
+ std::string name2 = name;
+ char *suff2 = strdup(suffixes);
+ char *suff3 = suff2+strlen(suff2);
+ for (char *s=suff2; s<suff3; s++) if (*s==' ' || *s==',') *s=0;
+ for (char *s=suff2; s<suff3; s+=strlen(s)+1) {
+ std::string ss = s;
+ suffix_table[ss]=name2;
+ }
+}
+
+\class SuffixLookup : FObject {
+ \constructor () {}
+ \decl 0 symbol (t_symbol *str);
+};
+\def 0 symbol (t_symbol *str) {
+ char *s = strdup(str->s_name);
+ char *t = strrchr(s,'.');
+ if (!t) outlet_symbol(bself->outlets[2],gensym(s));
+ else {
+ *t = 0;
+ outlet_symbol(bself->outlets[1],gensym(t+1));
+ std::map<std::string,std::string>::iterator u = suffix_table.find(std::string(t+1));
+ if (u==suffix_table.end()) outlet_bang(bself->outlets[0]);
+ else outlet_symbol(bself->outlets[0],gensym((char *)u->second.data()));
+ }
+ free(s);
+}
+\end class SuffixLookup {install("gf.suffix_lookup",1,3);}
+
+\class Format : FObject
+Format::Format (BFObject *bself, MESSAGE) : FObject(bself,MESSAGE2) {
+ mode=0; fd=-1; f=0; cast=int32_e; frame=0;
+ if (argv[0]==gensym("out")) this->mode=2; else
+ if (argv[0]==gensym("in")) this->mode=4; else RAISE("unknown mode");
+// case mode
+// when :in; flags[2]==1
+// when :out; flags[1]==1
+// else raise "Format opening mode is incorrect"
+ //end or raise "Format '#{self.class.instance_eval{@symbol_name}}' does not support mode '#{mode}'"
+}
+
+\def 0 open(t_symbol *mode, string filename) {
+ const char *fmode;
+ if (mode==gensym("in")) fmode="r"; else
+ if (mode==gensym("out")) fmode="w"; else
+ RAISE("bad mode");
+ if (f) _0_close(0,0);
+ if (mode==gensym("in")) {filename = gf_find_file(filename);}
+ f = fopen(filename.data(),fmode);
+ if (!f) RAISE("can't open file '%s': %s",filename.data(),strerror(errno));
+ fd = fileno(f);
+// case gzfile:
+// if (mode==SYM(in)) {filename = GridFlow.find_file(filename);}
+// if (mode==:in) {raw_open_gzip_in filename; else raw_open_gzip_out filename;}
+// def self.rewind() raw_open(*@raw_open_args); @frame = 0 end unless @rewind_redefined
+// @rewind_redefined = true
+}
+\def 0 close() {if (f) {fclose(f); f=0; fd=-1;}}
+\def 0 cast(NumberTypeE nt) {cast = nt;}
+
+\def 0 seek(int frame) {
+ if (!frame) {_0_rewind(0,0); return;}
+ RAISE("don't know how to seek for frame other than # 0");
+}
+
+// this is what you should use to rewind
+// different file-sources may redefine this as something else
+// (eg: gzip)
+\def 0 rewind () {
+ if (!f) RAISE("Nothing to rewind about...");
+ fseek(f,0,SEEK_SET);
+ frame = 0;
+}
+
+Format::~Format () {if (f) fclose(f); /*if (fd>=0) close(fd);*/}
+\end class Format {}
+
+/* This is the Grid format I defined: */
+struct GridHeader {
+ char magic[5]; // = "\x7fgrid" on little endian, "\x7fGRID" on big endian
+ uint8 type; // supported: 8=int8 9=uint8 16=int16 32=int32
+ // unsupported: 34=float32 64=int64 66=float64
+ // (number of bits is multiple of 8; add 1 for unsigned; add 2 for float)
+ uint8 reserved; // set this to 0 all of the time.
+ uint8 dimn; // number of dimensions supported: at least 0..4)
+ // int32 dimv[dimn]; // number of elements in each dimension. (in the file's endianness!)
+ // raw data goes after that
+};
+
+\class FormatGrid : Format {
+ GridHeader head;
+ int endian;
+ NumberTypeE nt;
+ P<Dim> headerless_dim; // if null: headerful; if Dim: it is the assumed dimensions of received grids
+ \grin 0
+ \constructor (t_symbol *mode, string filename) {
+ nt = int32_e;
+ endian = is_le();
+ _0_open(0,0,mode,filename);
+ }
+ \decl 0 bang ();
+ \decl 0 headerless (...);
+ \decl 0 headerful ();
+ \decl 0 type (NumberTypeE nt);
+ ~FormatGrid() {
+ //@stream.close if @stream
+ //GridFlow.hunt_zombies
+ }
+// \decl void raw_open_gzip_in(string filename);
+// \decl void raw_open_gzip_out(string filename);
+};
+\def 0 bang () {
+ P<Dim> dim;
+ if (feof(f)) {outlet_bang(bself->te_outlet); return;}
+ if (headerless_dim) {
+ dim = headerless_dim;
+ } else {
+ if (fread(&head,1,8,f)<8) RAISE("can't read header");
+ uint8 *m = (uint8 *)head.magic;
+ if (strncmp((char *)m,"\x7fgrid",5)==0) endian=1; else
+ if (strncmp((char *)m,"\x7fGRID",5)==0) endian=0; else
+ RAISE("unknown header, can't read grid from file: "
+ "%02x %02x %02x %02x %02x %02x %02x %02x",
+ m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7]);
+ switch (head.type) {
+ case 8: nt=uint8_e; break; // sorry, was supposed to be signed.
+ case 9: nt=uint8_e; break;
+ case 16: nt=int16_e; break;
+ case 32: nt=int32_e; break;
+ default: RAISE("unsupported grid type %d in file",head.type);
+ }
+ // apparently, head.type 8 and 16 worked too.
+ if (head.reserved!=0) RAISE("unsupported grid reserved field %d in file",head.reserved);
+ if (head.dimn>16) RAISE("unsupported grid number of dimensions %d in file",head.dimn);
+ int32 dimv[head.dimn];
+ ;
+ if (fread(dimv,1,head.dimn*4,f)<size_t(head.dimn*4)) RAISE("can't read dimension list");
+ if (endian != is_le()) swap32(head.dimn,(uint32 *)dimv);
+ dim = new Dim(head.dimn,dimv);
+ }
+ GridOutlet out(this,0,dim,nt);
+ long nn = dim->prod();
+#define FOO(T) {T data[nn]; if (fread(data,1,nn*sizeof(T),f)<nn*sizeof(T)) RAISE("can't read grid data (body)"); out.send(nn,(T *)data);}
+TYPESWITCH(nt,FOO,)
+#undef FOO
+ SUPER;
+}
+
+GRID_INLET(0) {
+ if (!headerless_dim) {
+ strncpy(head.magic,is_le()?"\x7fgrid":"\x7fGRID",5);
+ switch (in->nt) {
+ case uint8_e: head.type = 9; break;
+ case int16_e: head.type = 16; break;
+ case int32_e: head.type = 32; break;
+ default: RAISE("can't write that type of number to a file");
+ }
+ head.reserved = 0;
+ head.dimn = in->dim->n;
+ fwrite(&head,1,8,f);
+ fwrite(in->dim->v,in->dim->n,4,f);
+ }
+} GRID_FLOW {
+#define FOO(T) {T data2[n]; for(int i=0; i<n; i++) data2[i]=(T)data[i]; \
+ if (endian!=is_le()) swap_endian(n,data2); \
+ fwrite(data2,n,sizeof(T),f);}
+TYPESWITCH(in->nt,FOO,)
+#undef FOO
+} GRID_FINISH {
+ fflush(f);
+} GRID_END
+
+\def 0 headerless (...) {
+ if (argc>=0 && argv[0].a_type==A_LIST) {
+ t_binbuf *b = (t_binbuf *)argv[0]; argc = binbuf_getnatom(b); argv = (t_atom2 *)binbuf_getvec(b);}
+ int v[argc];
+ for (int i=0; i<argc; i++) v[i] = argv[i];
+ headerless_dim = new Dim(argc,v);
+}
+\def 0 headerful () { headerless_dim = 0; }
+//#!@#$ method name conflict ?
+\def 0 type (NumberTypeE nt) {
+ //!@#$ bug: should not be able to modify this _during_ a transfer
+ switch (nt) {
+ case uint8_e: head.type= 8; break;
+ case int16_e: head.type=16; break;
+ case int32_e: head.type=32; break;
+ default: RAISE("unsupported type");
+ }
+ this->nt = nt;
+}
+
+//\def void raw_open_gzip_in(string filename) {
+ //r,w = IO.pipe
+ //if (pid=fork) {GridFlow.subprocesses[pid]=true; w.close; @stream = r;}
+ //else {r.close; STDOUT.reopen w; STDIN.reopen filename, "r"; exec "gzip", "-dc";}
+//\def void raw_open_gzip_out(string filename) {
+ //r,w = IO.pipe
+ //if (pid=fork) {GridFlow.subprocesses[pid]=true; r.close; @stream = w;}
+ //else {w.close; STDIN.reopen r; STDOUT.reopen filename, "w"; exec "gzip", "-c";}
+
+\end class FormatGrid {install_format("#io.grid",6,"grid");}
+
+void startup_format () {
+ \startall
+}
diff --git a/externals/gridflow/src/gem.cxx b/externals/gridflow/src/gem.cxx
new file mode 100644
index 00000000..4d1d00c3
--- /dev/null
+++ b/externals/gridflow/src/gem.cxx
@@ -0,0 +1,202 @@
+/*
+ $Id: gem.c 4621 2009-11-01 21:18:17Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <GL/gl.h>
+/* summarising GEM's headers: GemState.h and GemPixUtil.h */
+struct imageStruct {
+ imageStruct(); ~imageStruct();
+ unsigned char* allocate(size_t size); unsigned char* allocate();
+ unsigned char* reallocate(size_t size); unsigned char* reallocate();
+ void clear();
+ GLint xsize, ysize, csize;
+ GLenum type, format;
+ int notowned;
+ void copy2Image(imageStruct *to) const;
+ void copy2ImageStruct(imageStruct *to) const; // copy the imageStruct (but not the actual data)
+ void refreshImage(imageStruct *to);
+ void swapRedBlue ();
+ void convertTo (imageStruct*to, GLenum dest_format=0);
+ void convertFrom(imageStruct*from, GLenum dest_format=0);
+ unsigned char *data;
+ private:
+ unsigned char *pdata;
+ size_t datasize;
+ public:
+ GLboolean upsidedown;
+};
+struct pixBlock {
+ pixBlock();
+ imageStruct image;
+ int newimage, newfilm;
+};
+class TexCoord {
+ public:
+ TexCoord() : s(0.f), t(0.f) {}
+ TexCoord(float s_, float t_) : s(s_), t(t_) {}
+ float s,t;
+};
+class GemState {
+ public:
+ int dirty, inDisplayList, lighting, smooth, texture;
+ pixBlock *image;
+ TexCoord *texCoords;
+ int numTexCoords, multiTexUnits;
+ float tickTime;
+ GLenum drawType;
+ int stackDepth[4];
+ int VertexDirty;
+ GLfloat *VertexArray; int VertexArraySize; int VertexArrayStride;
+ GLfloat *ColorArray; int HaveColorArray;
+ GLfloat *NormalArray; int HaveNormalArray;
+ GLfloat *TexCoordArray; int HaveTexCoordArray;
+ GemState();
+ ~GemState();
+ float texCoordX(int num) const {if (texture && numTexCoords > num) return texCoords[num].s; else return 0.;}
+ float texCoordY(int num) const {if (texture && numTexCoords > num) return texCoords[num].t; else return 0.;}
+ void reset();
+};
+/* end of summary */
+
+// in 0: gem
+// in 1: grid
+// out 0: gem
+\class GridToPix : FObject {
+ P<BitPacking> bit_packing3;
+ P<BitPacking> bit_packing4;
+ pixBlock m_pixBlock;
+ \attr bool yflip;
+ \decl 0 gem_state (...);
+ void render(GemState *state) {state->image = &m_pixBlock;}
+ void startRendering () {m_pixBlock.newimage = 1;}
+ GridToPix (BFObject *bself, MESSAGE) : FObject(bself,MESSAGE2) {
+ yflip = false;
+ imageStruct &im = m_pixBlock.image = imageStruct();
+ im.ysize = 1;
+ im.xsize = 1;
+ im.csize = 4;
+ im.format = GL_RGBA;
+ im.type = GL_UNSIGNED_BYTE;
+ im.allocate();
+ *(int*)im.data = 0x0000ff;
+ uint32 mask[4] = {0x0000ff,0x00ff00,0xff0000,0x000000};
+ bit_packing3 = new BitPacking(is_le(),4,3,mask);
+ bit_packing4 = new BitPacking(is_le(),4,4,mask);
+ }
+ ~GridToPix () {}
+ \grin 1 int
+};
+\def 0 gem_state (...) {
+ if (argc==2) render((GemState *)(void *)argv[1]); else startRendering();
+ outlet_anything(bself->te_outlet,gensym("gem_state"),argc,argv);
+}
+GRID_INLET(1) {
+ if (in->dim->n != 3) RAISE("expecting 3 dimensions: rows,columns,channels");
+ int c = in->dim->get(2);
+ if (c!=3 && c!=4) RAISE("expecting 3 or 4 channels (got %d)",in->dim->get(2));
+ in->set_chunk(1);
+ imageStruct &im = m_pixBlock.image;
+ im.clear();
+ im.ysize = in->dim->get(0);
+ im.xsize = in->dim->get(1);
+ im.type = GL_UNSIGNED_BYTE;
+ switch (in->dim->get(2)) {
+ case 1: im.csize = 1; im.format = GL_LUMINANCE; break;
+ case 3: im.csize = 4; im.format = GL_RGBA; break;
+ case 4: im.csize = 4; im.format = GL_RGBA; break;
+ default: RAISE("you shouldn't see this error message.");
+ }
+ im.allocate();
+} GRID_FLOW {
+ uint8 *buf = (uint8 *)m_pixBlock.image.data;
+ /*!@#$ it would be nice to skip the bitpacking when we can */
+ long sxc = in->dim->prod(1);
+ long sx = in->dim->v[1];
+ long sy = in->dim->v[0];
+ BitPacking *bp = in->dim->get(2)==3 ? bit_packing3 : bit_packing4;
+ imageStruct &im = m_pixBlock.image;
+ if (yflip) {for (long y= dex/sxc; n; data+=sxc, n-=sxc, y++) bp->pack(sx,data,buf+y*sx*im.csize);}
+ else {for (long y=sy-1-dex/sxc; n; data+=sxc, n-=sxc, y--) bp->pack(sx,data,buf+y*sx*im.csize);}
+} GRID_END
+\end class {install("#to_pix",2,1); add_creator("#export_pix");}
+
+//------------------------------------------------------------------------
+
+\class GridFromPix : FObject {
+ P<BitPacking> bit_packing;
+ \attr bool yflip;
+ \attr NumberTypeE cast;
+ int channels;
+ GridFromPix () : FObject(0,0,0,0) {RAISE("don't call this. this exists only to make GEM happy.");}
+ GridFromPix (BFObject *bself, MESSAGE) : FObject(bself,MESSAGE2) {
+ uint32 mask[4] = {0x0000ff,0x00ff00,0xff0000,0x000000};
+ bit_packing = new BitPacking(is_le(),4,3,mask);
+ yflip = false;
+ cast = int32_e;
+ channels = 3;
+ }
+ virtual ~GridFromPix () {}
+ \decl 0 gem_state (...);
+ \decl 0 colorspace (t_symbol *s);
+ void render(GemState *state) {
+ if (!state->image) {::post("gemstate has no pix"); return;}
+ imageStruct &im = state->image->image;
+ if (im.format != GL_RGBA ) {::post("can't produce grid from pix format %d",im.format); return;}
+ if (im.type != GL_UNSIGNED_BYTE) {::post("can't produce grid from pix type %d", im.type ); return;}
+ int32 v[] = { im.ysize, im.xsize, channels };
+ GridOutlet out(this,0,new Dim(3,v),cast);
+ long sxc = im.xsize*channels;
+ long sy = v[0];
+ if (channels==3) {
+ #define FOO(T) {T buf[sxc]; \
+ for (int y=0; y<v[0]; y++) { \
+ uint8 *data = (uint8 *)im.data+im.xsize*im.csize*(yflip?y:sy-1-y); \
+ bit_packing->unpack(im.xsize,data,buf); out.send(sxc,buf);}}
+ TYPESWITCH(cast,FOO,)
+ #undef FOO
+ } else {
+ for (int y=0; y<v[0]; y++) out.send(sxc,(uint8 *)im.data+sxc*(yflip?y:sy-1-y));
+ }
+ }
+};
+\def 0 colorspace (t_symbol *s) {
+ if (s==gensym("rgb" )) channels=3; else
+ if (s==gensym("rgba")) channels=4; else
+ RAISE("unknown colorspace '%s'",s->s_name);
+}
+\def 0 gem_state (...) {if (argc==2) render((GemState *)(void *)argv[1]);}
+\end class {install("#from_pix",2,1); add_creator("#import_pix");}
+
+//------------------------------------------------------------------------
+
+void startup_gem () {
+ \startall
+}
+
+/*
+virtual void processRGBAImage(imageStruct &image) {}
+virtual void processRGBImage (imageStruct &image) {}
+virtual void processGrayImage(imageStruct &image) {}
+virtual void processYUVImage (imageStruct &image) {}
+*/
+
diff --git a/externals/gridflow/src/grid.cxx b/externals/gridflow/src/grid.cxx
new file mode 100644
index 00000000..3d721329
--- /dev/null
+++ b/externals/gridflow/src/grid.cxx
@@ -0,0 +1,295 @@
+/*
+ $Id: grid.c 4391 2009-10-25 16:56:27Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "gridflow.hxx.fcs"
+#include <ctype.h>
+
+//#define TRACEBUFS
+
+#define CHECK_TYPE(d,NT) if (NumberTypeE_type_of(&d)!=NT) RAISE("(%s): " \
+ "type mismatch during transmission (got %s expecting %s)", __PRETTY_FUNCTION__, \
+ number_type_table[NumberTypeE_type_of(&d)].name, number_type_table[NT].name);
+#define CHECK_BUSY1(s) if (!dim) RAISE(#s " not busy");
+#define CHECK_BUSY(s) if (!dim) RAISE(#s " not busy (wanting to write %ld values)",(long)n);
+#define CHECK_ALIGN(d,nt) {int bytes = number_type_table[nt].size/8; int align = ((long)(void*)d)%bytes; \
+ if (align) {post("(%s): Alignment Warning: %p is not %d-aligned: %d", __PRETTY_FUNCTION__, (void*)d,bytes,align);}}
+
+// **************** Grid ******************************************
+
+void Grid::init_from_list(int n, t_atom *aa, NumberTypeE nt) {
+ t_atom2 *a = (t_atom2 *)aa;
+ t_symbol *delim = gensym("#");
+ for (int i=0; i<n; i++) {
+ if (a[i] == delim) {
+ int32 v[i];
+ if (i!=0 && a[i-1].a_type==A_SYMBOL) nt=NumberTypeE_find(a[--i]);
+ for (int j=0; j<i; j++) v[j] = convert(a[j],(int32*)0);
+ init(new Dim(i,v),nt);
+ CHECK_ALIGN(this->data,nt);
+ if (a[i] != delim) i++;
+ i++; a+=i; n-=i;
+ goto fill;
+ }
+ }
+ if (n!=0 && a[0].a_type==A_SYMBOL) {nt = NumberTypeE_find(a[0]); a++; n--;}
+ init(new Dim(n),nt);
+ CHECK_ALIGN(this->data,nt);
+ fill:
+ int nn = dim->prod();
+ n = min(n,nn);
+#define FOO(T) { \
+ T *p = (T *)*this; \
+ if (n==0) CLEAR(p,nn); else { \
+ for (int i=0; i<n; i++) p[i] = a[i]; \
+ for (int i=n; i<nn; i+=n) COPY(p+i,p,min(n,nn-i)); }}
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+}
+
+void Grid::init_from_atom(const t_atom &x) {
+ const t_atom2 &a = *(t_atom2 *)&x;
+ if (a.a_type==A_LIST) {
+ t_binbuf *b = a;
+ init_from_list(binbuf_getnatom(b),binbuf_getvec(b));
+ } else if (x.a_type==A_FLOAT) {
+ init(new Dim(),int32_e);
+ CHECK_ALIGN(this->data,nt);
+ ((int32 *)*this)[0] = (int32)a.a_float;
+ } else {
+ std::ostringstream s; s << x;
+ RAISE("can't convert to grid: %s",s.str().data());
+ }
+}
+
+// **************** GridInlet *************************************
+
+// must be set before the end of GRID_BEGIN phase, and so cannot be changed
+// afterwards. This is to allow some optimisations. Anyway there is no good reason
+// why this would be changed afterwards.
+void GridInlet::set_chunk(long whichdim) {
+ chunk = whichdim;
+ long n = dim->prod(whichdim);
+ if (!n) n=1;
+ if(!dim) RAISE("huh?");
+ if (n>1) {
+ buf=new Grid(new Dim(n), sender->nt);
+ bufi=0;
+ } else buf=0;
+}
+
+bool GridInlet::supports_type(NumberTypeE nt) {
+#define FOO(T) return !! gh->flow_##T;
+ TYPESWITCH(nt,FOO,return false)
+#undef FOO
+}
+
+void GridInlet::begin(GridOutlet *sender) {
+ if (dim) RAISE("grid inlet aborting from %s at %ld/%ld because of %s",
+ ARGS(this->sender->parent),long(dex),long(dim->prod()),ARGS(sender->parent));
+ this->sender = sender;
+ if (!supports_type(sender->nt)) RAISE("number type %s not supported here", number_type_table[sender->nt].name);
+ this->nt = sender->nt;
+ this->dim = sender->dim;
+ dex=0;
+ buf=0;
+ try {
+#define FOO(T) gh->flow(this,dex,-1,(T *)0); break;
+ TYPESWITCH(sender->nt,FOO,)
+#undef FOO
+ } catch (Barf &barf) {this->dim=0; throw;}
+ this->dim = dim;
+ sender->callback(this);
+#ifdef TRACEBUFS
+ post("GridInlet: %20s buf for recving from %p",dim->to_s(),sender);
+#endif
+}
+
+#define CATCH_IT catch (Barf &slimy) {slimy.error(parent->bself);}
+
+template <class T> void GridInlet::flow(long n, T *data) {
+ CHECK_BUSY(inlet); CHECK_TYPE(*data,sender->nt); CHECK_ALIGN(data,sender->nt);
+ if (!n) return; // no data
+ long d = dex + bufi;
+ if (d+n > dim->prod()) {
+ post("grid input overflow: %ld of %ld from [%s] to [%s]", d+n, long(dim->prod()), ARGS(sender->parent), ARGS(parent));
+ n = dim->prod() - d;
+ if (n<=0) return;
+ }
+ int bufn = factor();
+ if (buf && bufi) {
+ T *bufd = *buf;
+ long k = min((long)n,bufn-bufi);
+ COPY(bufd+bufi,data,k);
+ bufi+=k; data+=k; n-=k;
+ if (bufi==bufn) {
+ long newdex = dex+bufn;
+ CHECK_ALIGN(bufd,sender->nt);
+ try {gh->flow(this,dex,bufn,bufd);} CATCH_IT;
+ dex = newdex;
+ bufi = 0;
+ }
+ }
+ int m = (n/bufn)*bufn;
+ if (m) {
+ int newdex = dex + m;
+ try {gh->flow(this,dex,m,data);} CATCH_IT;
+ dex = newdex;
+ }
+ data += m;
+ n -= m;
+ if (buf && n>0) COPY((T *)*buf+bufi,data,n), bufi+=n;
+}
+
+void GridInlet::finish() {
+ if (!dim) RAISE("inlet not busy");
+ if (dim->prod() != dex) post("%s: incomplete grid: %ld of %ld from [%s] to [%s]",
+ ARGS(parent),dex,long(dim->prod()),ARGS(sender->parent),ARGS(parent));
+#define FOO(T) try {gh->flow(this,dex,-2,(T *)0);} CATCH_IT;
+ TYPESWITCH(sender->nt,FOO,)
+#undef FOO
+ dim=0; buf=0; dex=0;
+}
+
+template <class T> void GridInlet::from_grid2(Grid *g, T foo) {
+ GridOutlet out(0,-1,g->dim,g->nt);
+ begin(&out);
+ size_t n = g->dim->prod();
+ if (n) out.send(n,(T *)*g); else finish();
+}
+
+void GridInlet::from_grid(Grid *g) {
+ if (!supports_type(g->nt)) RAISE("number type %s not supported here",number_type_table[g->nt].name);
+#define FOO(T) from_grid2(g,(T)0);
+ TYPESWITCH(g->nt,FOO,)
+#undef FOO
+}
+
+/* **************** GridOutlet ************************************ */
+
+GridOutlet::GridOutlet(FObject *parent_, int woutlet, P<Dim> dim_, NumberTypeE nt_) {
+ parent=parent_; dim=dim_; nt=nt_; dex=0; bufi=0; buf=0;
+ t_atom a[1];
+ SETGRIDOUT(a,this);
+ if (parent) {
+ outlet_anything(parent->bself->outlets[woutlet],bsym._grid,1,a);
+ if (!dim->prod()) finish();
+ }
+}
+
+void GridOutlet::create_buf () {
+ int32 lcm_factor = 1;
+ for (uint32 i=0; i<inlets.size(); i++) lcm_factor = lcm(lcm_factor,inlets[i]->factor());
+ //size_t ntsz = number_type_table[nt].size;
+ // biggest packet size divisible by lcm_factor
+ int32 v = (MAX_PACKET_SIZE/lcm_factor)*lcm_factor;
+ if (v==0) v=MAX_PACKET_SIZE; // factor too big. don't have a choice.
+ buf=new Grid(new Dim(v),nt);
+#ifdef TRACEBUFS
+ std::ostringstream text;
+ oprintf(text,"GridOutlet: %20s buf for sending to ",buf->dim->to_s());
+ for (uint i=0; i<inlets.size(); i++) text << " " << (void *)inlets[i]->parent;
+ post("%s",text.str().data());
+#endif
+}
+
+// send modifies dex; send_direct doesn't
+template <class T>
+void GridOutlet::send_direct(long n, T *data) {
+ CHECK_BUSY(outlet); CHECK_TYPE(*data,nt); CHECK_ALIGN(data,nt);
+ while (n>0) {
+ long pn = n;//min((long)n,MAX_PACKET_SIZE);
+ for (uint32 i=0; i<inlets.size(); i++) try {inlets[i]->flow(pn,data);} CATCH_IT;
+ data+=pn, n-=pn;
+ }
+}
+
+void GridOutlet::flush() {
+ if (!buf) return;
+ if (!bufi) return;
+#define FOO(T) send_direct(bufi,(T *)*buf);
+ TYPESWITCH(buf->nt,FOO,)
+#undef FOO
+ bufi = 0;
+}
+
+template <class T, class S>
+static void convert_number_type(int n, T *out, S *in) {for (int i=0; i<n; i++) out[i]=(T)in[i];}
+
+//!@#$ buffering in outlet still is 8x faster...?
+//!@#$ should use BitPacking for conversion...?
+// send modifies dex; send_direct doesn't
+template <class T>
+void GridOutlet::send_2(long n, T *data) {
+ //if (inlets.size()==1 && inlets[0]->buf) post("GridOutlet::send(%ld), bufsize %ld",long(n),long(inlets[0]->buf->dim->prod()));
+ if (!n) return;
+ CHECK_BUSY(outlet); CHECK_ALIGN(data,nt);
+ if (NumberTypeE_type_of(data)!=nt) {
+ int bs = MAX_PACKET_SIZE;
+#define FOO(T) {T data2[bs]; \
+ for (;n>=bs;n-=bs,data+=bs) {convert_number_type(bs,data2,data); send(bs,data2);} \
+ convert_number_type(n,data2,data); send(n,data2);}
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+ } else {
+ dex += n;
+ if (n > MIN_PACKET_SIZE || bufi + n > MAX_PACKET_SIZE) flush();
+ if (n > MIN_PACKET_SIZE) {
+ //post("send_direct %d",n);
+ send_direct(n,data);
+ } else {
+ //post("send_indirect %d",n);
+ if (!buf) create_buf();
+ COPY((T *)*buf+bufi,data,n);
+ bufi += n;
+ }
+ if (dex==dim->prod()) finish();
+ }
+}
+
+void GridOutlet::callback(GridInlet *in) {
+ CHECK_BUSY1(outlet);
+ inlets.push_back(in);
+}
+
+void GridOutlet::finish () {
+ flush();
+ for (uint32 i=0; i<inlets.size(); i++) inlets[i]->finish();
+ dim=0;
+}
+
+// never call this. this is a hack to make some things work.
+// i'm trying to circumvent either a bug in the compiler or i don't have a clue. :-(
+void make_gimmick () {
+ GridOutlet foo(0,0,0);
+#define FOO(S) foo.send(0,(S *)0);
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ //foo.send(0,(float64 *)0); // this doesn't work, when trying to fix the new link problem in --lite mode.
+}
diff --git a/externals/gridflow/src/gridflow.cxx b/externals/gridflow/src/gridflow.cxx
new file mode 100644
index 00000000..d30c8f4a
--- /dev/null
+++ b/externals/gridflow/src/gridflow.cxx
@@ -0,0 +1,961 @@
+/*
+ $Id: rubyext.c 3621 2008-04-19 01:47:38Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <stdlib.h>
+//#include <cstdlib>
+#include <sys/stat.h>
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#ifndef HAVE_DESIREDATA
+#include "bundled/g_canvas.h"
+#endif
+
+/* for exception-handling in 0.9.0... Linux-only */
+#ifndef MACOSX
+#include <exception>
+#include <execinfo.h>
+#endif
+#undef check
+
+std::map<string,FClass *> fclasses;
+std::map<t_class *,FClass *> fclasses_pd;
+
+//using namespace std;
+
+BuiltinSymbols bsym;
+
+Barf::Barf(const char *s, ...) {
+ std::ostringstream os;
+ va_list ap;
+ va_start(ap,s);
+ voprintf(os,s,ap);
+ va_end(ap);
+ text = os.str();
+}
+Barf::Barf(const char *file, int line, const char *func, const char *fmt, ...) {
+ std::ostringstream os;
+ va_list ap;
+ va_start(ap,fmt);
+ voprintf(os,fmt,ap);
+ //oprintf(os,"\n%s:%d:in `%s'",file,line,func);
+ va_end(ap);
+ text = os.str();
+}
+
+void Barf::error(BFObject *bself) {
+ if (bself) pd_error(bself,"%s: %s",bself->binbuf_string().data(),text.data());
+ else ::error( "%s: %s",bself->binbuf_string().data(),text.data());
+}
+
+void pd_oprint (std::ostream &o, int argc, t_atom *argv) {
+ for (int i=0; i<argc; i++) {
+ t_atomtype t = argv[i].a_type;
+ if (t==A_FLOAT) o << argv[i].a_float;
+ else if (t==A_SYMBOL) o << argv[i].a_symbol->s_name;
+ else if (t==A_POINTER) o << "(pointer)";
+ else if (t==A_COMMA) o << ",";
+ else if (t==A_SEMI) o << ";";
+ else if (t==A_LIST) {
+ t_binbuf *b = (t_binbuf *)argv[i].a_gpointer;
+ o << "[";
+ pd_oprint(o,binbuf_getnatom(b),binbuf_getvec(b));
+ o << "]";
+ } else o << "(atom of type " << t << ")";
+ if (i!=argc-1) o << " ";
+ }
+}
+
+void pd_post (const char *s, int argc, t_atom *argv) {
+ std::ostringstream os;
+ if (s) os << s << ": ";
+ pd_oprint(os,argc,argv);
+ post("%s",os.str().data());
+}
+
+void pd_oprintf (std::ostream &o, const char *s, int argc, t_atom *argv) {
+ int i=0;
+ for (; *s; s++) {
+ if (*s!='%') {o << (char)*s; continue;}
+ s++; // skip the %
+ switch (*s) {
+ case 'f':
+ if (!argc) RAISE("not enough args");
+ if (argv[i].a_type != A_FLOAT) RAISE("expected float");
+ o << argv[i++].a_float;
+ break;
+ case 's':
+ if (!argc) RAISE("not enough args");
+ if (argv[i].a_type != A_SYMBOL) RAISE("expected symbol");
+ o << argv[i++].a_symbol->s_name;
+ break;
+ case '_':
+ if (!argc) RAISE("not enough args");
+ char buf[MAXPDSTRING];
+ atom_string(&argv[i++],buf,MAXPDSTRING);
+ o << buf;
+ break;
+ case '%':
+ o << "%";
+ break;
+ default:
+ RAISE("sorry, the format character '%c' is not supported yet",*s);
+ }
+ }
+}
+
+//----------------------------------------------------------------
+// Dim
+
+void Dim::check() {
+ if (n>MAX_DIM) RAISE("too many dimensions");
+ for (int i=0; i<n; i++) if (v[i]<0) RAISE("Dim: negative dimension");
+}
+
+// !@#$ big leak machine?
+// returns a string like "Dim[240,320,3]"
+char *Dim::to_s() {
+ // if you blow 256 chars it's your own fault
+ char buf[256];
+ char *s = buf;
+ s += sprintf(s,"Dim[");
+ for(int i=0; i<n; i++) s += sprintf(s,"%s%d", ","+!i, v[i]);
+ s += sprintf(s,"]");
+ return strdup(buf);
+}
+
+NumberTypeE NumberTypeE_find (string s) {
+ if (number_type_dict.find(s)==number_type_dict.end()) RAISE("unknown number type \"%s\"", s.data());
+ return number_type_dict[s]->index;
+}
+
+NumberTypeE NumberTypeE_find (const t_atom &x) {
+ if (x.a_type!=A_SYMBOL) RAISE("expected number-type (as symbol)");
+ return NumberTypeE_find(string(x.a_symbol->s_name));
+}
+
+// don't touch.
+static void gfmemcopy32(int32 *as, int32 *bs, long n) {
+ ptrdiff_t ba = bs-as;
+#define FOO(I) as[I] = (as+ba)[I];
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+void gfmemcopy(uint8 *out, const uint8 *in, long n) {
+ for (; n>16; in+=16, out+=16, n-=16) {
+ ((int32*)out)[0] = ((int32*)in)[0];
+ ((int32*)out)[1] = ((int32*)in)[1];
+ ((int32*)out)[2] = ((int32*)in)[2];
+ ((int32*)out)[3] = ((int32*)in)[3];
+ }
+ for (; n>4; in+=4, out+=4, n-=4) *(int32*)out = *(int32*)in;
+ for (; n; in++, out++, n--) *out=*in;
+}
+
+//----------------------------------------------------------------
+
+uint64 gf_timeofday () {
+ timeval t;
+ gettimeofday(&t,0);
+ return t.tv_sec*1000000+t.tv_usec;
+}
+
+#define CONVERT0(z) ((in[z] >> chop[z]) << slide[z])
+#define CONVERT1 t = CONVERT0(0) | CONVERT0(1) | CONVERT0(2)
+#define CONVERT2 for (t=0,i=0; i<self->size; i++) t |= CONVERT0(i);
+
+#define WRITE_LE \
+ for (int bytes = self->bytes; bytes; bytes--, t>>=8) *out++ = t;
+
+#define WRITE_BE {int bytes; \
+ bytes = self->bytes; \
+ while (bytes--) {out[bytes] = t; t>>=8;}\
+ out += self->bytes;}
+
+/* this macro would be faster if the _increment_
+ was done only once every loop. or maybe gcc does it, i dunno */
+#define NTIMES(_x_) \
+ for (; n>=4; n-=4) {_x_ _x_ _x_ _x_} \
+ for (; n; n--) {_x_}
+
+/* this could be faster (use asm) */
+void swap64 (long n, uint64 *data) {
+ NTIMES({
+ uint64 x = *data;
+ x = (x<<32) | (x>>32);
+ x = ((x&0x0000ffff0000ffffLL)<<16) | ((x>>16)&0x0000ffff0000ffffLL);
+ x = ((x&0x00ff00ff00ff00ffLL)<< 8) | ((x>> 8)&0x00ff00ff00ff00ffLL);
+ *data++ = x;
+ })
+}
+
+/* this could be faster (use asm) */
+void swap32 (long n, uint32 *data) {
+ NTIMES({
+ uint32 x = *data;
+ x = (x<<16) | (x>>16);
+ x = ((x&0xff00ff)<<8) | ((x>>8)&0xff00ff);
+ *data++ = x;
+ })
+}
+
+/* this could be faster (use asm or do it in int32 chunks) */
+void swap16 (long n, uint16 *data) {NTIMES({ uint16 x = *data; *data++ = (x<<8) | (x>>8); })}
+
+/* **************************************************************** */
+
+//#define DEBUG 1
+#ifdef DEBUG
+#define TRACE static int use=0; use++; if ((use%10000)==0) post("%s",__PRETTY_FUNCTION__);
+#else
+#define TRACE
+#endif
+
+template <class T>
+static void default_pack(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ uint32 t;
+ int i;
+ int sameorder = self->endian==2 || self->endian==::is_le();
+ int size = self->size;
+ uint32 mask[4]; memcpy(mask,self->mask,size*sizeof(uint32));
+ uint32 hb[4]; for (i=0; i<size; i++) hb[i] = highest_bit(mask[i]);
+ uint32 span[4]; for (i=0; i<size; i++) span[i] = hb[i] - lowest_bit(mask[i]);
+ uint32 chop[4]; for (i=0; i<size; i++) chop[i] = 7-span[i];
+ uint32 slide[4]; for (i=0; i<size; i++) slide[i] = hb[i]-span[i];
+
+ if (sameorder && size==3) {
+ switch(self->bytes) {
+ case 2: NTIMES(CONVERT1; *((int16 *)out)=t; out+=2; in+=3;) return;
+ case 4: NTIMES(CONVERT1; *((int32 *)out)=t; out+=4; in+=3;) return;
+ }
+ }
+ if (self->is_le()) {
+ switch (size) {
+ case 3: for (; n--; in+=3) {CONVERT1; WRITE_LE;} break;
+ case 4: for (; n--; in+=4) {CONVERT1; WRITE_LE;} break;
+ default:for (; n--; in+=size) {CONVERT2; WRITE_LE;}}
+ } else {
+ switch (size) {
+ case 3: for (; n--; in+=3) {CONVERT1; WRITE_BE;} break;
+ case 4: for (; n--; in+=4) {CONVERT1; WRITE_BE;} break;
+ default:for (; n--; in+=size) {CONVERT2; WRITE_BE;}}
+ }
+}
+
+#define LOOP_UNPACK(_reader_) \
+ for (; n; n--) { \
+ int bytes=0; uint32 temp=0; _reader_; \
+ for (int i=0; i<self->size; i++, out++) { \
+ uint32 t=temp&self->mask[i]; \
+ *out = (t<<(7-hb[i]))|(t>>(hb[i]-7));}}
+
+template <class T>
+static void default_unpack(BitPacking *self, long n, uint8 *in, T *out) {TRACE
+ int hb[4];
+ for (int i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]);
+ if (is_le()) { // smallest byte first
+ LOOP_UNPACK(
+ for(; self->bytes>bytes; bytes++, in++) temp |= *in<<(8*bytes);
+ )
+ } else { // biggest byte first
+ LOOP_UNPACK(
+ bytes=self->bytes; for (; bytes; bytes--, in++) temp=(temp<<8)|*in;
+ )
+ }
+}
+
+/* **************************************************************** */
+
+template <class T>
+static void pack2_565(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ uint32 chop[3] = {3,2,3};
+ uint32 slide[3] = {11,5,0};
+ uint32 t;
+ NTIMES(CONVERT1; *((short *)out)=t; out+=2; in+=3;)
+}
+
+template <class T>
+static void pack3_888(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ int32 *o32 = (int32 *)out;
+ while (n>=4) {
+ o32[0] = (in[5]<<24) | (in[ 0]<<16) | (in[ 1]<<8) | in[2];
+ o32[1] = (in[7]<<24) | (in[ 8]<<16) | (in[ 3]<<8) | in[4];
+ o32[2] = (in[9]<<24) | (in[10]<<16) | (in[11]<<8) | in[6];
+ o32+=3; in+=12;
+ n-=4;
+ }
+ out = (uint8 *)o32;
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+/*
+template <>
+static void pack3_888(BitPacking *self, long n, uint8 *in, uint8 *out) {TRACE
+ uint32 *o32 = uint32 *((uint32 *)out.p,n*3/4);
+ uint32 *i32 = uint32 *((uint32 *)in.p,n*3/4);
+ while (n>=4) {
+#define Z(w,i) ((word##w>>(i*8))&255)
+ uint32 word0 = i32[0];
+ uint32 word1 = i32[1];
+ uint32 word2 = i32[2];
+ o32[0] = (Z(1,1)<<24) | (Z(0,0)<<16) | (Z(0,1)<<8) | Z(0,2);
+ o32[1] = (Z(1,3)<<24) | (Z(2,0)<<16) | (Z(0,3)<<8) | Z(1,0);
+ o32[2] = (Z(2,1)<<24) | (Z(2,2)<<16) | (Z(2,3)<<8) | Z(1,2);
+ o32+=3; i32+=3;
+ n-=4;
+ }
+#undef Z
+ out = (uint8 *)o32;
+ in = (uint8 *)i32;
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+*/
+
+template <class T> static void unpack3_888 (BitPacking *self, long n, uint8 *in, T *out) {TRACE
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+template <class T> static void pack3_888c(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out[3]=0; out+=4; in+=3; )
+}
+template <class T> static void pack3_888d(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ NTIMES( out[0]=in[0]; out[1]=in[1]; out[2]=in[2]; out[3]=0; out+=4; in+=3; )
+}
+template <class T> static void unpack3_888d(BitPacking *self, long n, uint8 *in, T *out) {TRACE
+ NTIMES( out[0]=in[0]; out[1]=in[1]; out[2]=in[2]; out+=3; in+=4; )
+}
+template <class T> static void pack3_bgrn8888b(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out[3]=0; out+=4; in+=4; )
+}
+
+template <class T>
+static void pack3_888b(BitPacking *self, long n, T *in, uint8 *out) {TRACE
+ int32 *o32 = (int32 *)out;
+ while (n>=4) {
+ o32[0] = (in[0]<<16) | (in [1]<<8) | in [2];
+ o32[1] = (in[3]<<16) | (in [4]<<8) | in [5];
+ o32[2] = (in[6]<<16) | (in [7]<<8) | in [8];
+ o32[3] = (in[9]<<16) | (in[10]<<8) | in[11];
+ o32+=4; in+=12;
+ n-=4;
+ }
+ NTIMES( o32[0] = (in[0]<<16) | (in[1]<<8) | in[2]; o32++; in+=3; )
+}
+
+// (R,G,B,?) -> B:8,G:8,R:8,0:8
+static void pack3_bgrn8888(BitPacking *self, long n, uint8 *in, uint8 *out) {TRACE
+/* NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=4; in+=4; ) */
+ int32 *i32 = (int32 *)in;
+ int32 *o32 = (int32 *)out;
+ while (n>=4) {
+ o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff);
+ o32[1] = ((i32[1]&0xff)<<16) | (i32[1]&0xff00) | ((i32[1]>>16)&0xff);
+ o32[2] = ((i32[2]&0xff)<<16) | (i32[2]&0xff00) | ((i32[2]>>16)&0xff);
+ o32[3] = ((i32[3]&0xff)<<16) | (i32[3]&0xff00) | ((i32[3]>>16)&0xff);
+ o32+=4; i32+=4; n-=4;
+ }
+ NTIMES( o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff); o32++; i32++; )
+}
+
+static uint32 bp_masks[][4] = {
+ {0x0000f800,0x000007e0,0x0000001f,0},
+ {0x00ff0000,0x0000ff00,0x000000ff,0},
+ {0x000000ff,0x0000ff00,0x00ff0000,0},
+};
+
+#define ANYCASE(a) {a,a,a}
+static Packer bp_packers[] = {
+ ANYCASE(default_pack),
+ ANYCASE(pack2_565),
+ ANYCASE(pack3_888),
+ {pack3_888b, default_pack, default_pack}, /* {pack3_888c, pack3_888c, pack3_888c}, not tested */
+ {pack3_bgrn8888, pack3_bgrn8888b, pack3_bgrn8888b},
+ ANYCASE(pack3_888d),
+};
+
+static Unpacker bp_unpackers[] = {
+ ANYCASE(default_unpack),
+ ANYCASE(unpack3_888),
+ {pack3_bgrn8888, default_unpack, default_unpack},
+ ANYCASE(unpack3_888d),
+};
+
+static BitPacking builtin_bitpackers[] = {
+ BitPacking(2, 2, 3, bp_masks[0], &bp_packers[1], &bp_unpackers[0]),
+ BitPacking(1, 3, 3, bp_masks[1], &bp_packers[2], &bp_unpackers[1]),
+ BitPacking(1, 4, 3, bp_masks[1], &bp_packers[3], &bp_unpackers[0]),
+ BitPacking(1, 4, 4, bp_masks[1], &bp_packers[4], &bp_unpackers[2]),
+ BitPacking(1, 4, 3, bp_masks[2], &bp_packers[5], &bp_unpackers[3]),
+};
+
+/* **************************************************************** */
+
+bool BitPacking::eq(BitPacking *o) {
+ if (!(bytes == o->bytes)) return false;
+ if (!(size == o->size)) return false;
+ for (int i=0; i<size; i++) {
+ if (!(mask[i] == o->mask[i])) return false;
+ }
+ if (endian==o->endian) return true;
+ /* same==little on a little-endian; same==big on a big-endian */
+ return (endian ^ o->endian ^ ::is_le()) == 2;
+}
+
+void post_BitPacking(BitPacking *b) {
+ ::post("Bitpacking: endian=%d bytes=%d size=%d packer=%d unpacker=%d",
+ b->endian,b->bytes,b->size,b->packer-bp_packers,b->unpacker-bp_unpackers);
+ ::post(" mask=[0x%08x,0x%08x,0x%08x,0x%08x]",b->mask[0],b->mask[1],b->mask[2],b->mask[3]);
+}
+
+BitPacking::BitPacking(int endian, int bytes, int size, uint32 *mask, Packer *packer, Unpacker *unpacker) {
+ this->endian = endian;
+ this->bytes = bytes;
+ this->size = size;
+ for (int i=0; i<size; i++) this->mask[i] = mask[i];
+ if (packer) {
+ this->packer = packer;
+ this->unpacker = unpacker;
+ return;
+ }
+ int packeri=-1;
+ this->packer = &bp_packers[0];
+ this->unpacker = &bp_unpackers[0];
+ for (int i=0; i<(int)(sizeof(builtin_bitpackers)/sizeof(BitPacking)); i++) {
+ BitPacking *bp = &builtin_bitpackers[i];
+ if (this->eq(bp)) {
+ this-> packer = bp-> packer;
+ this->unpacker = bp->unpacker;
+ packeri=i;
+ goto end;
+ }
+ }
+end:;
+}
+
+bool BitPacking::is_le() {return endian==1 || (endian ^ ::is_le())==3;}
+
+#undef TRACE
+#ifdef DEBUG
+#define TRACE static int use=0; use++; if ((use%10000)==0) post_BitPacking(this);
+#else
+#define TRACE
+#endif
+template <class T> void BitPacking:: pack(long n, T *in, uint8 *out) {TRACE
+ switch (NumberTypeE_type_of(in)) {
+ case uint8_e: packer->as_uint8(this,n,(uint8 *)in,out); break;
+ case int16_e: packer->as_int16(this,n,(int16 *)in,out); break;
+ case int32_e: packer->as_int32(this,n,(int32 *)in,out); break;
+ default: RAISE("argh");}}
+template <class T> void BitPacking::unpack(long n, uint8 *in, T *out) {TRACE
+ switch (NumberTypeE_type_of(out)) {
+ case uint8_e: unpacker->as_uint8(this,n,in,(uint8 *)out); break;
+ case int16_e: unpacker->as_int16(this,n,in,(int16 *)out); break;
+ case int32_e: unpacker->as_int32(this,n,in,(int32 *)out); break;
+ default: RAISE("argh");}}
+
+// i'm sorry... see the end of grid.c for an explanation...
+//static
+void make_hocus_pocus () {
+// exit(1);
+#define FOO(S) \
+ ((BitPacking*)0)-> pack(0,(S *)0,(uint8 *)0); \
+ ((BitPacking*)0)->unpack(0,(uint8 *)0,(S *)0);
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+}
+
+std::vector<string> gf_data_path;
+string gf_find_file (string x) {
+ if (strchr(x.data(),'/')) return x;
+ int n = gf_data_path.size();
+ struct stat dummy;
+ for (int i=0; i<n; i++) {
+ string s = gf_data_path[i]+"/"+x;
+ if (lstat(s.data(),&dummy)==0) return s;
+ }
+ return x;
+}
+
+/* **************************************************************** */
+
+#undef pd_class
+#define pd_class(x) (*(t_pd *)x)
+#define pd_classname(x) (fclasses_pd[pd_class(x)]->name.data())
+
+static FMethod funcall_lookup (FClass *fclass, const char *sel) {
+ int n = fclass->methodsn;
+ for (int i=0; i<n; i++) if (strcmp(fclass->methods[i].selector,sel)==0) return fclass->methods[i].method;
+ if (fclass->super) return funcall_lookup(fclass->super,sel);
+ return 0;
+}
+static FMethod funcall_lookup (BFObject *bself, const char *sel) {
+ return funcall_lookup(fclasses_pd[pd_class(bself)],sel);
+}
+
+void call_super(int argc, t_atom *argv) {/* unimplemented */}
+
+//****************************************************************
+// BFObject
+
+struct BFProxy : t_object {
+ BFObject *parent;
+ t_inlet *inlet;
+ int id;
+};
+
+static t_class *BFProxy_class;
+
+static void BFObject_loadbang (BFObject *bself) {
+ FMethod m = funcall_lookup(bself,"_0_loadbang");
+ m(bself->self,0,0);
+}
+
+static void BFObject_anything (BFObject *bself, int winlet, t_symbol *selector, int ac, t_atom2 *at) {
+ try {
+ t_atom2 argv[ac+1];
+ for (int i=0; i<ac; i++) argv[i+1] = at[i];
+ int argc = handle_braces(ac,argv+1);
+ SETFLOAT(argv+0,winlet);
+ char buf[256];
+ sprintf(buf,"_n_%s",selector->s_name);
+ FMethod m;
+ m = funcall_lookup(bself,buf);
+ if (m) {m(bself->self,argc+1,argv); return;}
+ sprintf(buf,"_%d_%s",winlet,selector->s_name);
+ m = funcall_lookup(bself,buf);
+ if (m) {m(bself->self,argc,argv+1); return;}
+ m = funcall_lookup(bself,"anything");
+ if (m) {SETSYMBOL(argv+0,gensym(buf)); m(bself->self,argc+1,argv); return;}
+ pd_error((t_pd *)bself, "method '%s' not found for inlet %d in class '%s'",selector->s_name,winlet,pd_classname(bself));
+ } catch (Barf &oozy) {oozy.error(bself);}
+}
+static void BFObject_anything0 (BFObject *self, t_symbol *s, int argc, t_atom2 *argv) {
+ BFObject_anything(self,0,s,argc,argv);
+}
+static void BFProxy_anything (BFProxy *self, t_symbol *s, int argc, t_atom2 *argv) {
+ BFObject_anything(self->parent,self->id,s,argc,argv);
+}
+
+static void *BFObject_new (t_symbol *classsym, int ac, t_atom *at) {
+ string name = string(classsym->s_name);
+ if (fclasses.find(name)==fclasses.end()) {post("GF: class not found: '%s'",classsym->s_name); return 0;}
+ t_class *qlass = fclasses[name]->bfclass;
+ BFObject *bself = (BFObject *)pd_new(qlass);
+ try {
+ int argc = ac;
+ t_atom argv[argc];
+ for (int i=0; i<argc; i++) argv[i] = at[i];
+ argc = handle_braces(argc,argv);
+ //pd_post(classsym->s_name,argc,argv);
+ int j;
+ for (j=0; j<argc; j++) if (argv[j].a_type==A_COMMA) break;
+
+ bself->self = 0;
+ bself->mom = (t_canvas *)canvas_getcurrent();
+ bself->ninlets = 1;
+ bself->noutlets = 0;
+ bself->inlets = new BFProxy*[1];
+ bself->outlets = new t_outlet*[1];
+ bself->inlets[0] = 0; // inlet 0 of this table is not in use
+ bself->ninlets_set( fclasses[classsym->s_name]->ninlets ,false);
+ bself->noutlets_set(fclasses[classsym->s_name]->noutlets,false);
+ t_allocator alloc = fclasses[string(classsym->s_name)]->allocator;
+ bself->self = alloc(bself,0,j,(t_atom2 *)argv);
+ while (j<argc) {
+ j++;
+ int k=j;
+ for (; j<argc; j++) if (argv[j].a_type==A_COMMA) break;
+ if (argv[k].a_type==A_SYMBOL) pd_typedmess((t_pd *)bself,argv[k].a_symbol,j-k-1,argv+k+1);
+ }
+ return bself;
+ } catch (Barf &oozy) {oozy.error(bself); return 0;}
+}
+
+static void BFObject_delete (BFObject *bself) {
+ try {delete bself->self;} catch (Barf &oozy) {oozy.error(bself);}
+ bself->ninlets_set(1,false);
+ delete[] bself->inlets;
+ delete[] bself->outlets;
+}
+
+//****************************************************************
+
+static void BFObject_undrawio (BFObject *bself) {
+#ifndef HAVE_DESIREDATA
+ if (!bself->mom || !glist_isvisible(bself->mom)) return;
+ t_rtext *rt = glist_findrtext(bself->mom,bself);
+ if (!rt) return;
+ glist_eraseiofor(bself->mom,bself,rtext_gettag(rt));
+#endif
+}
+
+static void BFObject_redraw (BFObject *bself) {
+#ifndef HAVE_DESIREDATA
+ if (!bself->mom || !glist_isvisible(bself->mom)) return;
+ t_rtext *rt = glist_findrtext(bself->mom,bself);
+ if (!rt) return;
+ gobj_vis((t_gobj *)bself,bself->mom,0);
+ gobj_vis((t_gobj *)bself,bself->mom,1);
+ canvas_fixlinesfor(bself->mom,(t_text *)bself);
+#endif
+}
+
+/* warning: deleting inlets that are connected will cause pd to crash */
+void BFObject::ninlets_set (int n, bool draw) {
+ if (!te_binbuf) draw=false;
+ if (n<1) RAISE("ninlets_set: n=%d must be at least 1",n);
+ if (draw) BFObject_undrawio(this);
+ if (ninlets<n) {
+ BFProxy **noo = new BFProxy*[n];
+ memcpy(noo,inlets,ninlets*sizeof(BFProxy*));
+ delete[] inlets;
+ inlets = noo;
+ while (ninlets<n) {
+ BFProxy *p = inlets[ninlets] = (BFProxy *)pd_new(BFProxy_class);
+ p->parent = this;
+ p->id = ninlets;
+ p->inlet = inlet_new(this, &p->ob_pd, 0,0);
+ ninlets++;
+ }
+ } else {
+ while (ninlets>n) {
+ ninlets--;
+ inlet_free(inlets[ninlets]->inlet);
+ pd_free((t_pd *)inlets[ninlets]);
+ }
+ }
+ if (draw) BFObject_redraw(this);
+}
+/* warning: deleting outlets that are connected will cause pd to crash */
+void BFObject::noutlets_set (int n, bool draw) {
+ if (!te_binbuf) draw=false;
+ if (n<0) RAISE("noutlets_set: n=%d must be at least 0",n);
+ if (draw) BFObject_undrawio(this);
+ if (noutlets<n) {
+ t_outlet **noo = new t_outlet*[n>0?n:1];
+ memcpy(noo,outlets,noutlets*sizeof(t_outlet*));
+ delete[] outlets;
+ outlets = noo;
+ while (noutlets<n) outlets[noutlets++] = outlet_new(this,&s_anything);
+ } else {
+ while (noutlets>n) outlet_free(outlets[--noutlets]);
+ }
+ if (draw) BFObject_redraw(this);
+}
+
+string BFObject::binbuf_string () {
+ if (!te_binbuf) return "[???]";
+ std::ostringstream s;
+ int n = binbuf_getnatom(te_binbuf);
+ t_atom *at = binbuf_getvec(te_binbuf);
+ for (int i=0; i<n; i++) s << (i ? " " : "[") << at[i];
+ s << "]";
+ return s.str();
+}
+
+void add_creator2(FClass *fclass, const char *name) {
+ fclasses[string(name)] = fclass;
+ class_addcreator((t_newmethod)BFObject_new,gensym((char *)name),A_GIMME,0);
+}
+typedef struct _methodentry
+{
+ t_symbol *me_name;
+ t_gotfn me_fun;
+ t_atomtype me_arg[MAXPDARG+1];
+} t_methodentry;
+struct _class {
+ t_symbol *c_name; /* name (mostly for error reporting) */
+ t_symbol *c_helpname; /* name of help file */
+ t_symbol *c_externdir; /* directory extern was loaded from */
+ size_t c_size; /* size of an instance */
+ t_methodentry *c_methods; /* methods other than bang, etc below */
+ int c_nmethod; /* number of methods */
+ // ...
+};
+void add_creator3(FClass *fclass, const char *name) {
+ fclasses[string(name)] = fclass;
+ t_class *c = pd_objectmaker;
+ t_symbol *want = gensym(name);
+ for (int i=c->c_nmethod-1; i>=0; i--) {
+ t_methodentry *m = c->c_methods+i;
+ if (m->me_name==want) {m->me_fun = t_gotfn(BFObject_new); m->me_arg[0]=A_GIMME; m->me_arg[1]=A_NULL; break;}
+ }
+}
+
+//****************************************************************
+
+struct t_namelist;
+extern t_namelist *sys_searchpath, *sys_helppath;
+extern "C" t_namelist *namelist_append_files(t_namelist *, char *);
+static void add_to_path(char *dir) {
+ static bool debug = false;
+ char bof[1024];
+ if (debug) post("gridflow was found in %s",dir);
+ gf_data_path.push_back(string(dir)+"/images");
+ if (debug) post("adding gf_data_path %s/images",dir);
+ sprintf(bof,"%s/abstractions",dir); sys_searchpath = namelist_append_files(sys_searchpath,bof);
+ if (debug) post("adding -path %s",bof);
+ sprintf(bof,"%s/deprecated",dir); sys_searchpath = namelist_append_files(sys_searchpath,bof);
+ if (debug) post("adding -path %s",bof);
+ sprintf(bof,"%s/doc/flow_classes",dir); sys_helppath = namelist_append_files(sys_helppath, bof);
+ if (debug) post("adding -helppath %s",bof);
+}
+
+//----------------------------------------------------------------
+
+t_list *list_new (int argc, t_atom *argv) {
+ t_list *b = binbuf_new();
+ binbuf_add(b,argc,argv);
+ return b;
+}
+void list_free (t_list *self) {binbuf_free(self);}
+
+//----------------------------------------------------------------
+
+void fclass_install(FClass *fclass, FClass *super, size_t bytes) {
+ fclass->super = super;
+ if (fclass->startup) fclass->startup(fclass);
+ fclass->bytes = bytes;
+}
+
+void install2(FClass *fclass, const char *name, int inlets, int outlets) {
+ fclass->ninlets = inlets;
+ fclass->noutlets = outlets;
+ fclass->name = string(name);
+ fclass->bfclass = class_new(gensym((char *)name), (t_newmethod)BFObject_new, (t_method)BFObject_delete,
+ sizeof(BFObject), CLASS_DEFAULT, A_GIMME,0);
+ fclasses[string(name)] = fclass;
+ fclasses_pd[fclass->bfclass] = fclass;
+ t_class *b = fclass->bfclass;
+ class_addanything(b,t_method(BFObject_anything0));
+ FMethod m = funcall_lookup(fclass,"_0_loadbang");
+ //post("class %s loadbang %08x",name,long(m));
+ if (m) class_addmethod(fclass->bfclass,t_method(BFObject_loadbang),gensym("loadbang"),A_NULL);
+}
+
+/* This code handles nested lists because PureData (all versions including 0.40) doesn't do it */
+int handle_braces(int ac, t_atom *av) {
+ int stack[16];
+ int stackn=0;
+ int j=0;
+ t_binbuf *buf = binbuf_new();
+ for (int i=0; i<ac; ) {
+ int close=0;
+ if (av[i].a_type==A_SYMBOL) {
+ const char *s = av[i].a_symbol->s_name;
+ while (*s=='(') {
+ if (stackn==16) {binbuf_free(buf); RAISE("too many nested lists (>16)");}
+ stack[stackn++]=j;
+ s++;
+ }
+ const char *se = s+strlen(s);
+ while (se>s && se[-1]==')') {se--; close++;}
+ if (s!=se) {
+ binbuf_text(buf,(char *)s,se-s);
+ if ((binbuf_getnatom(buf)==1 && binbuf_getvec(buf)[0].a_type==A_FLOAT) || binbuf_getvec(buf)[0].a_type==A_COMMA) {
+ av[j++] = binbuf_getvec(buf)[0];
+ } else {
+ char ss[MAXPDSTRING];
+ int n = min(long(se-s),long(MAXPDSTRING-1));
+ sprintf(ss,"%.*s",n,s);
+ SETSYMBOL(av+j,gensym(ss)); j++; // av[j++] = gensym(s);
+ }
+ }
+ } else av[j++]=av[i];
+ i++;
+ while (close--) {
+ if (!stackn) {binbuf_free(buf); RAISE("close-paren without open-paren",av[i]);}
+ t_binbuf *a2 = binbuf_new(); /* leak because there is no deallocation mechanism whatsoever */
+ int j2 = stack[--stackn];
+ binbuf_add(a2,j-j2,av+j2);
+ j=j2;
+ SETLIST(av+j,a2);
+ j++;
+ }
+ }
+ binbuf_free(buf);
+ if (stackn) RAISE("too many open-paren (%d)",stackn);
+ return j;
+}
+
+// foreach macro from desiredata:
+#define foreach(ITER,COLL) for(typeof(COLL.begin()) ITER = COLL.begin(); ITER != (COLL).end(); ITER++)
+
+\class FObject
+\def 0 get (t_symbol *s=0) {
+ FClass *fc = fclasses_pd[pd_class(bself)];
+ if (!s) {
+ t_atom a[1];
+ foreach(attr,fc->attrs) {
+ SETSYMBOL(a,gensym((char *)attr->second->name.data()));
+ pd_typedmess((t_pd *)bself,gensym("get"),1,a);
+ }
+ } else {
+ //t_atom a[1];
+ //outlet_anything(bself->outlets[bself->noutlets-1],s,1,a);
+ FMethod m = funcall_lookup(bself,"___get");
+ t_atom2 a[1];
+ SETSYMBOL(a,s);
+ if (m) m(this,1,a);
+ }
+}
+\def 0 help () {
+ FClass *fc = fclasses_pd[pd_class(bself)];
+ post("attributes (");
+ foreach(attr,fc->attrs) post(" %s %s;",attr->second->type.data(),attr->second->name.data());
+ post(")");
+ post("methods (");
+ for (int i=0; i<fc->methodsn; i++) post(" %s",fc->methods[i].selector);
+ post(")");
+}
+\classinfo {}
+\end class
+
+void startup_number();
+void startup_flow_objects();
+void startup_flow_objects2();
+void startup_format();
+STARTUP_LIST(void)
+
+void blargh () {
+#ifdef MACOSX
+ fprintf(stderr,"unhandled exception\n");
+#else
+ void *array[25];
+ int nSize = backtrace(array, 25);
+ char **symbols = backtrace_symbols(array, nSize);
+ for (int i=0; i<nSize; i++) fprintf(stderr,"%d: %s\n",i,symbols[i]);
+ free(symbols);
+#endif
+}
+
+static t_gobj *canvas_last (t_canvas *self) {
+#ifdef DESIRE
+ t_gobj *g = canvas_first(self);
+ while (gobj_next(g)) g=gobj_next(g);
+#else
+ t_gobj *g = self->gl_list;
+ while (g->g_next) g=g->g_next;
+#endif
+ return g;
+}
+
+#ifdef DESIRE
+extern "C" void canvas_delete(t_canvas *, t_gobj *);
+#define glist_delete canvas_delete
+#endif
+
+static void canvas_else (t_canvas *self, t_symbol *s, int argc, t_atom *argv) {
+ t_gobj *g = canvas_last(self);
+ if (pd_newest()) return;
+ glist_delete(self,g);
+ if (argc<1 || argv[0].a_type!=A_SYMBOL) {error("$1 must be a symbol"); return;}
+ pd_typedmess((t_pd *)self,argv[0].a_w.w_symbol,argc-1,argv+1);
+}
+
+// those are not really leaks but deleting them make them disappear from valgrind
+// however, there's still a problem doing it, so, we won't do it.
+static void gridflow_unsetup () {
+/*
+ foreach(iter,fclasses_pd) {
+ FClass *fc = iter->second;
+ foreach(iter2,fc->attrs) delete iter2->second;
+ fc->FClass::~FClass();
+ }
+*/
+}
+
+void allow_big_stack () {
+ struct rlimit happy;
+ if (0>getrlimit(RLIMIT_STACK,&happy))
+ error("GF: getrlimit: %s",strerror(errno));
+ happy.rlim_cur = happy.rlim_max;
+ if (0>setrlimit(RLIMIT_STACK,&happy))
+ error("GF: setting stack size to %ld: %s",happy.rlim_cur,strerror(errno));
+ else
+ post( "GF: setting stack size to %ld",happy.rlim_cur);
+}
+
+// note: contrary to what m_pd.h says, pd_getfilename() and pd_getdirname()
+// don't exist; also, canvas_getcurrentdir() isn't available during setup
+// (segfaults), in addition to libraries not being canvases ;-)
+// AND ALSO, CONTRARY TO WHAT m_pd.h SAYS, open_via_path()'s args are reversed!!!
+extern "C" void gridflow_setup () {
+ post("GridFlow " GF_VERSION ", Copyright (c) 2001-2009 Mathieu Bouchard");
+ post("GridFlow was compiled on "__DATE__", "__TIME__);
+ //std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
+ std::set_terminate(blargh);
+ allow_big_stack();
+ try {
+ char *dirname = new char[MAXPDSTRING];
+ char *dirresult = new char[MAXPDSTRING];
+ char *nameresult;
+ char *zz=getcwd(dirname,MAXPDSTRING); /* zz only exists because gcc 4.3.3 gives me a bogus warning otherwise. */
+ if (zz<0) {post("AAAARRRRGGGGHHHH!"); exit(69);}
+ int fd=open_via_path(dirname,"gridflow/gridflow",PDSUF,dirresult,&nameresult,MAXPDSTRING,1);
+ if (fd<0) fd=open_via_path(dirname, "gridflow",PDSUF,dirresult,&nameresult,MAXPDSTRING,1);
+ if (fd>=0) close(fd); else post("%s was not found via the -path!","gridflow"PDSUF);
+ /* nameresult is only a pointer in dirresult space so don't delete[] it. */
+ add_to_path(dirresult);
+ BFProxy_class = class_new(gensym("gf.proxy"),0,0,sizeof(BFProxy),CLASS_PD|CLASS_NOINLET, A_NULL);
+ class_addanything(BFProxy_class,BFProxy_anything);
+ srandom(rdtsc());
+#define FOO(_sym_,_name_) bsym._sym_ = gensym(_name_);
+BUILTIN_SYMBOLS(FOO)
+#undef FOO
+ startup_number();
+ \startall
+ startup_flow_objects();
+ startup_flow_objects2();
+ startup_format();
+ STARTUP_LIST()
+ //sys_gui("bind . <Motion> {puts %W}\n");
+ sys_vgui("proc gridflow_add_to_help {menu} {\n"
+ "$menu add separator\n"
+ "$menu add command -label {GridFlow About} -command {pd pd open about.pd %s/doc \\;}\n"
+ "$menu add command -label {GridFlow Index} -command {pd pd open index.pd %s/doc \\;}\n"
+ "}\n"
+ "gridflow_add_to_help .mbar.help\n"
+ "rename menu_addstd menu_addstd_old\n"
+ "proc menu_addstd {mbar} {menu_addstd_old $mbar; gridflow_add_to_help $mbar.help}\n",dirresult,dirresult);
+ delete[] dirresult;
+ delete[] dirname;
+ } catch (Barf &oozy) {oozy.error(0);}
+ signal(SIGSEGV,SIG_DFL);
+ signal(SIGABRT,SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ atexit(gridflow_unsetup);
+ extern t_class *canvas_class;
+ class_addmethod(canvas_class,(t_method)canvas_else,gensym("else"),A_GIMME,0);
+}
diff --git a/externals/gridflow/src/gridflow.hxx b/externals/gridflow/src/gridflow.hxx
new file mode 100644
index 00000000..bf278ffe
--- /dev/null
+++ b/externals/gridflow/src/gridflow.hxx
@@ -0,0 +1,911 @@
+/*
+ $Id: gridflow.h 4535 2009-10-31 14:50:38Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __GRIDFLOW_H
+#define __GRIDFLOW_H
+
+#define GF_VERSION "0.9.6"
+
+#include "m_pd.h"
+#include "config.h"
+#include <vector>
+#include <string>
+#include <sstream>
+#include <map>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <math.h>
+#ifdef __APPLE__
+static inline void *memalign (size_t a, size_t n) {return malloc(n);}
+#else
+#include <malloc.h>
+#endif
+
+typedef std::string string;
+
+#ifndef a_float
+#define a_float a_w.w_float
+#define a_symbol a_w.w_symbol
+#define a_gpointer a_w.w_gpointer
+#endif
+
+#define gensym(s) gensym(const_cast<char *>(s))
+#define sys_vgui(FMT,ARGS...) sys_vgui(const_cast<char *>(FMT),ARGS)
+#define sys_gui(s) sys_gui(const_cast<char *>(s))
+
+#ifndef DESIREDATA
+#define A_LIST t_atomtype(13) /* (t_binbuf *) */
+#endif
+#define A_GRID t_atomtype(14) /* (Grid *) */
+#define A_GRIDOUT t_atomtype(15) /* (GridOut *) */
+// the use of w_gpointer here is fake, just because there's no suitable member in the union
+struct Grid;
+struct GridOutlet;
+static inline void SETLIST( t_atom *a, t_binbuf *b) {a->a_type = A_LIST; a->a_gpointer = (t_gpointer *)b;}
+static inline void SETGRID( t_atom *a, Grid *g) {a->a_type = A_GRID; a->a_gpointer = (t_gpointer *)g;}
+static inline void SETGRIDOUT(t_atom *a, GridOutlet *g) {a->a_type = A_GRIDOUT; a->a_gpointer = (t_gpointer *)g;}
+static inline void SETNULL( t_atom *a) {a->a_type = A_NULL; a->a_gpointer = 0;}
+
+typedef t_binbuf t_list;
+
+t_list *list_new (int argc, t_atom *argv);
+void list_free (t_list *self);
+
+typedef char int8; typedef unsigned char uint8;
+typedef short int16; typedef unsigned short uint16;
+typedef int int32; typedef unsigned int uint32;
+typedef long long int64; typedef unsigned long long uint64;
+typedef float float32;
+typedef double float64;
+
+// three-way comparison (T is assumed Comparable)
+template <class T> static inline T cmp(T a, T b) {return a<b ? -1 : a>b;}
+
+// a remainder function such that div2(a,b)*b+mod(a,b) = a and for which mod(a,b) is in [0;b) or (b;0].
+// in contrast to C-language builtin a%b, this one has uniform behaviour around zero,
+// that is, the same as around any other whole number.
+static inline int mod(int a, int b) {int c=a%b; c+=b&-(c&&(a<0)^(b<0)); return c;}
+
+// counterpart of mod(a,b), just like a/b and a%b are counterparts
+static inline int div2(int a, int b) {return (a/b)-((a<0)&&!!(a%b));}
+
+static inline int32 gf_abs( int32 a) {return a>0?a:-a;}
+static inline int64 gf_abs( int64 a) {return a>0?a:-a;}
+static inline float32 gf_abs(float32 a) {return fabs(a);}
+static inline float64 gf_abs(float64 a) {return fabs(a);}
+
+// integer powers in log(b) time. T is assumed Integer
+template <class T> static inline T ipow(T a, T b) {T r=1; for(;;) {if (b&1) r*=a; b>>=1; if (!b) return r; a*=a;}}
+static inline float32 ipow(float32 a, float32 b) {return pow(a,b);}
+static inline float64 ipow(float64 a, float64 b) {return pow(a,b);}
+
+#undef min
+#undef max
+// minimum/maximum functions; T is assumed to be Comparable
+template <class T> static inline T min(T a, T b) {return a<b?a:b;}
+template <class T> static inline T max(T a, T b) {return a>b?a:b;}
+template <class T> static inline T clip(T a, T lower, T upper) {return a<lower?lower:a>upper?upper:a;}
+//template <class T> inline T min(T a, T b) { T c = (a-b)>>31; return (a&c)|(b&~c); }
+
+// greatest common divisor, by euclid's algorithm
+// this runs in log(a+b) number operations
+template <class T> static T gcd (T a, T b) {
+ while (b) {T c=mod(a,b); a=b; b=c;}
+ return a;
+}
+
+// greatest common divisor, the binary algorithm. haven't tried yet.
+template <class T> static T gcd2 (T a, T b) {
+ int s=0;
+ while (((a|b)&1)==0) { a>>=1; b>>=1; s++; }
+ while (a) {
+ if ((a&1)==0) a>>=1;
+ else if ((b&1)==0) b>>=1;
+ else {T t=abs(a-b); if (a<b) b=t; else a=t;}
+ }
+ return b<<s;
+}
+
+// least common multiple; this runs in log(a+b) like gcd.
+template <class T> static inline T lcm (T a, T b) {return a*b/gcd(a,b);}
+
+// returns the position (0..63) of highest bit set in a word, or 0 if none.
+#define Z(N) if ((x>>N)&(((typeof(x))1<<N)-1)) { x>>=N; i+=N; }
+static int highest_bit(uint8 x) {int i=0; Z(4)Z(2)Z(1)return i;}
+static int highest_bit(uint16 x) {int i=0; Z(8)Z(4)Z(2)Z(1)return i;}
+static int highest_bit(uint32 x) {int i=0; Z(16)Z(8)Z(4)Z(2)Z(1)return i;}
+static int highest_bit(uint64 x) {int i=0;Z(32)Z(16)Z(8)Z(4)Z(2)Z(1)return i;}
+#undef Z
+// returns the position (0..63) of lowest bit set in a word, or 0 if none.
+template <class T> static int lowest_bit(T n) { return highest_bit((~n+1)&n); }
+
+static double drand() { return 1.0*rand()/(RAND_MAX+1.0); }
+
+// is little-endian
+static inline bool is_le() {int x=1; return ((char *)&x)[0];}
+
+static inline uint64 rdtsc()
+#if defined(HAVE_PENTIUM)
+{uint64 x; __asm__ volatile (".byte 0x0f, 0x31":"=A"(x)); return x;}
+#else
+{return 0;}
+#endif
+
+#ifdef HAVE_LITE
+#define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32)
+#define EACH_FLOAT_TYPE(MACRO) MACRO(float32)
+#else
+#define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32) MACRO(int64)
+#define EACH_FLOAT_TYPE(MACRO) MACRO(float32) MACRO(float64)
+#endif
+#define EACH_NUMBER_TYPE(MACRO) EACH_INT_TYPE(MACRO) EACH_FLOAT_TYPE(MACRO)
+
+// note: loop unrolling macros assume N!=0
+// btw this may cause alignment problems when 8 does not divide N
+#define UNROLL_8(MACRO,N,PTR,ARGS...) \
+ int n__=(-N)&7; PTR-=n__; N+=n__; \
+ switch (n__) { start: \
+ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \
+ case 4:MACRO(4); case 5:MACRO(5); case 6:MACRO(6); case 7:MACRO(7); \
+ PTR+=8; N-=8; ARGS; if (N) goto start; }
+#define UNROLL_4(MACRO,N,PTR,ARGS...) \
+ int n__=(-N)&3; PTR-=n__; N+=n__; \
+ switch (n__) { start: \
+ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \
+ PTR+=4; N-=4; ARGS; if (N) goto start; }
+
+struct BFObject;
+struct Barf {
+ string text;
+ Barf(const char *s, ...);
+ Barf(const char *file, int line, const char *func, const char *s, ...);
+ void error(BFObject *bself);
+ ~Barf() {}
+};
+
+#define NEWBUF(T,N) (new T[N])
+#define DELBUF(A) (delete[] A)
+
+#ifdef __WIN32__
+#define INT winINT
+#define random rand
+#undef send
+#undef close
+#define sigjmp_buf jmp_buf
+#define siglongjmp longjmp
+#endif
+
+//#define _L_ post("%s:%d in %s",__FILE__,__LINE__,__PRETTY_FUNCTION__);
+#define _L_ fprintf(stderr,"%s:%d in %s\n",__FILE__,__LINE__,__PRETTY_FUNCTION__);
+#define RAISE(args...) throw Barf(__FILE__,__LINE__,__PRETTY_FUNCTION__,args)
+#define VA int argc, t_atom2 *argv
+// returns the size of a statically defined array
+#define COUNT(_array_) ((int)(sizeof(_array_) / sizeof((_array_)[0])))
+#define WATCH(n,ar) { \
+ char foo[16*1024], *p=foo; p += sprintf(p,"%s: ",#ar); \
+ for (int q=0; q<n; q++) p += sprintf(p,"%lld ",(long long)ar[q]); \
+ post("%s",foo);}
+
+//****************************************************************
+
+struct FObject;
+struct t_atom2;
+typedef void (*FMethod)(FObject *, int, t_atom2 *);
+
+#define BUILTIN_SYMBOLS(MACRO) \
+ MACRO(_grid,"grid") MACRO(_bang,"bang") MACRO(_float,"float") \
+ MACRO(_list,"list") MACRO(_sharp,"#") \
+ MACRO(_in,"in") MACRO(_out,"out")
+
+extern struct BuiltinSymbols {
+#define FOO(_sym_,_str_) t_symbol *_sym_;
+BUILTIN_SYMBOLS(FOO)
+#undef FOO
+} bsym;
+
+struct Numop;
+struct Pointer;
+#define INT(x) convert(x,(int32*)0)
+#define TO(T,x) convert(x,(T*)0)
+
+// trick to be able to define methods in t_atom
+struct t_atom2 : t_atom {
+ bool operator == (t_symbol *b) {return this->a_type==A_SYMBOL && this->a_symbol==b;}
+ bool operator != (t_symbol *b) {return !(*this==b);}
+ operator float32 () const {if (a_type!=A_FLOAT) RAISE("expected float"); return a_float;}
+#define TYPECASTER(T,A,B) operator T () const { \
+ float f = round(float32(*this)); if (f<A || f>=B) RAISE("value %f is out of range",f); return (T)f;}
+ TYPECASTER( bool, 0 , 2 )
+ TYPECASTER( uint8, 0 , 0x100 )
+ TYPECASTER( int16, -0x8000 , 0x8000 )
+ TYPECASTER( uint16, 0 , 0x10000 )
+ TYPECASTER( int32, -0x80000000LL, 0x80000000LL)
+ TYPECASTER( uint32, 0 ,0x100000000LL)
+#undef TYPECASTER
+ operator uint64 () const {if (a_type!=A_FLOAT) RAISE("expected float"); return (uint64)round(a_float);}
+ operator int64 () const {if (a_type!=A_FLOAT) RAISE("expected float"); return (int64)round(a_float);}
+ operator float64 () const {if (a_type!=A_FLOAT) RAISE("expected float"); return a_float ;}
+
+#define TYPECASTER2(T,A,B,C) operator T () const {if (a_type!=A) RAISE("expected "B); return C;}
+ TYPECASTER2(std::string ,A_SYMBOL ,"symbol" ,std::string(a_symbol->s_name))
+ TYPECASTER2(t_symbol *,A_SYMBOL ,"symbol" , a_symbol )
+ TYPECASTER2(void *,A_POINTER,"pointer" , a_gpointer)
+ TYPECASTER2(t_binbuf *,A_LIST ,"nested list", (t_binbuf *)a_gpointer)
+ TYPECASTER2(Grid *,A_GRID ,"grid" , (Grid *)a_gpointer)
+ TYPECASTER2(GridOutlet *,A_GRIDOUT,"grid outlet", (GridOutlet *)a_gpointer)
+#undef TYPECASTER2
+};
+
+template <class T> T convert(const t_atom &x, T *foo) {const t_atom2 *xx = (const t_atom2 *)&x; return (T)*xx;}
+
+//****************************************************************
+
+//template <class T> class P : T * {};
+//a reference counting pointer class
+template <class T> class P {
+public:
+#define INCR if (p) p->refcount++;
+#define DECR if (p) {p->refcount--; if (!p->refcount) delete p;}
+ T *p;
+ P() {p=0;}
+ P(T *_p) {p=_p ; INCR;}
+ P(const P<T> &_p) {p=_p.p; INCR;}
+ P<T> &operator = (T * _p) {DECR; p=_p; INCR; return *this;}
+ P<T> &operator = (P<T> _p) {DECR; p=_p.p; INCR; return *this;}
+ bool operator == (P<T> _p) {return p==_p.p;}
+ bool operator != (P<T> _p) {return p!=_p.p;}
+ ~P() {DECR;}
+ bool operator !() {return !p;}
+ operator bool() {return !!p;}
+ T &operator *() {return *p;}
+ T *operator ->() {return p;}
+ operator T *() {return p;}
+//#undef INCR
+//#undef DECR
+};
+
+void gfmemcopy(uint8 *out, const uint8 *in, long n);
+template <class T> inline void COPY(T *dest, T *src, long n) {
+ gfmemcopy((uint8*)dest,(const uint8*)src,n*sizeof(T));
+}
+template <class T> inline void CLEAR(T *dest, long n) {
+ memset(dest,0,n*sizeof(T));
+}
+template <class T> static void memswap (T *a, T *b, long n) {
+ T c[n]; COPY(c,a,n); COPY(a,b,n); COPY(b,c,n);
+}
+
+//****************************************************************
+
+struct CObject {
+ int32 refcount;
+ CObject() : refcount(0) {}
+ virtual ~CObject() {}
+ virtual void changed (t_symbol *s=0) {}
+};
+
+// you shouldn't use MethodDecl directly (used by source_filter.rb)
+struct MethodDecl {const char *selector; FMethod method;};
+
+#undef check
+
+//****************************************************************
+// a Dim is a list of dimensions that describe the shape of a grid
+typedef int32 Card; /* should be switched to long int soon */
+struct Dim : CObject {
+ static const Card MAX_DIM=16; // maximum number of dimensions in a grid
+ Card n;
+ Card v[MAX_DIM]; // real stuff
+ void check(); // test invariants
+ Dim(Card n, Card *v){this->n=n; COPY(this->v,v,n); check();}
+ Dim() {n=0; check();}
+ Dim(Card a) {n=1;v[0]=a; check();}
+ Dim(Card a,Card b) {n=2;v[0]=a;v[1]=b; check();}
+ Dim(Card a,Card b,Card c){n=3;v[0]=a;v[1]=b;v[2]=c;check();}
+ Dim(Dim *a, Dim *b, Dim *c=0) {
+ n=a->n+b->n; if(c) n+=c->n;
+ if (n>Dim::MAX_DIM) RAISE("too many dims");
+ COPY(v ,a->v,a->n);
+ COPY(v+a->n,b->v,b->n);
+ if(c) COPY(v+a->n+b->n,c->v,c->n);
+ }
+ Card count() {return n;}
+ Card get(Card i) {return v[i];}
+/* Dim *range(Card i, Card j) {return new Dim(...);} */
+ Card prod(Card start=0, Card end=-1) {
+ if (start<0) start+=n;
+ if (end <0) end +=n;
+ Card tot=1;
+ for (Card i=start; i<=end; i++) tot *= v[i];
+ return tot;
+ }
+ char *to_s();
+ bool equal(P<Dim> o) {
+ if (n!=o->n) return false;
+ for (Card i=0; i<n; i++) if (v[i]!=o->v[i]) return false;
+ return true;
+ }
+};
+
+//****************************************************************
+//NumberTypeE is a very small int identifying the type of the (smallest) elements of a grid
+
+#define NT_UNSIGNED (1<<0)
+#define NT_FLOAT (1<<1)
+#define NT_UNIMPL (1<<2)
+#define NUMBER_TYPE_LIMITS(T,a,b,c) \
+ inline T nt_smallest(T *bogus) {return a;} \
+ inline T nt_greatest(T *bogus) {return b;} \
+ inline T nt_all_ones(T *bogus) {return c;}
+
+NUMBER_TYPE_LIMITS( uint8,0,255,255)
+NUMBER_TYPE_LIMITS( int16,-0x8000,0x7fff,-1)
+NUMBER_TYPE_LIMITS( int32,-0x80000000,0x7fffffff,-1)
+NUMBER_TYPE_LIMITS( int64,-0x8000000000000000LL,0x7fffffffffffffffLL,-1)
+NUMBER_TYPE_LIMITS(float32,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0))
+NUMBER_TYPE_LIMITS(float64,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0))
+
+#ifdef HAVE_LITE
+#define NT_NOTLITE NT_UNIMPL
+#define NONLITE(x...)
+#else
+#define NT_NOTLITE 0
+#define NONLITE(x...) x
+#endif
+#define NUMBER_TYPES(MACRO) \
+ MACRO(uint8, 8,NT_UNSIGNED, "u8,b") \
+ MACRO(int16,16,0, "i16,s") \
+ MACRO(int32,32,0, "i32,i") \
+ MACRO(int64,64,NT_NOTLITE, "i64,l") \
+ MACRO(float32,32,NT_FLOAT, "f32,f") \
+ MACRO(float64,64,NT_NOTLITE|NT_FLOAT, "f64,d")
+
+enum NumberTypeE {
+#define FOO(_sym_,args...) _sym_##_e,
+NUMBER_TYPES(FOO)
+#undef FOO
+number_type_table_end
+};
+
+#define FOO(_type_) \
+inline NumberTypeE NumberTypeE_type_of(_type_ *x) {return _type_##_e;}
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+
+struct NumberType : CObject {
+ const char *name;
+ int size;
+ int flags;
+ const char *aliases;
+ NumberTypeE index;
+ NumberType(const char *name_, int size_, int flags_, const char *aliases_) :
+ name(name_), size(size_), flags(flags_), aliases(aliases_) {}
+};
+
+NumberTypeE NumberTypeE_find (string sym);
+NumberTypeE NumberTypeE_find (const t_atom &sym);
+
+#define TYPESWITCH(T,C,E) switch (T) { \
+ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \
+ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break;) \
+ case float32_e: C(float32) break; NONLITE(case float64_e: C(float64) break;) \
+ default: E; RAISE("type '%s' not available here",number_type_table[T].name);}
+#define TYPESWITCH_JUSTINT(T,C,E) switch (T) { \
+ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \
+ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break;) \
+ default: E; RAISE("type '%s' not available here",number_type_table[T].name);}
+
+//****************************************************************
+//BitPacking objects encapsulate optimised loops of bitfield conversion (mostly for I/O)
+struct BitPacking;
+// those are the types of the optimised loops of conversion; inputs are const
+struct Packer {
+#define FOO(S) void (*as_##S)(BitPacking *self, long n, S *in, uint8 *out);
+EACH_INT_TYPE(FOO)
+#undef FOO
+};
+struct Unpacker {
+#define FOO(S) void (*as_##S)(BitPacking *self, long n, uint8 *in, S *out);
+EACH_INT_TYPE(FOO)
+#undef FOO
+};
+
+struct BitPacking : CObject {
+ Packer * packer;
+ Unpacker *unpacker;
+ unsigned int endian; // 0=big, 1=little, 2=same, 3=different
+ int bytes;
+ int size;
+ uint32 mask[4];
+ BitPacking(){::abort();} // don't call, but don't remove. sorry.
+ BitPacking(int endian, int bytes, int size, uint32 *mask,
+ Packer *packer=0, Unpacker *unpacker=0);
+ bool is_le();
+ bool eq(BitPacking *o);
+// main entrances to Packers/Unpackers
+ template <class T> void pack(long n, T *in, uint8 *out);
+ template <class T> void unpack(long n, uint8 *in, T *out);
+};
+
+int high_bit(uint32 n);
+int low_bit(uint32 n);
+void swap16(long n, uint16 *data);
+void swap32(long n, uint32 *data);
+void swap64(long n, uint64 *data);
+inline void swap_endian(long n, uint8 *data) {}
+inline void swap_endian(long n, int16 *data) {swap16(n,(uint16 *)data);}
+inline void swap_endian(long n, int32 *data) {swap32(n,(uint32 *)data);}
+inline void swap_endian(long n, int64 *data) {swap64(n,(uint64 *)data);}
+inline void swap_endian(long n, float32 *data) {swap32(n,(uint32 *)data);}
+inline void swap_endian(long n, float64 *data) {swap64(n,(uint64 *)data);}
+
+//****************************************************************
+// Numop objects encapsulate optimised loops of simple operations
+
+enum LeftRight { at_left, at_right };
+
+template <class T>
+struct NumopOn {
+ // Function Vectorisations
+ typedef void (*Map )( long n, T *as, T b ); Map map;
+ typedef void (*Zip )( long n, T *as, T *bs); Zip zip;
+ typedef void (*Fold)(long an, long n, T *as, T *bs); Fold fold;
+ typedef void (*Scan)(long an, long n, T *as, T *bs); Scan scan;
+ // Algebraic Properties (those involving simply numeric types)
+ typedef bool (*AlgebraicCheck)(T x, LeftRight side);
+ // neutral: right: forall y {f(x,y)=x}; left: forall x {f(x,y)=y};
+ // absorbent: right: exists a forall y {f(x,y)=a}; ...
+ void (*neutral)(T *,LeftRight); // default neutral: e.g. 0 for addition, 1 for multiplication
+ AlgebraicCheck is_neutral, is_absorbent;
+ NumopOn(Map m, Zip z, Fold f, Scan s,
+ void (*neu)(T *,LeftRight), AlgebraicCheck n, AlgebraicCheck a) :
+ map(m), zip(z), fold(f), scan(s), neutral(neu), is_neutral(n), is_absorbent(a) {}
+ NumopOn() : map(0),zip(0),fold(0),scan(0),neutral(0),is_neutral(0),is_absorbent(0) {}
+ NumopOn(const NumopOn &z) {
+ map=z.map; zip=z.zip; fold=z.fold; scan=z.scan;
+ is_neutral = z.is_neutral; neutral = z.neutral;
+ is_absorbent = z.is_absorbent; }
+};
+
+// semigroup property: associativity: f(a,f(b,c))=f(f(a,b),c)
+#define OP_ASSOC (1<<0)
+// abelian property: commutativity: f(a,b)=f(b,a)
+#define OP_COMM (1<<1)
+
+struct Numop {
+ const char *name;
+ t_symbol *sym;
+ int flags;
+ int size; // numop=1; vecop>1
+#define FOO(T) NumopOn<T> on_##T; \
+ NumopOn<T> *on(T &foo) { \
+ if (!on_##T.map) RAISE("operator %s does not support type "#T,name); \
+ return &on_##T;}
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ template <class T> inline void map(long n, T *as, T b) {
+ on(*as)->map(n,(T *)as,b);}
+ template <class T> inline void zip(long n, T *as, T *bs) {
+ on(*as)->zip(n,(T *)as,(T *)bs);}
+ template <class T> inline void fold(long an, long n, T *as, T *bs) {
+ typename NumopOn<T>::Fold f = on(*as)->fold;
+ if (!f) RAISE("operator %s does not support fold",name);
+ f(an,n,(T *)as,(T *)bs);}
+ template <class T> inline void scan(long an, long n, T *as, T *bs) {
+ typename NumopOn<T>::Scan f = on(*as)->scan;
+ if (!f) RAISE("operator %s does not support scan",name);
+ f(an,n,(T *)as,(T *)bs);}
+
+ Numop(const char *name_,
+#define FOO(T) NumopOn<T> op_##T,
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ int flags_, int size_) : name(name_), flags(flags_), size(size_) {
+#define FOO(T) on_##T = op_##T;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ sym=gensym((char *)name);
+ }
+};
+
+extern NumberType number_type_table[];
+extern std::map<string,NumberType *> number_type_dict;
+extern std::map<string,Numop *> op_dict;
+extern std::map<string,Numop *> vop_dict;
+
+static inline NumberTypeE convert(const t_atom &x, NumberTypeE *bogus) {
+ if (x.a_type!=A_SYMBOL) RAISE("expected number-type"); return NumberTypeE_find(string(x.a_symbol->s_name));}
+
+
+static Numop *convert(const t_atom &x, Numop **bogus) {
+ if (x.a_type!=A_SYMBOL) RAISE("expected numop (as symbol)");
+ string k = string(x.a_symbol->s_name);
+ if (op_dict.find(k)==op_dict.end()) {
+ if (vop_dict.find(k)==vop_dict.end()) RAISE("expected two-input-operator, not '%s'", k.data());
+ return vop_dict[k];
+ } else return op_dict[k];
+}
+
+// ****************************************************************
+struct Grid : CObject {
+ P<Dim> dim;
+ NumberTypeE nt;
+ void *data;
+ int state; /* 0:borrowing 1:owning -1:expired(TODO) */
+ Grid(P<Dim> dim, NumberTypeE nt, bool clear=false) {
+ state=1;
+ if (!dim) RAISE("hell");
+ init(dim,nt);
+ if (clear) {long size = bytes(); CLEAR((char *)data,size);}
+ }
+ Grid(const t_atom &x) {state=1; init_from_atom(x);}
+ Grid(int n, t_atom *a, NumberTypeE nt_=int32_e) {state=1; init_from_list(n,a,nt_);}
+ template <class T> Grid(P<Dim> dim, T *data) {
+ state=0; this->dim=dim;
+ this->nt=NumberTypeE_type_of((T *)0);
+ this->data = data;
+ }
+ // parens are necessary to prevent overflow at 1/16th of the word size (256 megs)
+ long bytes() { return dim->prod()*(number_type_table[nt].size/8); }
+ P<Dim> to_dim () { return new Dim(dim->prod(),(int32 *)*this); }
+#define FOO(T) operator T *() { return (T *)data; }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ Grid *dup () { /* always produce an owning grid even if from a borrowing grid */
+ Grid *foo=new Grid(dim,nt);
+ memcpy(foo->data,data,bytes());
+ return foo;
+ }
+ ~Grid() {if (state==1 && data) free(data);}
+private:
+ void init(P<Dim> dim, NumberTypeE nt) {
+ this->dim = dim;
+ this->nt = nt;
+ data = 0;
+ if (dim) {
+ data = memalign(16,bytes()+16);
+ if (!data) RAISE("out of memory (?) trying to get %ld bytes",bytes());
+ }
+ //fprintf(stderr,"rdata=%p data=%p align=%d\n",rdata,data,align);
+ }
+ void init_from_atom(const t_atom &x);
+ void init_from_list(int n, t_atom *a, NumberTypeE nt=int32_e);
+};
+static inline Grid *convert (const t_atom &r, Grid **bogus) {return new Grid(r);}
+
+// DimConstraint interface:
+// return if d is acceptable
+// else RAISE with proper descriptive message
+typedef void (*DimConstraint)(P<Dim> d);
+
+struct PtrGrid : public P<Grid> {
+ DimConstraint dc;
+ void constrain(DimConstraint dc_) { dc=dc_; }
+ P<Grid> next;
+ PtrGrid() : P<Grid>(), dc(0), next(0) {}
+ PtrGrid(const PtrGrid &_p) : P<Grid>(), dc(0), next(0) {dc=_p.dc; p=_p.p; INCR;}
+// PtrGrid( P<Grid> _p) : P<Grid>(), dc(0), next(0) {dc=_p.dc; p=_p.p; INCR;}
+ PtrGrid( Grid *_p) : P<Grid>(), dc(0), next(0) {
+ p=_p;
+INCR;}
+ PtrGrid &operator =( Grid *_p) {if(dc&&_p)dc(_p->dim);
+DECR; p=_p; INCR;
+return *this;}
+ PtrGrid &operator =(P<Grid> _p) {if(dc&&_p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;}
+ PtrGrid &operator =(PtrGrid _p) {if(dc&&_p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;}
+};
+#undef INCR
+#undef DECR
+
+static inline P<Dim> convert(const t_atom &x, P<Dim> *foo) {
+ Grid *d = convert(x,(Grid **)0);
+ if (!d) RAISE("urgh");
+ if (d->dim->n!=1) RAISE("dimension list must have only one dimension itself");
+ return new Dim(d->dim->v[0],(int32 *)(d->data));
+}
+static inline PtrGrid convert(const t_atom &x, PtrGrid *foo) {return PtrGrid(convert(x,(Grid **)0));}
+
+//****************************************************************
+// GridInlet represents a grid-aware inlet
+
+#define GRIDHANDLER_ARGS(T) GridInlet *in, long dex, long n, T *data
+
+// four-part macro for defining the behaviour of a gridinlet in a class
+// C:Class I:Inlet
+#define GRID_INLET(I) \
+ template <class T> void THISCLASS::grinw_##I (GRIDHANDLER_ARGS(T)) {\
+ ((THISCLASS*)in->parent)->grin_##I(in,dex,n,data);}\
+ template <class T> void THISCLASS::grin_##I (GRIDHANDLER_ARGS(T)) {if (n==-1)
+#define GRID_FLOW else if (n>=0)
+#define GRID_FINISH else if (n==-2)
+#define GRID_END }
+
+/* macro for defining a gridinlet's behaviour as just storage (no backstore) */
+// V is a PtrGrid instance-var
+#define GRID_INPUT(I,V) \
+ GRID_INLET(I) {V=new Grid(in->dim,NumberTypeE_type_of(data));} GRID_FLOW {COPY((T *)*(V)+dex,data,n);} GRID_FINISH
+
+// macro for defining a gridinlet's behaviour as just storage (with backstore)
+// V is a PtrGrid instance-var
+#define GRID_INPUT2(I,V) GRID_INLET(I) {V.next = new Grid(in->dim,NumberTypeE_type_of(data));} \
+ GRID_FLOW {COPY(((T *)*(V.next?V.next.p:&*V.p))+dex,data,n);} GRID_FINISH
+
+typedef struct GridInlet GridInlet;
+typedef struct GridHandler {
+#define FOO(T) void (*flow_##T)(GRIDHANDLER_ARGS(T)); \
+ void flow (GRIDHANDLER_ARGS(T)) const {flow_##T(in,dex,n,data);}
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+} GridHandler;
+
+struct FObject;
+struct GridInlet : CObject {
+ FObject *parent;
+ const GridHandler *gh;
+ GridOutlet *sender;
+ P<Dim> dim;
+ NumberTypeE nt; // kill this
+ long dex;
+ int chunk;
+ PtrGrid buf;// factor-chunk buffer
+ long bufi; // buffer index: how much of buf is filled
+ GridInlet(FObject *parent_, const GridHandler *gh_) :
+ parent(parent_), gh(gh_), sender(0), dim(0), nt(int32_e), dex(0), chunk(-1), bufi(0) {}
+ ~GridInlet() {}
+ void set_chunk(long whichdim);
+ int32 factor() {return buf?buf->dim->prod():1;} // which is usually not the same as this->dim->prod(chunk)
+ void begin(GridOutlet *sender);
+ void finish();
+ template <class T> void flow(long n, T *data); // n=-1 is begin, and n=-2 is finish.
+ void from_list(VA, NumberTypeE nt=int32_e) {Grid t(argc,argv,nt); from_grid(&t);}
+ void from_atom(VA) {Grid t(argv[0]); from_grid(&t);}
+ void from_grid(Grid *g);
+ bool supports_type(NumberTypeE nt);
+private:
+ template <class T> void from_grid2(Grid *g, T foo);
+};
+
+//****************************************************************
+// for use by source_filter.rb ONLY (for \grin)
+#ifndef HAVE_LITE
+#define GRIN(TB,TS,TI,TL,TF,TD) {TB,TS,TI,TL,TF,TD}
+#else
+#define GRIN(TB,TS,TI,TL,TF,TD) {TB,TS,TI,TF}
+#endif // HAVE_LITE
+#define MESSAGE t_symbol *sel, int argc, t_atom2 *argv
+#define MESSAGE2 sel,argc,argv
+#define MESSAGE3 t_symbol *, int, t_atom2 *
+struct AttrDecl {
+ string name;
+ string type;
+ AttrDecl(string name_, string type_) {name=name_; type=type_;}
+};
+typedef FObject *(*t_allocator)(BFObject *,MESSAGE3);
+struct FClass {
+ t_allocator allocator; // returns a new C++ object
+ void (*startup)(FClass *);
+ const char *cname; // C++ name (not PD name)
+ int methodsn; MethodDecl *methods;
+ FClass *super;
+ int ninlets;
+ int noutlets;
+ t_class *bfclass;
+ string name;
+ size_t bytes;
+ std::map<string,AttrDecl *> attrs;
+};
+
+void fclass_install(FClass *fc, FClass *super, size_t bytes);
+
+//****************************************************************
+// GridOutlet represents a grid-aware outlet
+struct GridOutlet : CObject {
+// number of (minimum,maximum) BYTES to send at once
+// starting with version 0.8, this is amount of BYTES, not amount of NUMBERS.
+ static const long MIN_PACKET_SIZE = 1<<8;
+ static const long MAX_PACKET_SIZE = 1<<12;
+// those are set only once
+ FObject *parent; // not a P<> because of circular refs
+ P<Dim> dim; // dimensions of the grid being sent
+ NumberTypeE nt;
+ std::vector<GridInlet *> inlets; // which inlets are we connected to
+// those are updated during transmission
+ long dex; // how many numbers were already sent in this connection
+
+ GridOutlet(FObject *parent_, int woutlet, P<Dim> dim_, NumberTypeE nt_=int32_e);
+ ~GridOutlet() {}
+ void callback(GridInlet *in);
+
+ // send/send_direct: data belongs to caller, may be stack-allocated,
+ // receiver doesn't modify the data; in send(), there is buffering;
+ // in send_direct(), there is not. When switching from buffered to
+ // unbuffered mode, flush() must be called
+ #define FOO(T) void send(long n, T *data) {send_2(n,data);}
+ EACH_NUMBER_TYPE(FOO)
+ #undef FOO
+ template <class T> void send_2(long n, T *data);
+ void flush(); // goes with send();
+ PtrGrid buf; // temporary buffer
+ long bufi; // number of bytes used in the buffer
+ template <class T> void send_direct(long n, T *data);
+ void finish();
+ void create_buf();
+};
+
+//****************************************************************
+
+#define CHECK_GRIN(class,i) \
+ if (in.size()<=i) in.resize(i+1); \
+ if (!in[i]) in[i]=new GridInlet((FObject *)this,&class##_grid_##i##_hand);
+
+#define CHECK_ALIGN16(d,nt) \
+ {int bytes = 16; \
+ int align = ((unsigned long)(void*)d)%bytes; \
+ if (align) {_L_;post("%s(%s): Alignment Warning: %s=%p is not %d-aligned: %d", \
+ ARGS(this), __PRETTY_FUNCTION__,#d,(void*)d,bytes,align);}}
+
+struct BFProxy;
+struct BFObject : t_object {
+ FObject *self;
+ int ninlets,noutlets; // per object settings (not class)
+ BFProxy **inlets; // direct access to inlets (not linked lists)
+ t_outlet **outlets; // direct access to outlets (not linked lists)
+ t_canvas *mom;
+ void ninlets_set(int n, bool draw=true);
+ void noutlets_set(int n, bool draw=true);
+ string binbuf_string ();
+};
+
+// represents objects that have inlets/outlets
+\class FObject : CObject {
+ BFObject *bself; // point to PD peer
+ std::vector<P<GridInlet> > in;
+ P<GridOutlet> out;
+ FObject(BFObject *bself, MESSAGE) : bself(bself) {bself->self = this;}
+ template <class T> void send_out(int outlet, int argc, T *argv) {
+ t_atom foo[argc];
+ for (int i=0; i<argc; i++) SETFLOAT(&foo[i],argv[i]);
+ outlet_list(bself->outlets[outlet],&s_list,argc,foo);
+ }
+ \decl 0 get (t_symbol *s=0);
+ \decl 0 help ();
+};
+\end class
+
+uint64 gf_timeofday();
+extern "C" void Init_gridflow ();
+extern Numop *op_add,*op_sub,*op_mul,*op_div,*op_mod,*op_shl,*op_and,*op_put;
+
+#undef ARGS
+#define ARGS(OBJ) ((OBJ) ? (OBJ)->bself->binbuf_string().data() : "[null]")
+#define NOTEMPTY(_a_) if (!(_a_)) RAISE("'%s' is empty",#_a_);
+#define SAME_TYPE(_a_,_b_) if ((_a_)->nt != (_b_)->nt) RAISE("same type please (%s has %s; %s has %s)", \
+ #_a_, number_type_table[(_a_)->nt].name, \
+ #_b_, number_type_table[(_b_)->nt].name);
+static void SAME_DIM(int n, P<Dim> a, int ai, P<Dim> b, int bi) {
+ if (ai+n > a->n) RAISE("left hand: not enough dimensions");
+ if (bi+n > b->n) RAISE("right hand: not enough dimensions");
+ for (int i=0; i<n; i++) {
+ if (a->v[ai+i] != b->v[bi+i]) {
+ RAISE("mismatch: left dim #%d is %d, right dim #%d is %d",
+ ai+i, a->v[ai+i],
+ bi+i, b->v[bi+i]);}}}
+
+void suffixes_are (const char *name, const char *suffixes);
+#define install(name,inlets,outlets) install2(fclass,name,inlets,outlets)
+void install2(FClass *fclass, const char *name, int inlets, int outlets);
+#define add_creator(name) add_creator2(fclass,name)
+void add_creator2(FClass *fclass, const char *name);
+void add_creator3(FClass *fclass, const char *name);
+#define install_format(name,mode,suffixes) do {install(name,1,1); suffixes_are(name,suffixes);} while(0)
+void call_super(int argc, t_atom *argv);
+#define SUPER call_super(argc,argv);
+
+\class Format : FObject {
+ int mode;
+ int fd;
+ FILE *f;
+ NumberTypeE cast;
+ long frame;
+ Format(BFObject *, MESSAGE);
+ \decl 0 open (t_symbol *mode, string filename);
+ \decl 0 close ();
+ \decl 0 cast (NumberTypeE nt);
+ \decl 0 seek(int frame);
+ \decl 0 rewind ();
+ ~Format ();
+};
+\end class
+
+extern std::vector<string> gf_data_path;
+string gf_find_file (string x);
+void pd_oprintf (std::ostream &o, const char *s, int argc, t_atom *argv);
+void pd_oprint (std::ostream &o, int argc, t_atom *argv);
+void pd_post (const char *s, int argc, t_atom *argv);
+
+inline void set_atom (t_atom *a, uint8 v) {SETFLOAT(a,(float)v);}
+inline void set_atom (t_atom *a, int16 v) {SETFLOAT(a,(float)v);}
+inline void set_atom (t_atom *a, int32 v) {SETFLOAT(a,(float)v);}
+inline void set_atom (t_atom *a, uint32 v) {SETFLOAT(a,(float)v);}
+inline void set_atom (t_atom *a, long v) {SETFLOAT(a,(float)v);}
+inline void set_atom (t_atom *a, float32 v) {SETFLOAT(a,v);}
+inline void set_atom (t_atom *a, float64 v) {SETFLOAT(a,v);}
+inline void set_atom (t_atom *a, t_symbol *v) {SETSYMBOL(a,v);}
+inline void set_atom (t_atom *a, Numop *v) {SETSYMBOL(a,v->sym);}
+inline void set_atom (t_atom *a, t_binbuf *v) {SETLIST(a,v);}
+
+extern std::map<string,FClass *> fclasses;
+int handle_braces(int ac, t_atom *av);
+
+extern FClass ciFObject, ciFormat;
+
+/* both oprintf are copied from desiredata */
+
+static inline int voprintf(std::ostream &buf, const char *s, va_list args) {
+ char *b;
+ int n = vasprintf(&b,s,args);
+ buf << b;
+ free(b);
+ return n;
+}
+static inline int oprintf(std::ostream &buf, const char *s, ...) {
+ va_list args;
+ va_start(args,s);
+ int n = voprintf(buf,s,args);
+ va_end(args);
+ return n;
+}
+
+/* this function was copied from desiredata */
+#ifndef DESIRE
+inline t_symbol *symprintf(const char *s, ...) {
+ char *buf;
+ va_list args;
+ va_start(args,s);
+ if (vasprintf(&buf,s,args)<0) {/*rien*/}
+ va_end(args);
+ t_symbol *r = gensym(buf);
+ free(buf);
+ return r;
+}
+#endif
+
+std::ostream &operator << (std::ostream &self, const t_atom &a);
+
+// from pd/src/g_canvas.c
+#ifdef DESIRE
+#define ce_argc argc
+#define ce_argv argv
+#else
+struct _canvasenvironment {
+ t_symbol *ce_dir; /* directory patch lives in */
+ int ce_argc; /* number of "$" arguments */
+ t_atom *ce_argv; /* array of "$" arguments */
+ int ce_dollarzero; /* value of "$0" */
+};
+#endif
+
+#endif // __GF_GRID_H
diff --git a/externals/gridflow/src/jpeg.cxx b/externals/gridflow/src/jpeg.cxx
new file mode 100644
index 00000000..97482223
--- /dev/null
+++ b/externals/gridflow/src/jpeg.cxx
@@ -0,0 +1,118 @@
+/*
+ $Id: jpeg.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//!@#$ not handling abort on compress
+//!@#$ not handling abort on decompress
+
+#include "gridflow.hxx.fcs"
+/* removing macros (removing warnings) */
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef EXTERN
+extern "C" {
+#include <jpeglib.h>
+};
+
+\class FormatJPEG : Format {
+ P<BitPacking> bit_packing;
+ struct jpeg_compress_struct cjpeg;
+ struct jpeg_decompress_struct djpeg;
+ struct jpeg_error_mgr jerr;
+ short quality;
+ \constructor (t_symbol *mode, string filename) {
+ Format::_0_open(0,0,mode,filename);
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+ quality = 75;
+ }
+ \decl 0 bang ();
+ \decl 0 quality (short quality);
+ \grin 0 int
+};
+
+GRID_INLET(0) {
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2)!=3) RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_chunk(1);
+ cjpeg.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cjpeg);
+ jpeg_stdio_dest(&cjpeg,f);
+ cjpeg.image_width = in->dim->get(1);
+ cjpeg.image_height = in->dim->get(0);
+ cjpeg.input_components = 3;
+ cjpeg.in_color_space = JCS_RGB;
+ jpeg_set_defaults(&cjpeg);
+ jpeg_set_quality(&cjpeg,quality,false);
+ jpeg_start_compress(&cjpeg,TRUE);
+} GRID_FLOW {
+ int rowsize = in->dim->get(1)*in->dim->get(2);
+ int rowsize2 = in->dim->get(1)*3;
+ uint8 row[rowsize2];
+ uint8 *rows[1] = {row};
+ while (n) {
+ bit_packing->pack(in->dim->get(1),data,row);
+ jpeg_write_scanlines(&cjpeg,rows,1);
+ n-=rowsize; data+=rowsize;
+ }
+} GRID_FINISH {
+ jpeg_finish_compress(&cjpeg);
+ jpeg_destroy_compress(&cjpeg);
+} GRID_END
+
+static bool gfeof(FILE *f) {
+ off_t cur,end;
+ cur = ftell(f);
+ fseek(f,0,SEEK_END);
+ end = ftell(f);
+ fseek(f,cur,SEEK_SET);
+ return cur==end;
+}
+
+\def 0 bang () {
+ //off_t off = ftell(f);
+ //fseek(f,off,SEEK_SET);
+ if (gfeof(f)) {outlet_bang(bself->te_outlet); return;}
+ djpeg.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&djpeg);
+ jpeg_stdio_src(&djpeg,f);
+ jpeg_read_header(&djpeg,TRUE);
+ int sx=djpeg.image_width, sy=djpeg.image_height, chans=djpeg.num_components;
+ GridOutlet out(this,0,new Dim(sy,sx,chans),cast);
+ jpeg_start_decompress(&djpeg);
+ uint8 row[sx*chans];
+ uint8 *rows[1] = { row };
+ for (int n=0; n<sy; n++) {
+ jpeg_read_scanlines(&djpeg,rows,1);
+ out.send(sx*chans,row);
+ }
+ jpeg_finish_decompress(&djpeg);
+ jpeg_destroy_decompress(&djpeg);
+}
+
+\def 0 quality (short quality) {this->quality = min(max((int)quality,0),100);}
+
+\classinfo {install_format("#io.jpeg",6,"jpeg jpg");}
+\end class FormatJPEG
+void startup_jpeg () {
+ \startall
+}
diff --git a/externals/gridflow/src/mmx.rb b/externals/gridflow/src/mmx.rb
new file mode 100644
index 00000000..7427771d
--- /dev/null
+++ b/externals/gridflow/src/mmx.rb
@@ -0,0 +1,219 @@
+=begin
+ $Id: mmx.rb 3638 2008-04-21 04:31:20Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2008 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+=end
+
+STDOUT.reopen ARGV[0], "w"
+$loader = File.open ARGV[1], "w"
+$count = 0
+$lines = 0
+puts "; generated by/for GridFlow 0.9.2"
+$loader.puts "#include \"gridflow.hxx.fcs\"\nextern \"C\" {"
+
+# this class is not really used yet (only self.make)
+class AsmFunction
+ def initialize(name)
+ @name = name
+ @label_count = 1
+ end
+ def self.make(name)
+ puts "", "GLOBAL #{name}", "#{name}:"
+ puts "push ebp", "mov ebp,esp", "push esi", "push edi"
+ yield AsmFunction.new(name)
+ puts "pop edi", "pop esi", "leave", "ret", ""
+ end
+ def make_until(*ops)
+ a = @label_count
+ b = @label_count+1
+ @label_count+=2
+ ops[-1] << " #{@name}_#{b}"
+ puts "#{@name}_#{a}: ", *ops
+ yield
+ puts "jmp #{@name}_#{a}"
+ puts "#{@name}_#{b}:"
+ end
+end
+
+$sizeof = {
+ :uint8 => 1,
+ :int16 => 2,
+ :int32 => 4,
+ :int64 => 8,
+ :float32 => 4,
+ :float64 => 8,
+}
+$accum = {
+ :uint8 => "al",
+ :int16 => "ax",
+ :int32 => "eax",
+}
+$asm_type = {
+ :uint8 => "byte",
+ :int16 => "word",
+ :int32 => "dword",
+ :int64 => "qword",
+}
+
+# in the following, the opcode "_" means no such thing seems available.
+# also >> for x86 ought to be shr in the uint8 case.
+# btw, i got all of the MMX information from the NASM manual, Appendix B.
+$opcodes = {
+# [--GF--|--x86--|--mmx-et-al----------------------------------------]
+# [ | |-uint8-|-int16-|-int32-|-int64-|-float32-|-float64-]
+ :add => %w[ + add paddb paddw paddd paddq ],
+ :sub => %w[ - sub psubb psubw psubd psubq ],
+ :and => %w[ & and pand pand pand pand ],
+ :xor => %w[ ^ xor pxor pxor pxor pxor ],
+ :or => %w[ | or por por por por ],
+# :max => %w[ max _ pmaxub pmaxsw _ _ ], # not plain MMX !!! (req.Katmai)
+# :min => %w[ min _ pminub pminsw _ _ ], # not plain MMX !!! (req.Katmai)
+# :eq => %w[ == _ pcmpeqb pcmpeqw pcmpeqd _ ],
+# :gt => %w[ > _ pcmpgtb pcmpgtw pcmpgtd _ ],
+# :shl => %w[ << shl _ psllw pslld psllq ], # noncommutative
+# :shr => %w[ >> sar _ psraw psrad _ ], # noncommutative
+# :clipadd => %w[ clip+ _ paddusb paddsw _ _ ], # future use
+# :clipsub => %w[ clip- _ psubusb psubsw _ _ ], # future use
+# :andnot => %w[ &not _ pandn pandn pandn pandn ], # not planned
+}
+
+$opcodes.each {|k,op|
+ op.map! {|x| if x=="_" then nil else x end }
+ STDERR.puts op.inspect
+}
+$decls = ""
+$install = ""
+
+def make_fun_map(op,type)
+ s="mmx_#{type}_map_#{op}"
+ size = $sizeof[type]
+ accum = $accum[type]
+ sym = $opcodes[op][0]
+ opcode = $opcodes[op][1]
+ mopcode = $opcodes[op][size+(size<4 ? 1 : 0)]
+ return if not mopcode
+ AsmFunction.make(s) {|a|
+ puts "mov ecx,[ebp+8]", "mov esi,[ebp+12]", "mov eax,[ebp+16]"
+ puts "mov dx,ax", "shl eax,8", "mov al,dl" if size==1
+ puts "mov edx,eax", "shl eax,16", "mov ax,dx" if size<=2
+ puts "push eax", "push eax", "movq mm7,[esp]", "add esp,8"
+ foo = proc {|n|
+ a.make_until("cmp ecx,#{8/size*n}","jb near") {
+ 0.step(n,4) {|k|
+ nn=[n-k,4].min
+ o=(0..3).map{|x| 8*(x+k) }
+ for i in 0...nn do puts "movq mm#{i},[esi+#{o[i]}]" end
+ for i in 0...nn do puts "#{mopcode} mm#{i},mm7" end
+ for i in 0...nn do puts "movq [esi+#{o[i]}],mm#{i}" end
+ }
+ puts "lea esi,[esi+#{8*n}]", "lea ecx,[ecx-#{8 / size*n}]"
+ }
+ }
+ foo.call 4
+ foo.call 1
+ a.make_until("test ecx,ecx", "jz") {
+ puts "#{opcode} #{$asm_type[type]} [esi],#{accum}", "lea esi,[esi+#{size}]"
+ puts "dec ecx"
+ }
+ puts "emms"
+ }
+ $decls << "void #{s}(long,#{type}*,#{type});\n"
+ $install << "op_dict[string(\"#{sym}\")]->on_#{type}.map = #{s};\n"
+ $count += 1
+end
+
+def make_fun_zip(op,type)
+ s="mmx_#{type}_zip_#{op}"
+ size = $sizeof[type]
+ accum = $accum[type]
+ sym = $opcodes[op][0]
+ opcode = $opcodes[op][1]
+ mopcode = $opcodes[op][size+(size<4 ? 1 : 0)]
+ return if not mopcode
+ AsmFunction.make(s) {|a|
+ puts "mov ecx,[ebp+8]", "mov edi,[ebp+12]",
+ "mov esi,[ebp+16]"#, "mov ebx,[ebp+20]"
+ foo = proc {|n|
+ a.make_until("cmp ecx,#{8/size*n}","jb near") {
+ 0.step(n,4) {|k|
+ nn=[n-k,4].min
+ o=(0..3).map{|x| 8*(x+k) }
+ for i in 0...nn do puts "movq mm#{i},[edi+#{o[i]}]" end
+ for i in 0...nn do puts "movq mm#{i+4},[esi+#{o[i]}]" end
+ for i in 0...nn do puts "#{mopcode} mm#{i},mm#{i+4}" end
+ for i in 0...nn do puts "movq [edi+#{o[i]}],mm#{i}" end
+ }
+ #for i in 0...n do puts "movq [ebx+#{8*i}],mm#{i}" end
+ puts "lea edi,[edi+#{8*n}]"
+ puts "lea esi,[esi+#{8*n}]"
+ #puts "lea ebx,[ebx+#{8*n}]"
+ puts "lea ecx,[ecx-#{8/size*n}]"
+ }
+ }
+ foo.call 4
+ foo.call 1
+ a.make_until("test ecx,ecx", "jz") {
+ # requires commutativity ??? fails with shl, shr
+ puts "mov #{accum},[esi]"
+ puts "#{opcode} #{$asm_type[type]} [edi],#{accum}"
+ #puts "mov #{accum},[edi]"
+ #puts "#{opcode} #{accum},[esi]"
+ #puts "mov [ebx],#{accum}"
+ puts "lea edi,[edi+#{size}]"
+ puts "lea esi,[esi+#{size}]"
+ #puts "lea ebx,[ebx+#{size}]"
+ puts "dec ecx"
+ }
+ puts "emms"
+ }
+ #$decls << "void #{s}(long,#{type}*,#{type}*,#{type}*);\n"
+ $decls << "void #{s}(long,#{type}*,#{type}*);\n"
+ $install << "op_dict[string(\"#{sym}\")]->on_#{type}.zip = #{s};\n"
+ $count += 1
+end
+
+for op in $opcodes.keys do
+ for type in [:uint8, :int16#, :int32
+ ] do
+ make_fun_map(op,type)
+ make_fun_zip(op,type)
+ end
+end
+
+$loader.puts $decls
+$loader.puts %`
+}; /* extern */
+#include <stdlib.h>
+void startup_mmx_loader () {/*bogus*/}
+void startup_mmx () {
+ if (getenv("NO_MMX")) return;
+ post(\"startup_cpu: using MMX optimisations\");
+ #{$install}
+}`
+
+STDERR.puts "automatically generated #{$count} MMX asm functions"
+
+=begin notes:
+CPUID has a bit for detecting MMX
+PACKSSDW PACKSSWB PACKUSWB = saturation-casting
+PCMPxx: Compare Packed Integers
+PMULHW, PMULLW: Multiply Packed _unsigned_ 16-bit Integers, and Store
+PUNPCKxxx: Unpack and Interleave Data
+=end
diff --git a/externals/gridflow/src/mpeg3.cxx b/externals/gridflow/src/mpeg3.cxx
new file mode 100644
index 00000000..4ee5bf1f
--- /dev/null
+++ b/externals/gridflow/src/mpeg3.cxx
@@ -0,0 +1,83 @@
+/*
+ $Id: mpeg3.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define LIBMPEG_INCLUDE_HERE
+#include "gridflow.hxx.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+\class FormatMPEG3 : Format {
+ mpeg3_t *mpeg;
+ int track;
+ ~FormatMPEG3 () {if (mpeg) {mpeg3_close(mpeg); mpeg=0;}}
+ \constructor (t_symbol *mode, string filename) {
+ track=0;
+ // libmpeg3 may be nice, but it won't take a filehandle, only filename
+ if (mode!=gensym("in")) RAISE("read-only, sorry");
+ filename = gf_find_file(filename);
+ #ifdef MPEG3_UNDEFINED_ERROR
+ int err;
+ mpeg = mpeg3_open((char *)filename.data(),&err);
+ post("mpeg error code = %d",err);
+ #else
+ mpeg = mpeg3_open((char *)filename.data());
+ #endif
+ if (!mpeg) RAISE("IO Error: can't open file `%s': %s", filename.data(), strerror(errno));
+ }
+ \decl 0 seek (int32 frame);
+ \decl 0 rewind ();
+ \decl 0 bang ();
+};
+
+\def 0 seek (int32 frame) {
+ mpeg3_set_frame(mpeg,clip(frame,int32(0),int32(mpeg3_video_frames(mpeg,track)-1)),track);
+}
+\def 0 rewind () {_0_seek(0,0,0);}
+
+\def 0 bang () {
+ int nframe = mpeg3_get_frame(mpeg,track);
+ int nframes = mpeg3_video_frames(mpeg,track);
+ //post("track=%d; nframe=%d; nframes=%d",track,nframe,nframes);
+ if (nframe >= nframes) {outlet_bang(bself->te_outlet); return;}
+ int sx = mpeg3_video_width(mpeg,track);
+ int sy = mpeg3_video_height(mpeg,track);
+ int channels = 3;
+ /* !@#$ the doc says "You must allocate 4 extra bytes in the
+ last output_row. This is scratch area for the MMX routines." */
+ uint8 *buf = NEWBUF(uint8,sy*sx*channels+16);
+ uint8 *rows[sy];
+ for (int i=0; i<sy; i++) rows[i]=buf+i*sx*channels;
+ mpeg3_read_frame(mpeg,rows,0,0,sx,sy,sx,sy,MPEG3_RGB888,track);
+ GridOutlet out(this,0,new Dim(sy,sx,channels),cast);
+ int bs = out.dim->prod(1);
+ for(int y=0; y<sy; y++) out.send(bs,buf+channels*sx*y);
+ DELBUF(buf);
+// return INT2NUM(nframe);
+}
+
+\classinfo {install_format("#io.mpeg",4,"mpg mpeg");}
+\end class FormatMPEG3
+void startup_mpeg3 () {
+ \startall
+}
diff --git a/externals/gridflow/src/netpbm.cxx b/externals/gridflow/src/netpbm.cxx
new file mode 100644
index 00000000..32ef5645
--- /dev/null
+++ b/externals/gridflow/src/netpbm.cxx
@@ -0,0 +1,117 @@
+/*
+ $Id$
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define NETPBM_INCLUDE_HERE
+#include "gridflow.hxx.fcs"
+
+\class FormatNetPBM : Format {
+ struct pam inpam, outpam;
+ \grin 0
+ \constructor (t_symbol *mode, string filename) {
+ Format::_0_open(0,0,mode,filename);
+ memset(& inpam,0,sizeof(pam));
+ memset(&outpam,0,sizeof(pam));
+ }
+ \decl 0 bang ();
+};
+\def 0 bang () {
+ //inpam.allocation_depth = 3;
+ pnm_readpaminit(f, &inpam, /*PAM_STRUCT_SIZE(tuple_type)*/ sizeof(struct pam));
+ tuple *tuplerow = pnm_allocpamrow(&inpam);
+ if (inpam.depth!=3) RAISE("image has %d channels instead of 3 channels",inpam.depth);
+ GridOutlet out(this,0,new Dim(inpam.height,inpam.width,inpam.depth),cast);
+ uint8 buf[inpam.width*3];
+ for (int i=0; i<inpam.height; i++) {
+ pnm_readpamrow(&inpam, tuplerow);
+ for (int j=0; j<inpam.width; j++) {
+ buf[j*3+0] = tuplerow[j][0];
+ buf[j*3+1] = tuplerow[j][1];
+ buf[j*3+2] = tuplerow[j][2];
+ }
+ out.send(inpam.width*inpam.depth,buf);
+ }
+ pnm_freepamrow(tuplerow);
+}
+GRID_INLET(0) {
+ if (in->dim->n!=3) RAISE("need 3 dimensions");
+ if (in->dim->v[2]!=3) RAISE("need 3 channels");
+ outpam.size = sizeof(struct pam);
+ outpam.len = sizeof(struct pam);
+ outpam.file = f;
+ outpam.format = PPM_FORMAT;
+ outpam.height = in->dim->v[0];
+ outpam.width = in->dim->v[1];
+ outpam.depth = in->dim->v[2];
+ outpam.plainformat = false;
+ outpam.maxval = 255;
+ //outpam.allocation_depth = 3;
+ strcpy(outpam.tuple_type,PAM_PPM_TUPLETYPE);
+ pnm_writepaminit(&outpam);
+ in->set_chunk(1);
+} GRID_FLOW {
+ tuple *tuplerow = pnm_allocpamrow(&outpam);
+ int m = in->dim->v[1];
+ for (int i=0; i<n; i+=in->dim->prod(1)) {
+ for (int j=0; j<m; j++, data+=3) {
+ tuplerow[j][0] = int(data[0]);
+ tuplerow[j][1] = int(data[1]);
+ tuplerow[j][2] = int(data[2]);
+ }
+ pnm_writepamrow(&outpam, tuplerow);
+ }
+ pnm_freepamrow(tuplerow);
+} GRID_FINISH {
+ fflush(f);
+} GRID_END
+/* was supposed to be "#io.netpbm" but there's backwards compat. */
+\classinfo {install_format("#io.ppm",6,"ppm pgm pnm pam");}
+\end class FormatNetPBM
+
+/*FormatPPM.subclass("#io:tk",1,1) {
+ install_rgrid 0
+ def initialize(mode)
+ @id = sprintf("x%08x",object_id)
+ @filename = "/tmp/tk-#{$$}-#{@id}.ppm"
+ if mode!=:out then raise "only #out" end
+ super(mode,:file,@filename)
+ GridFlow.gui "toplevel .#{@id}\n"
+ GridFlow.gui "wm title . GridFlow/Tk\n"
+ GridFlow.gui "image create photo gf#{@id} -width 320 -height 240\n"
+ GridFlow.gui "pack [label .#{@id}.im -image #{@id}]\n"
+ end
+ def _0_rgrid_end
+ super
+ @stream.seek 0,IO::SEEK_SET
+ GridFlow.gui "image create photo #{@id} -file #{@filename}\n"
+ end
+ def delete
+ GridFlow.gui "destroy .#{@id}\n"
+ GridFlow.gui "image delete #{@id}\n"
+ end
+ alias close delete
+}*/
+
+void startup_netpbm () {
+ pm_init(0,0);
+ \startall
+}
diff --git a/externals/gridflow/src/number.cxx b/externals/gridflow/src/number.cxx
new file mode 100644
index 00000000..00acb645
--- /dev/null
+++ b/externals/gridflow/src/number.cxx
@@ -0,0 +1,446 @@
+/*
+ $Id: number.c 4452 2009-10-27 15:59:57Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <complex>
+#include <assert.h>
+//using namespace std;
+
+static inline uint64 weight(uint64 x) {uint64 k;
+ k=0x5555555555555555ULL; x = (x&k) + ((x>> 1)&k); //(2**64-1)/(2**2**0-1)
+ k=0x3333333333333333ULL; x = (x&k) + ((x>> 2)&k); //(2**64-1)/(2**2**1-1)
+ k=0x0f0f0f0f0f0f0f0fULL; x = (x&k) + ((x>> 4)&k); //(2**64-1)/(2**2**2-1)
+ k=0x00ff00ff00ff00ffULL; x = (x&k) + ((x>> 8)&k); //(2**64-1)/(2**2**3-1)
+ k=0x0000ffff0000ffffULL; x = (x&k) + ((x>>16)&k); //(2**64-1)/(2**2**4-1)
+ k=0x00000000ffffffffULL; x = (x&k) + ((x>>32)&k); //(2**64-1)/(2**2**5-1)
+ return x;
+}
+
+#ifdef PASS1
+NumberType number_type_table[] = {
+#define FOO(_sym_,_size_,_flags_,args...) NumberType( #_sym_, _size_, _flags_, args ),
+NUMBER_TYPES(FOO)
+#undef FOO
+};
+const long number_type_table_n = COUNT(number_type_table);
+#endif
+
+// those are bogus class-templates in the sense that you don't create
+// objects from those, you just call static functions. The same kind
+// of pattern is present in STL to overcome some limitations of C++.
+
+template <class T> class Op {
+public:
+ // I call abort() on those because I can't say they're purevirtual.
+ static T f(T a, T b) {abort();}
+ static bool is_neutral (T x, LeftRight side) {assert(!"Op::is_neutral called?"); return false;}
+ static bool is_absorbent(T x, LeftRight side) {assert(!"Op::is_absorbent called?"); return false;}
+};
+
+template <class O, class T> class OpLoops: public NumopOn<T> {
+public:
+ static inline T f(T a, T b) {return O::f(a,b);}
+ #define FOO(I) as[I]=f(as[I],b);
+ static void _map (long n, T *as, T b) {if (!n) return; UNROLL_8(FOO,n,as)}
+ #undef FOO
+ #define FOO(I) as[I]=f(as[I],as[ba+I]);
+ static void _zip (long n, T *as, T *bs) {if (!n) return; ptrdiff_t ba=bs-as; UNROLL_8(FOO,n,as)}
+ #undef FOO
+ #define W(i) as[i]=f(as[i],bs[i]);
+ #define Z(i,j) as[i]=f(f(f(f(as[i],bs[i]),bs[i+j]),bs[i+j+j]),bs[i+j+j+j]);
+ static void _fold (long an, long n, T *as, T *bs) {
+ switch (an) {
+ case 1:for(;(n&3)!=0;bs+=1,n--){W(0) } for (;n;bs+= 4,n-=4){Z(0,1) } break;
+ case 2:for(;(n&3)!=0;bs+=2,n--){W(0)W(1) } for (;n;bs+= 8,n-=4){Z(0,2)Z(1,2) } break;
+ case 3:for(;(n&3)!=0;bs+=3,n--){W(0)W(1)W(2) } for (;n;bs+=12,n-=4){Z(0,3)Z(1,3)Z(2,3) } break;
+ case 4:for(;(n&3)!=0;bs+=4,n--){W(0)W(1)W(2)W(3)} for (;n;bs+=16,n-=4){Z(0,4)Z(1,4)Z(2,4)Z(3,4)} break;
+ default:while (n--) {int i=0;
+ for (; i<(an&-4); i+=4, bs+=4) {
+ as[i+0]=f(as[i+0],bs[0]);
+ as[i+1]=f(as[i+1],bs[1]);
+ as[i+2]=f(as[i+2],bs[2]);
+ as[i+3]=f(as[i+3],bs[3]);}
+ for (; i<an; i++, bs++) as[i] = f(as[i],*bs);}}}
+ #undef W
+ #undef Z
+ static void _scan (long an, long n, T *as, T *bs) {
+ for (; n--; as=bs-an) {
+ for (int i=0; i<an; i++, as++, bs++) *bs=f(*as,*bs);
+ }
+ }
+};
+
+template <class T>
+static void quick_mod_map (long n, T *as, T b) {
+ if (!b) return;
+#define FOO(I) as[I]=mod(as[I],b);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+template <class T> static void quick_ign_map (long n, T *as, T b) {}
+template <class T> static void quick_ign_zip (long n, T *as, T *bs) {}
+template <class T> static void quick_put_map (long n, T *as, T b) {
+#define FOO(I) as[I]=b;
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+#ifdef PASS1
+void quick_put_map (long n, int16 *as, int16 b) {
+ if ((n&1)!=0 && ((long)as&4)!=0) {*as++=b; n--;}
+ quick_put_map(n>>1, (int32 *)as, (int32)(b<<16)+b);
+ if ((n&1)!=0) *as++=b;
+}
+void quick_put_map (long n, uint8 *as, uint8 b) {
+ while ((n&3)!=0 && ((long)as&4)!=0) {*as++=b; n--;}
+ int32 c=(b<<8)+b; c+=c<<16;
+ quick_put_map(n>>2, (int32 *)as, c);
+ while ((n&3)!=0) *as++=b;
+}
+#endif
+template <class T> static void quick_put_zip (long n, T *as, T *bs) {
+ gfmemcopy((uint8 *)as, (uint8 *)bs, n*sizeof(T));
+}
+
+#define Plex std::complex
+
+// classic two-input operator
+
+#define DEF_OP_COMMON(op,expr,neu,isneu,isorb,T) \
+ inline static T f(T a, T b) { return (T)(expr); } \
+ inline static void neutral (T *a, LeftRight side) {*a = neu;} \
+ inline static bool is_neutral (T x, LeftRight side) {return isneu;} \
+ inline static bool is_absorbent(T x, LeftRight side) {return isorb;}
+#define DEF_OP(op,expr,neu,isneu,isorb) template <class T> class Y##op : Op<T> { public: \
+ DEF_OP_COMMON(op,expr,neu,isneu,isorb,T);};
+#define DEF_OPFT(op,expr,neu,isneu,isorb,T) template <> class Y##op<T> : Op<T> { public: \
+ DEF_OP_COMMON(op,expr,neu,isneu,isorb,T);};
+// this macro is for operators that have different code for the float version
+#define DEF_OPF( op,expr,expr2,neu,isneu,isorb) \
+ DEF_OP( op,expr, neu,isneu,isorb) \
+ DEF_OPFT(op, expr2,neu,isneu,isorb,float32) \
+ DEF_OPFT(op, expr2,neu,isneu,isorb,float64)
+
+#define OL(O,T) OpLoops<Y##O<T>,T>
+#define VOL(O,T) OpLoops<Y##O<Plex<T> >,Plex<T> >
+#define DECL_OPON(L,O,T) NumopOn<T>( \
+ (NumopOn<T>::Map) L(O,T)::_map, (NumopOn<T>::Zip) L(O,T)::_zip, \
+ (NumopOn<T>::Fold)L(O,T)::_fold, (NumopOn<T>::Scan)L(O,T)::_scan, \
+ &Y##O<T>::neutral, &Y##O<T>::is_neutral, &Y##O<T>::is_absorbent)
+#define DECL_OPON_NOFOLD(L,O,T) NumopOn<T>( \
+ (NumopOn<T>::Map)L(O,T)::_map, (NumopOn<T>::Zip)L(O,T)::_zip, 0,0, \
+ &Y##O<T>::neutral, &Y##O<T>::is_neutral, &Y##O<T>::is_absorbent)
+#define DECLOP( L,M,O,sym,flags,dim) Numop(sym,M(L,O,uint8),M(L,O,int16),M(L,O,int32) \
+ NONLITE(,M(L,O,int64)), M(L,O,float32) NONLITE(,M(L,O,float64)),flags,dim)
+#define DECLOP_NOFLOAT(L,M,O,sym,flags,dim) Numop(sym,M(L,O,uint8),M(L,O,int16),M(L,O,int32) \
+ NONLITE(,M(L,O,int64)),NumopOn<float32>() NONLITE(,NumopOn<float64>()), flags,dim)
+// NONLITE(,M(L,O,int64),NumopOn<float32>(),NumopOn<float64>()), flags,dim)
+#define DECLOP_FLOAT( L,M,O,sym,flags,dim) Numop(sym,NumopOn<uint8>(),NumopOn<int16>(),NumopOn<int32>() \
+ NONLITE(,NumopOn<int64>()),M(L,O,float32) NONLITE(,M(L,O,float64)),flags,dim)
+
+#define DECL_OP( O,sym,flags) DECLOP( OL,DECL_OPON ,O,sym,flags,1)
+#define DECL_OP_NOFLOAT( O,sym,flags) DECLOP_NOFLOAT( OL,DECL_OPON ,O,sym,flags,1)
+#define DECL_OP_NOFOLD( O,sym,flags) DECLOP( OL,DECL_OPON_NOFOLD,O,sym,flags,1)
+#define DECL_OP_NOFOLD_NOFLOAT( O,sym,flags) DECLOP_NOFLOAT( OL,DECL_OPON_NOFOLD,O,sym,flags,1)
+#define DECL_OP_NOFOLD_FLOAT( O,sym,flags) DECLOP_FLOAT( OL,DECL_OPON_NOFOLD,O,sym,flags,1)
+
+#define DECL_VOP( O,sym,flags,dim) DECLOP( VOL,DECL_OPON ,O,sym,flags,dim)
+#define DECL_VOP_NOFLOAT( O,sym,flags,dim) DECLOP_NOFLOAT(VOL,DECL_OPON ,O,sym,flags,dim)
+#define DECL_VOP_NOFOLD( O,sym,flags,dim) DECLOP( VOL,DECL_OPON_NOFOLD,O,sym,flags,dim)
+#define DECL_VOP_NOFOLD_NOFLOAT(O,sym,flags,dim) DECLOP_NOFLOAT(VOL,DECL_OPON_NOFOLD,O,sym,flags,dim)
+#define DECL_VOP_NOFOLD_FLOAT( O,sym,flags,dim) DECLOP_FLOAT( VOL,DECL_OPON_NOFOLD,O,sym,flags,dim)
+
+template <class T> static inline T gf_floor (T a) {
+ return (T) floor((double)a); }
+template <class T> static inline T gf_trunc (T a) {
+ return (T) floor(abs((double)a)) * (a<0?-1:1); }
+
+#ifdef PASS1
+DEF_OP(ignore, a, 0, side==at_right, side==at_left)
+DEF_OP(put, b, 0, side==at_left, side==at_right)
+DEF_OP(add, a+b, 0, x==0, false)
+DEF_OP(sub, a-b, 0, side==at_right && x==0, false)
+DEF_OP(bus, b-a, 0, side==at_left && x==0, false)
+DEF_OP(mul, a*b, 1, x==1, x==0)
+DEF_OP(mulshr8, (a*b)>>8, 256, x==256, x==0)
+DEF_OP(div, b==0 ? (T)0 : a/b , 1, side==at_right && x==1, false)
+DEF_OP(div2, b==0 ? 0 : div2(a,b), 1, side==at_right && x==1, false)
+DEF_OP(vid, a==0 ? (T)0 : b/a , 1, side==at_left && x==1, false)
+DEF_OP(vid2, a==0 ? 0 : div2(b,a), 1, side==at_left && x==1, false)
+DEF_OPF(mod, b==0 ? 0 : mod(a,b), b==0 ? 0 : a-b*gf_floor(a/b), 0, false, (side==at_left && x==0) || (side==at_right && x==1))
+DEF_OPF(dom, a==0 ? 0 : mod(b,a), a==0 ? 0 : b-a*gf_floor(b/a), 0, false, (side==at_left && x==0) || (side==at_right && x==1))
+//DEF_OPF(rem, b==0 ? 0 : a%b, b==0 ? 0 : a-b*gf_trunc(a/b))
+//DEF_OPF(mer, a==0 ? 0 : b%a, a==0 ? 0 : b-a*gf_trunc(b/a))
+DEF_OP(rem, b==0?(T)0:a%b, 0, false, (side==at_left&&x==0) || (side==at_right&&x==1))
+DEF_OP(mer, a==0?(T)0:b%a, 0, false, (side==at_left&&x==0) || (side==at_right&&x==1))
+#endif
+#ifdef PASS2
+DEF_OP(gcd, gcd(a,b), 0, x==0, x==1)
+DEF_OP(gcd2, gcd2(a,b), 0, x==0, x==1) // should test those and pick one of the two
+DEF_OP(lcm, a==0 || b==0 ? (T)0 : lcm(a,b), 1, x==1, x==0)
+DEF_OPF(or , a|b, (float32)((int32)a | (int32)b), 0, x==0, x==nt_all_ones(&x))
+DEF_OPF(xor, a^b, (float32)((int32)a ^ (int32)b), 0, x==0, false)
+DEF_OPF(and, a&b, (float32)((int32)a & (int32)b), -1 /*nt_all_ones((T*)0)*/, x==nt_all_ones(&x), x==0)
+DEF_OPF(shl, a<<b, a*pow(2.0,+b), 0, side==at_right && x==0, false)
+DEF_OPF(shr, a>>b, a*pow(2.0,-b), 0, side==at_right && x==0, false)
+DEF_OP(sc_and, a ? b : a, 1, side==at_left && x!=0, side==at_left && x==0)
+DEF_OP(sc_or, a ? a : b, 0, side==at_left && x==0, side==at_left && x!=0)
+DEF_OP(min, min(a,b), nt_greatest((T*)0), x==nt_greatest(&x), x==nt_smallest(&x))
+DEF_OP(max, max(a,b), nt_smallest((T*)0), x==nt_smallest(&x), x==nt_greatest(&x))
+DEF_OP(cmp, cmp(a,b), 0, false, false)
+DEF_OP(eq, a == b, 0, false, false)
+DEF_OP(ne, a != b, 0, false, false)
+DEF_OP(gt, a > b, 0, false, (side==at_left && x==nt_smallest(&x)) || (side==at_right && x==nt_greatest(&x)))
+DEF_OP(le, a <= b, 0, false, (side==at_left && x==nt_smallest(&x)) || (side==at_right && x==nt_greatest(&x)))
+DEF_OP(lt, a < b, 0, false, (side==at_left && x==nt_greatest(&x)) || (side==at_right && x==nt_smallest(&x)))
+DEF_OP(ge, a >= b, 0, false, (side==at_left && x==nt_greatest(&x)) || (side==at_right && x==nt_smallest(&x)))
+#endif
+#ifdef PASS3
+DEF_OP(sinmul, (float64)b * sin((float64)a * (M_PI / 18000)), 0, false, false) // "LN=9000+36000n RA=0 LA=..."
+DEF_OP(cosmul, (float64)b * cos((float64)a * (M_PI / 18000)), 0, false, false) // "LN=36000n RA=0 LA=..."
+DEF_OP(atan, atan2(a,b) * (18000 / M_PI), 0, false, false) // "LA=0"
+DEF_OP(tanhmul, (float64)b * tanh((float64)a * (M_PI / 18000)), 0, false, x==0)
+DEF_OP(gamma, b<=0 ? (T)0 : (T)(0+floor(pow((float64)a/256.0,256.0/(float64)b)*256.0)), 0, false, false) // "RN=256"
+DEF_OPF(pow, ipow(a,b), pow(a,b), 0, false, false) // "RN=1"
+DEF_OP(logmul, a==0 ? (T)0 : (T)((float64)b * log((float64)gf_abs(a))), 0, false, false) // "RA=0"
+// 0.8
+DEF_OPF(clipadd, clipadd(a,b), a+b, 0, x==0, false)
+DEF_OPF(clipsub, clipsub(a,b), a-b, 0, side==at_right && x==0, false)
+DEF_OP(abssub, gf_abs(a-b), 0, false, false)
+DEF_OP(sqsub, (a-b)*(a-b), 0, false, false)
+DEF_OP(avg, (a+b)/2, 0, false, false)
+DEF_OPF(hypot, floor(sqrt(a*a+b*b)), sqrt(a*a+b*b), 0, false, false)
+DEF_OPF(sqrt, floor(sqrt(a)), sqrt(a), 0, false, false)
+DEF_OP(rand, a==0 ? (T)0 : (T)(random()%(int32)a), 0, false, false)
+//DEF_OP(erf,"erf*", 0)
+DEF_OP(weight,weight((uint64)(a^b) & (0xFFFFFFFFFFFFFFFFULL>>(64-sizeof(T)*8))),0,false,false)
+#define BITS(T) (sizeof(T)*8)
+DEF_OP(rol,((uint64)a<<b)|((uint64)a>>(T)((-b)&(BITS(T)-1))),0,false,false)
+DEF_OP(ror,((uint64)a>>b)|((uint64)a<<(T)((-b)&(BITS(T)-1))),0,false,false)
+
+DEF_OP(sin, sin(a-b), 0, false, false)
+DEF_OP(cos, cos(a-b), 0, false, false)
+DEF_OP(atan2,atan2(a,b), 0, false, false)
+DEF_OP(tanh, tanh(a-b), 0, false, false)
+DEF_OP(exp, exp(a-b), 0, false, false)
+DEF_OP(log, log(a-b), 0, false, false)
+
+#endif
+#ifdef PASS4
+
+template <class T> inline T gf_sqrt(T a) {return (T)floor(sqrt( a));}
+inline float32 gf_sqrt(float32 a) {return sqrtf(a) ;}
+inline float64 gf_sqrt(float64 a) {return sqrt( a) ;}
+
+template <class T> inline Plex<T> cx_sqsub(const Plex<T>& a, const Plex<T>& b) { Plex<T> v=a-b; return v*v; }
+template <class T> inline Plex<T> cx_abssub(const Plex<T>& a, const Plex<T>& b) { Plex<T> v=a-b; return norm(v); }
+
+template <class T> inline Plex<T> gf_c2p(const Plex<T>& a) {
+ return Plex<T>(hypot(a.real(),a.imag()),atan2(a.real(),a.imag())*(18000 / M_PI));
+}
+template <class T> inline Plex<T> gf_p2c(const Plex<T>& a) {
+ return Plex<T>((float64)a.real() * sin((float64)a.imag() * (M_PI / 18000)),
+ (float64)a.real() * cos((float64)a.imag() * (M_PI / 18000)));
+}
+/*
+template <class T> inline Plex<T> cx_atan2 (Plex<T>& a, Plex<T>& b) {
+ if (b==0) return 0;
+ Plex<T> v=a/b;
+ return (log(1+iz)-log(log(1-iz))/2i;
+ // but this is not taking care of sign stuff...
+ // and then what's the use of atan2 on complexes? (use C.log ...)
+}
+*/
+
+//!@#$ neutral,is_neutral,is_absorbent are impossible to use here
+DEF_OP(cx_mul, a*b, 1, false, false)
+DEF_OP(cx_mulconj, a*conj(b), 1, false, false)
+DEF_OP(cx_div, a/b, 1, false, false)
+DEF_OP(cx_divconj, a/conj(b), 1, false, false)
+DEF_OP(cx_sqsub, cx_sqsub(a,b), 0, false, false)
+DEF_OP(cx_abssub, cx_abssub(a,b), 0, false, false)
+DEF_OP(cx_sin, sin(a-b), 0, false, false)
+DEF_OP(cx_cos, cos(a-b), 0, false, false)
+//DEF_OP(cx_atan2,atan2(a,b), 0, false, false)
+DEF_OP(cx_tanh, tanh(a-b), 0, false, false)
+DEF_OP(cx_exp, exp(a-b), 0, false, false)
+DEF_OP(cx_log, log(a-b), 0, false, false)
+DEF_OP(c2p, gf_c2p(a-b), 0, false, false)
+DEF_OP(p2c, gf_p2c(a)+b, 0, false, false)
+#endif
+
+extern Numop op_table1[], op_table2[], op_table3[], op_table4[];
+extern const long op_table1_n, op_table2_n, op_table3_n, op_table4_n;
+
+#ifdef PASS1
+Numop op_table1[] = {
+ DECL_OP(ignore, "ignore", OP_ASSOC),
+ DECL_OP(put, "put", OP_ASSOC),
+ DECL_OP(add, "+", OP_ASSOC|OP_COMM), // "LINV=sub"
+ DECL_OP(sub, "-", 0),
+ DECL_OP(bus, "inv+", 0),
+ DECL_OP(mul, "*", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(mulshr8, "*>>8", OP_ASSOC|OP_COMM),
+ DECL_OP(div, "/", 0),
+ DECL_OP_NOFLOAT(div2, "div", 0),
+ DECL_OP(vid, "inv*", 0),
+ DECL_OP_NOFLOAT(vid2,"swapdiv", 0),
+ DECL_OP_NOFLOAT(mod, "%", 0),
+ DECL_OP_NOFLOAT(dom, "swap%", 0),
+ DECL_OP_NOFLOAT(rem, "rem", 0),
+ DECL_OP_NOFLOAT(mer, "swaprem", 0),
+};
+const long op_table1_n = COUNT(op_table1);
+#endif
+#ifdef PASS2
+Numop op_table2[] = {
+ DECL_OP_NOFLOAT(gcd, "gcd", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(gcd2, "gcd2", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(lcm, "lcm", OP_ASSOC|OP_COMM),
+ DECL_OP(or , "|", OP_ASSOC|OP_COMM),
+ DECL_OP(xor, "^", OP_ASSOC|OP_COMM),
+ DECL_OP(and, "&", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFOLD(shl, "<<", 0),
+ DECL_OP_NOFOLD(shr, ">>", 0),
+ DECL_OP_NOFOLD(sc_and,"&&", 0),
+ DECL_OP_NOFOLD(sc_or, "||", 0),
+ DECL_OP(min, "min", OP_ASSOC|OP_COMM),
+ DECL_OP(max, "max", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFOLD(eq, "==", OP_COMM),
+ DECL_OP_NOFOLD(ne, "!=", OP_COMM),
+ DECL_OP_NOFOLD(gt, ">", 0),
+ DECL_OP_NOFOLD(le, "<=", 0),
+ DECL_OP_NOFOLD(lt, "<", 0),
+ DECL_OP_NOFOLD(ge, ">=", 0),
+ DECL_OP_NOFOLD(cmp, "cmp",0),
+};
+const long op_table2_n = COUNT(op_table2);
+#endif
+#ifdef PASS3
+uint8 clipadd(uint8 a, uint8 b) { int32 c=a+b; return c<0?0:c>255?255:c; }
+int16 clipadd(int16 a, int16 b) { int32 c=a+b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; }
+int32 clipadd(int32 a, int32 b) { int64 c=a+b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; }
+int64 clipadd(int64 a, int64 b) { int64 c=(a>>1)+(b>>1)+(a&b&1), p=nt_smallest((int64 *)0), q=nt_greatest((int64 *)0);
+ return c<p/2?p:c>q/2?q:a+b; }
+uint8 clipsub(uint8 a, uint8 b) { int32 c=a-b; return c<0?0:c>255?255:c; }
+int16 clipsub(int16 a, int16 b) { int32 c=a-b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; }
+int32 clipsub(int32 a, int32 b) { int64 c=a-b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; }
+int64 clipsub(int64 a, int64 b) { int64 c=(a>>1)-(b>>1); //???
+ int64 p=nt_smallest((int64 *)0), q=nt_greatest((int64 *)0);
+ return c<p/2?p:c>q/2?q:a-b; }
+
+Numop op_table3[] = {
+ DECL_OP_NOFOLD(sinmul, "sin*", 0),
+ DECL_OP_NOFOLD(cosmul, "cos*", 0),
+ DECL_OP_NOFOLD(atan, "atan", 0),
+ DECL_OP_NOFOLD(tanhmul,"tanh*", 0),
+ DECL_OP_NOFOLD(gamma, "gamma", 0),
+ DECL_OP_NOFOLD(pow, "**", 0),
+ DECL_OP_NOFOLD(logmul, "log*", 0),
+// 0.8
+ DECL_OP(clipadd,"clip+", OP_ASSOC|OP_COMM),
+ DECL_OP(clipsub,"clip-", 0),
+ DECL_OP_NOFOLD(abssub,"abs-", OP_COMM),
+ DECL_OP_NOFOLD(sqsub, "sq-", OP_COMM),
+ DECL_OP_NOFOLD(avg, "avg", OP_COMM),
+ DECL_OP_NOFOLD(hypot, "hypot",OP_COMM), // huh, almost OP_ASSOC
+ DECL_OP_NOFOLD(sqrt, "sqrt", 0),
+ DECL_OP_NOFOLD(rand, "rand", 0),
+ //DECL_OP_NOFOLD(erf,"erf*", 0),
+ DECL_OP_NOFOLD_NOFLOAT(weight,"weight",OP_COMM),
+ DECL_OP_NOFOLD_NOFLOAT(rol,"rol",0),
+ DECL_OP_NOFOLD_NOFLOAT(ror,"ror",0),
+
+ DECL_OP_NOFOLD_FLOAT(sin, "sin", 0),
+ DECL_OP_NOFOLD_FLOAT(cos, "cos", 0),
+ DECL_OP_NOFOLD_FLOAT(atan2,"atan2", 0),
+ DECL_OP_NOFOLD_FLOAT(tanh, "tanh", 0),
+ DECL_OP_NOFOLD_FLOAT(exp, "exp", 0),
+ DECL_OP_NOFOLD_FLOAT(log, "log", 0),
+
+};
+const long op_table3_n = COUNT(op_table3);
+#endif
+#ifdef PASS4
+Numop op_table4[] = {
+ DECL_VOP(cx_mul, "C.*", OP_ASSOC|OP_COMM,2),
+ DECL_VOP(cx_mulconj, "C.*conj", OP_ASSOC|OP_COMM,2),
+ DECL_VOP(cx_div, "C./", 0,2),
+ DECL_VOP(cx_divconj, "C./conj", 0,2),
+ DECL_VOP(cx_sqsub, "C.sq-", OP_COMM,2),
+ DECL_VOP(cx_abssub, "C.abs-", OP_COMM,2),
+ DECL_VOP_NOFOLD_FLOAT(cx_sin, "C.sin", 0,2),
+ DECL_VOP_NOFOLD_FLOAT(cx_cos, "C.cos", 0,2),
+// DECL_VOP_NOFOLD_FLOAT(cx_atan2,"C.atan2",0,2),
+ DECL_VOP_NOFOLD_FLOAT(cx_tanh, "C.tanh", 0,2),
+ DECL_VOP_NOFOLD_FLOAT(cx_exp, "C.exp", 0,2),
+ DECL_VOP_NOFOLD_FLOAT(cx_log, "C.log", 0,2),
+ DECL_VOP_NOFOLD( c2p, "c2p", 0,2),
+ DECL_VOP_NOFOLD( p2c, "p2c", 0,2),
+};
+const long op_table4_n = COUNT(op_table4);
+#endif
+
+// D=dictionary, A=table, A##_n=table count.
+#define INIT_TABLE(D,A) for(int i=0; i<A##_n; i++) D[string(A[i].name)]=&A[i];
+
+#ifdef PASS1
+std::map<string,NumberType *> number_type_dict;
+std::map<string,Numop *> op_dict;
+std::map<string,Numop *> vop_dict;
+void startup_number () {
+ INIT_TABLE( op_dict,op_table1)
+ INIT_TABLE( op_dict,op_table2)
+ INIT_TABLE( op_dict,op_table3)
+ INIT_TABLE(vop_dict,op_table4)
+ INIT_TABLE(number_type_dict,number_type_table)
+
+ for (int i=0; i<COUNT(number_type_table); i++) {
+ number_type_table[i].index = (NumberTypeE) i;
+ char a[64];
+ strcpy(a,number_type_table[i].aliases);
+ char *b = strchr(a,',');
+ if (b) {
+ *b=0;
+ number_type_dict[string(b+1)]=&number_type_table[i];
+ }
+ number_type_dict[string(a)]=&number_type_table[i];
+ }
+// S:name; M:mode; F:replacement function;
+#define OVERRIDE_INT(S,M,F) { \
+ Numop *foo = op_dict[string(#S)]; \
+ foo->on_uint8.M=F; \
+ foo->on_int16.M=F; \
+ foo->on_int32.M=F; }
+ OVERRIDE_INT(ignore,map,quick_ign_map);
+ OVERRIDE_INT(ignore,zip,quick_ign_zip);
+ //OVERRIDE_INT(put,map,quick_put_map);
+ //OVERRIDE_INT(put,zip,quick_put_zip);
+ //OVERRIDE_INT(%,map,quick_mod_map); // !@#$ does that make an improvement at all?
+}
+#endif
diff --git a/externals/gridflow/src/opencv.cxx b/externals/gridflow/src/opencv.cxx
new file mode 100644
index 00000000..424d9bd0
--- /dev/null
+++ b/externals/gridflow/src/opencv.cxx
@@ -0,0 +1,537 @@
+/*
+ $Id: opencv.c 4556 2009-11-01 00:40:16Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <opencv/cv.h>
+#include <errno.h>
+
+#define cvRelease(euh) cvRelease((void **)(euh))
+#define binbuf_addv(SELF,FMT,ARGS...) binbuf_addv(SELF,const_cast<char *>(FMT),ARGS)
+#define USELIST \
+ if (a.a_type != A_LIST) RAISE("expected listatom"); \
+ t_list *b = (t_list *)a.a_gpointer; \
+ int argc = binbuf_getnatom(b); \
+ t_atom2 *argv = (t_atom2 *)binbuf_getvec(b);
+#define GETF(I) atom_getfloatarg(I,argc,argv)
+#define GETI(I) int(atom_getfloatarg(I,argc,argv))
+
+int ipl_eltype(NumberTypeE e) {
+ switch (e) {
+ case uint8_e: return IPL_DEPTH_8U;
+ // IPL_DEPTH_8S not supported
+ // IPL_DEPTH_16U not supported
+ case int16_e: return IPL_DEPTH_16S;
+ case int32_e: return IPL_DEPTH_32S;
+ case float32_e: return IPL_DEPTH_32F;
+ case float64_e: return IPL_DEPTH_64F;
+ default: RAISE("unsupported type %s",number_type_table[e].name);
+ }
+}
+
+NumberTypeE gf_ipltype(int e) {
+ switch (e) {
+ case IPL_DEPTH_8U: return uint8_e;
+ // IPL_DEPTH_8S not supported
+ // IPL_DEPTH_16U not supported
+ case IPL_DEPTH_16S: return int16_e;
+ case IPL_DEPTH_32S: return int32_e;
+ case IPL_DEPTH_32F: return float32_e;
+ case IPL_DEPTH_64F: return float64_e;
+ default: RAISE("unsupported IPL type %d",e);
+ }
+}
+
+int cv_eltype(NumberTypeE e) {
+ switch (e) {
+ case uint8_e: return CV_8U;
+ // CV_8S not supported
+ // CV_16U not supported
+ case int16_e: return CV_16S;
+ case int32_e: return CV_32S;
+ case float32_e: return CV_32F;
+ case float64_e: return CV_64F;
+ default: RAISE("unsupported type %s",number_type_table[e].name);
+ }
+}
+
+NumberTypeE gf_cveltype(int e) {
+ switch (e) {
+ case CV_8U: return uint8_e;
+ // CV_8S not supported
+ // CV_16U not supported
+ case CV_16S: return int16_e;
+ case CV_32S: return int32_e;
+ case CV_32F: return float32_e;
+ case CV_64F: return float64_e;
+ default: RAISE("unsupported CV type %d",e);
+ }
+}
+
+enum CvMode {
+ cv_mode_auto,
+ cv_mode_channels,
+ cv_mode_nochannels,
+};
+
+CvMode convert (const t_atom2 &x, CvMode *foo) {
+ if (x==gensym("auto")) return cv_mode_auto;
+ if (x==gensym("channels")) return cv_mode_channels;
+ if (x==gensym("nochannels")) return cv_mode_nochannels;
+ RAISE("invalid CvMode");
+}
+
+CvTermCriteria convert (const t_atom2 &a, CvTermCriteria *foo) {
+ USELIST;
+ CvTermCriteria tc;
+ tc.type = 0;
+ if (argc>0 && argv[0]!=gensym("nil")) {tc.type |= CV_TERMCRIT_ITER; tc.max_iter = GETI(0);}
+ if (argc>1 && argv[1]!=gensym("nil")) {tc.type |= CV_TERMCRIT_EPS ; tc.epsilon = GETF(1);}
+ if (argc>2) RAISE("invalid CvTermCriteria (too many args)");
+ //post("type=0x%08x max_iter=%d epsilon=%f",tc.type,tc.max_iter,tc.epsilon);
+ return tc;
+}
+
+void set_atom (t_atom *a, CvTermCriteria &tc) {
+ t_binbuf *b = binbuf_new();
+ if (tc.type & CV_TERMCRIT_ITER) binbuf_addv(b,"f",tc.max_iter); else binbuf_addv(b,"s",gensym("nil"));
+ if (tc.type & CV_TERMCRIT_EPS ) binbuf_addv(b,"f",tc.epsilon ); else binbuf_addv(b,"s",gensym("nil"));
+ SETLIST(a,b);
+}
+
+CvArr *cvGrid(PtrGrid g, CvMode mode, int reqdims=-1) {
+ P<Dim> d = g->dim;
+ int channels=1;
+ int dims=g->dim->n;
+ //post("mode=%d",(int)mode);
+ if (mode==cv_mode_channels && g->dim->n==0) RAISE("CV: channels dimension required for 'mode channels'");
+ if ((mode==cv_mode_auto && g->dim->n>=3) || mode==cv_mode_channels) channels=g->dim->v[--dims];
+ if (channels>64) RAISE("CV: too many channels. max 64, got %d",channels);
+ //post("channels=%d dims=%d nt=%d",channels,dims,g->nt);
+ //post("bits=%d",number_type_table[g->nt].size);
+ //if (dims==2) return cvMat(g->dim->v[0],g->dim->v[1],cv_eltype(g->nt),g->data);
+ if (reqdims>=0 && reqdims!=dims) RAISE("CV: wrong number of dimensions. expected %d, got %d", reqdims, dims);
+ if (dims==2) {
+ CvMat *a = cvCreateMatHeader(g->dim->v[0],g->dim->v[1],CV_MAKETYPE(cv_eltype(g->nt),channels));
+ cvSetData(a,g->data,g->dim->prod(1)*(number_type_table[g->nt].size/8));
+ return a;
+ }
+ if (dims==1) {
+ CvMat *a = cvCreateMatHeader(g->dim->v[0], 1,CV_MAKETYPE(cv_eltype(g->nt),channels));
+ cvSetData(a,g->data,g->dim->prod(1)*(number_type_table[g->nt].size/8));
+ return a;
+ }
+ RAISE("unsupported number of dimensions (got %d)",g->dim->n);
+ //return 0;
+}
+
+IplImage *cvImageGrid(PtrGrid g /*, CvMode mode */) {
+ P<Dim> d = g->dim;
+ if (d->n!=3) RAISE("expected 3 dimensions, got %s",d->to_s());
+ int channels=g->dim->v[2];
+ if (channels>64) RAISE("too many channels. max 64, got %d",channels);
+ CvSize size = {d->v[1],d->v[0]};
+ IplImage *a = cvCreateImageHeader(size,ipl_eltype(g->nt),channels);
+ cvSetData(a,g->data,g->dim->prod(1)*(number_type_table[g->nt].size/8));
+ return a;
+}
+
+void cvMatSend(const CvMat *self, FObject *obj, int outno, Dim *dim=0) {
+ int m = self->rows;
+ int n = self->cols;
+ int e = CV_MAT_TYPE(cvGetElemType(self));
+ int c = CV_MAT_CN( cvGetElemType(self));
+ GridOutlet *out = new GridOutlet(obj,0,dim?dim:new Dim(m,n));
+ for (int i=0; i<m; i++) {
+ uchar *meuh = cvPtr2D(self,i,0,0);
+ switch (e) {
+ case CV_8U: out->send(c*n, (uint8 *)meuh); break;
+ case CV_16S: out->send(c*n, (int16 *)meuh); break;
+ case CV_32S: out->send(c*n, (int32 *)meuh); break;
+ case CV_32F: out->send(c*n,(float32 *)meuh); break;
+ case CV_64F: out->send(c*n,(float64 *)meuh); break;
+ }
+ }
+}
+
+void set_atom (t_atom *a, CvPoint &v) {
+ t_binbuf *b = binbuf_new();
+ binbuf_addv(b,"ii",v.y,v.x);
+ SETLIST(a,b);
+}
+void set_atom (t_atom *a, CvSize &v) {
+ t_binbuf *b = binbuf_new();
+ binbuf_addv(b,"ii",v.height,v.width);
+ SETLIST(a,b);
+}
+void set_atom (t_atom *a, CvScalar &scal) {
+ t_binbuf *b = binbuf_new();
+ binbuf_addv(b,"ffff",scal.val[0],scal.val[1],scal.val[2],scal.val[3]);
+ SETLIST(a,b);
+}
+CvPoint convert (const t_atom &a, CvPoint *) {USELIST; return cvPoint( GETI(0),GETI(1));}
+CvSize convert (const t_atom &a, CvSize *) {USELIST; return cvSize( GETI(0),GETI(1));}
+CvScalar convert (const t_atom &a, CvScalar *) {USELIST; return cvScalar(GETF(0),GETF(1),GETF(2),GETF(3));}
+
+/* ******************************** CLASSES ******************************** */
+
+\class CvOp1 : FObject {
+ \attr CvMode mode;
+ \constructor (...) {mode = cv_mode_auto;}
+ /* has no default \grin 0 handler so far. */
+};
+\end class {}
+
+// from flow_objects.c
+static void snap_backstore (PtrGrid &r) {if (r.next) {r=r.next.p; r.next=0;}}
+
+\class CvOp2 : CvOp1 {
+ PtrGrid r;
+ \constructor (Grid *r=0) {this->r = r?r:new Grid(new Dim(),int32_e,true);}
+ virtual void func(CvArr *l, CvArr *r, CvArr *o) {/* rien */}
+ \grin 0
+ \grin 1
+};
+GRID_INLET(0) {
+ snap_backstore(r);
+ SAME_TYPE(in,r);
+ if (!in->dim->equal(r->dim)) RAISE("dimension mismatch: left:%s right:%s",in->dim->to_s(),r->dim->to_s());
+ in->set_chunk(0);
+} GRID_FLOW {
+ PtrGrid l = new Grid(in->dim,(T *)data);
+ PtrGrid o = new Grid(in->dim,in->nt);
+ CvArr *a = cvGrid(l,mode);
+ CvArr *b = cvGrid(r,mode);
+ CvArr *c = cvGrid(o,mode);
+ func(a,b,c);
+ cvRelease(&a);
+ cvRelease(&b);
+ cvRelease(&c);
+ out = new GridOutlet(this,0,in->dim,in->nt);
+ out->send(o->dim->prod(),(T *)o->data);
+} GRID_END
+GRID_INPUT2(1,r) {} GRID_END
+\end class {}
+
+#define FUNC(CLASS) CLASS(BFObject *bself, MESSAGE):CvOp2(bself,MESSAGE2) {} virtual void func(CvArr *l, CvArr *r, CvArr *o)
+#define HELP class_sethelpsymbol(fclass->bfclass,gensym("cv/#numop"));
+
+\class CvAdd : CvOp2 {FUNC(CvAdd) {cvAdd(l,r,o,0);}};
+\end class {install("cv/#Add",2,1); HELP}
+\class CvSub : CvOp2 {FUNC(CvSub) {cvSub(l,r,o,0);}};
+\end class {install("cv/#Sub",2,1); HELP}
+\class CvMul : CvOp2 {FUNC(CvMul) {cvMul(l,r,o,1);}};
+\end class {install("cv/#Mul",2,1); HELP}
+\class CvDiv : CvOp2 {FUNC(CvDiv) {cvDiv(l,r,o,1);}};
+\end class {install("cv/#Div",2,1); HELP}
+\class CvAnd : CvOp2 {FUNC(CvAnd) {cvAnd(l,r,o,0);}};
+\end class {install("cv/#And",2,1); HELP}
+\class CvOr : CvOp2 {FUNC(CvOr ) {cvOr( l,r,o,0);}};
+\end class {install("cv/#Or" ,2,1); HELP}
+\class CvXor : CvOp2 {FUNC(CvXor) {cvXor(l,r,o,0);}};
+\end class {install("cv/#Xor",2,1); HELP}
+
+\class CvInvert : CvOp1 {
+ \constructor () {}
+ \grin 0
+};
+GRID_INLET(0) {
+ if (in->dim->n!=2) RAISE("should have 2 dimensions");
+ if (in->dim->v[0] != in->dim->v[1]) RAISE("matrix should be square");
+ in->set_chunk(0);
+} GRID_FLOW {
+ //post("l=%p, r=%p", &*l, &*r);
+ PtrGrid l = new Grid(in->dim,(T *)data);
+ PtrGrid o = new Grid(in->dim,in->nt);
+ CvArr *a = cvGrid(l,mode);
+ CvArr *c = cvGrid(o,mode);
+ //post("a=%p, b=%p", a, b);
+ cvInvert(a,c);
+ cvRelease(&a);
+ cvRelease(&c);
+ out = new GridOutlet(this,0,in->dim,in->nt);
+ out->send(o->dim->prod(),(T *)o->data);
+} GRID_END
+\end class {install("cv/#Invert",1,1);}
+
+\class CvSVD : CvOp1 {
+ \grin 0
+ \constructor () {}
+};
+GRID_INLET(0) {
+ if (in->dim->n!=2) RAISE("should have 2 dimensions");
+ if (in->dim->v[0] != in->dim->v[1]) RAISE("matrix should be square");
+ in->set_chunk(0);
+} GRID_FLOW {
+ PtrGrid l = new Grid(in->dim,(T *)data);
+ PtrGrid o0 = new Grid(in->dim,in->nt);
+ PtrGrid o1 = new Grid(in->dim,in->nt);
+ PtrGrid o2 = new Grid(in->dim,in->nt);
+ CvArr *a = cvGrid(l,mode);
+ CvArr *c0 = cvGrid(o0,mode);
+ CvArr *c1 = cvGrid(o1,mode);
+ CvArr *c2 = cvGrid(o2,mode);
+ cvSVD(a,c0,c1,c2);
+ cvRelease(&a);
+ cvRelease(&c0);
+ cvRelease(&c1);
+ cvRelease(&c2);
+ out = new GridOutlet(this,2,in->dim,in->nt); out->send(o2->dim->prod(),(T *)o2->data);
+ out = new GridOutlet(this,1,in->dim,in->nt); out->send(o1->dim->prod(),(T *)o1->data);
+ out = new GridOutlet(this,0,in->dim,in->nt); out->send(o0->dim->prod(),(T *)o0->data);
+} GRID_END
+\end class {install("cv/#SVD",1,3);}
+
+\class CvEllipse : FObject {
+ \grin 0
+ \attr CvPoint center;
+ \attr CvSize axes;
+ \attr double angle;
+ \attr double start_angle;
+ \attr double end_angle;
+ \attr CvScalar color;
+ \attr int thickness;
+ \attr int line_type;
+ \attr int shift;
+ \constructor () {
+ center=cvPoint(0,0); axes=cvSize(0,0); angle=0; start_angle=0; end_angle=360; color=cvScalar(0);
+ thickness=1; line_type=8; shift=0;
+ }
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+ PtrGrid l = new Grid(in->dim,in->nt); COPY((T *)*l,data,in->dim->prod());
+ IplImage *img = cvImageGrid(l);
+ cvEllipse(img,center,axes,angle,start_angle,end_angle,color,thickness,line_type,shift);
+ cvReleaseImageHeader(&img);
+ out = new GridOutlet(this,0,in->dim,in->nt); out->send(in->dim->prod(),(T *)*l);
+} GRID_END
+\end class {install("cv/#Ellipse",1,2);}
+
+\class CvApproxPoly : CvOp1 {
+ \grin 0
+ \attr int accuracy;
+ \attr bool closed;
+ CvMemStorage* storage;
+ \constructor () {closed=true; storage = cvCreateMemStorage(0);}
+ ~CvApproxPoly () {cvReleaseMemStorage(&storage);}
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+ PtrGrid l = new Grid(in->dim,(T *)data); CvArr *a = cvGrid(l,mode);
+ CvSeq *seq = cvApproxPoly(a,sizeof(CvMat),storage,CV_POLY_APPROX_DP,accuracy,closed);
+ seq=seq; //blah
+} GRID_END
+\end class {install("cv/#ApproxPoly",1,1);}
+
+\class CvCalcOpticalFlowHS : CvOp1 {
+ \grin 0
+ \attr double lambda;
+ //\attr CvTermCriteria criteria;
+ \constructor () {}
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+// cvCalcOpticalFlowHS(prev,curr,use_previous, CvArr* velx, CvArr* vely, lambda, CvTermCriteria criteria );
+} GRID_END
+\end class {install("cv/#CalcOpticalFlowHS",1,1);}
+\class CvCalcOpticalFlowLK : CvOp1 {
+ \grin 0
+ \constructor () {}
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+} GRID_END
+\end class {install("cv/#CalcOpticalFlowLK",1,1);}
+\class CvCalcOpticalFlowBM : CvOp1 {
+ \grin 0
+ \constructor () {}
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+} GRID_END
+\end class {install("cv/#CalcOpticalFlowBM",1,1);}
+\class CvCalcOpticalFlowPyrLK : CvOp1 {
+ \grin 0
+ \constructor () {}
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+} GRID_END
+\end class {install("cv/#CalcOpticalFlowPyrLK",1,1);}
+
+/*
+void cvCalcOpticalFlowLK(const CvArr* prev, const CvArr* curr, CvSize win_size, CvArr* velx, CvArr* vely);
+void cvCalcOpticalFlowBM(const CvArr* prev, const CvArr* curr, CvSize block_size, CvSize shift_size, CvSize max_range, int use_previous,
+ CvArr* velx, CvArr* vely);
+void cvCalcOpticalFlowPyrLK(const CvArr* prev, const CvArr* curr, CvArr* prev_pyr, CvArr* curr_pyr,
+ const CvPoint2D32f* prev_features, CvPoint2D32f* curr_features,
+ int count, CvSize win_size, int level, char* status,
+ float* track_error, CvTermCriteria criteria, int flags );
+void cvCalcBackProject( IplImage** image, CvArr* back_project, const CvHistogram* hist );
+void cvCalcHist( IplImage** image, CvHistogram* hist, int accumulate=0, const CvArr* mask=NULL );
+CvHistogram* cvCreateHist( int dims, int* sizes, int type, float** ranges=NULL, int uniform=1 );
+void cvSnakeImage( const IplImage* image, CvPoint* points, int length, float* alpha, float* beta, float* gamma, int coeff_usage,
+ CvSize win, CvTermCriteria criteria, int calc_gradient=1 );
+int cvMeanShift( const CvArr* prob_image, CvRect window, CvTermCriteria criteria, CvConnectedComp* comp );
+int cvCamShift( const CvArr* prob_image, CvRect window, CvTermCriteria criteria, CvConnectedComp* comp, CvBox2D* box=NULL );
+*/
+
+/* ******************************** UNFINISHED ******************************** */
+
+\class CvSplit : CvOp1 {
+ int channels;
+ \constructor (int channels) {
+ if (channels<0 || channels>64) RAISE("channels=%d is not in 1..64",channels);
+ this->channels = channels;
+ bself->noutlets_set(channels);
+ }
+};
+\end class {}
+
+\class CvHaarDetectObjects : FObject {
+ \attr double scale_factor; /*=1.1*/
+ \attr int min_neighbors; /*=3*/
+ \attr int flags; /*=0*/
+ \constructor () {
+ scale_factor=1.1;
+ min_neighbors=3;
+ flags=0;
+ //cascade = cvLoadHaarClassifierCascade("<default_face_cascade>",cvSize(24,24));
+ const char *filename = OPENCV_SHARE_PATH "/haarcascades/haarcascade_frontalface_alt2.xml";
+ FILE *f = fopen(filename,"r");
+ if (!f) RAISE("error opening %s: %s",filename,strerror(errno));
+ fclose(f);
+ cascade = (CvHaarClassifierCascade *)cvLoad(filename,0,0,0);
+ int s = cvGetErrStatus();
+ post("cascade=%p, cvGetErrStatus=%d cvErrorStr=%s",cascade,s,cvErrorStr(s));
+ //cascade = cvLoadHaarClassifierCascade(OPENCV_SHARE_PATH "/data/haarcascades/haarcascade_frontalface_alt2.xml",cvSize(24,24));
+ storage = cvCreateMemStorage(0);
+ }
+ CvHaarClassifierCascade *cascade;
+ CvMemStorage *storage;
+ \grin 0
+};
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+ PtrGrid l = new Grid(in->dim,(T *)data);
+ IplImage *img = cvImageGrid(l);
+ CvSeq *ret = cvHaarDetectObjects(img,cascade,storage,scale_factor,min_neighbors,flags);
+ int n = ret ? ret->total : 0;
+ out = new GridOutlet(this,0,new Dim(n,2,2));
+ for (int i=0; i<n; i++) {
+ CvRect *r = (CvRect *)cvGetSeqElem(ret,i);
+ int32 duh[] = {r->y,r->x,r->y+r->height,r->x+r->width};
+ out->send(4,duh);
+ }
+} GRID_END
+\end class {install("cv/#HaarDetectObjects",2,1);}
+
+/* **************************************************************** */
+
+\class CvKMeans : CvOp1 {
+ \attr int numClusters;
+ \attr CvTermCriteria termcrit;
+ \grin 0 float32
+ \decl 1 float (int v);
+ \constructor (int v) {
+ _1_float(0,0,v);
+ termcrit = CvTermCriteria();
+ }
+};
+
+\def 1 float (int v) {numClusters = v;}
+
+//post("typeof(a)=%p typeof(c)=%p typeof(CvMat)=%p",cvTypeOf(a),cvTypeOf(c),cvFindType("opencv-matrix"));
+//for (CvTypeInfo *t = cvFirstType(); t; t=t->next) post("type %s",t->type_name);
+
+GRID_INLET(0) {
+ if (in->dim->n<1) RAISE("should have at least 1 dimension");
+ in->set_chunk(0);
+} GRID_FLOW {
+ int32 v[] = {in->dim->prod(0)/in->dim->prod(-1),in->dim->prod(-1)};
+ PtrGrid l = new Grid(new Dim(2,v),(T *)data);
+ CvArr *a = (CvMat *)cvGrid(l,mode,2);
+ PtrGrid o = new Grid(new Dim(1,v),int32_e);
+ CvArr *c = (CvMat *)cvGrid(o,mode);
+ cvKMeans2(a,numClusters,c,termcrit);
+ int w[in->dim->n];
+ COPY(w,in->dim->v,in->dim->n);
+ w[in->dim->n-1] = 1;
+ P<Dim> d = new Dim(in->dim->n,w);
+ out = new GridOutlet(this,0,d);
+ out->send(v[0],(int32 *)*o);
+ cvRelease(&a);
+ cvRelease(&c);
+} GRID_END
+
+\end class {install("cv/#KMeans",2,1);}
+
+\class CvCornerHarris : CvOp1 {
+ \attr int block_size;
+ \attr int aperture_size;
+ \attr double k;
+ \constructor () {
+ block_size = 3;
+ aperture_size = 3;
+ k = 0.04;
+ }
+ \grin 0
+};
+
+GRID_INLET(0) {
+ in->set_chunk(0);
+} GRID_FLOW {
+ PtrGrid l = new Grid(in->dim,(T *)data);
+ CvArr *a = (CvMat *)cvGrid(l,mode,2);
+ PtrGrid o = new Grid(in->dim,float32_e);
+ CvArr *c = (CvMat *)cvGrid(o,mode);
+ cvCornerHarris(a,c,block_size,aperture_size,k);
+ cvRelease(&a);
+ cvRelease(&c);
+ out = new GridOutlet(this,0,in->dim,in->nt); out->send(o->dim->prod(),(T *)o->data);
+} GRID_END
+
+\end class {install("cv/#CornerHarris",1,1);}
+
+/* **************************************************************** */
+
+static int erreur_handleur (int status, const char* func_name, const char* err_msg, const char* file_name, int line, void *userdata) {
+ cvSetErrStatus(CV_StsOk);
+ // we might be looking for trouble because we don't know whether OpenCV is throw-proof.
+ RAISE("OpenCV error: status='%s' func_name=%s err_msg=\"%s\" file_name=%s line=%d",cvErrorStr(status),func_name,err_msg,file_name,line);
+ // if this breaks OpenCV, then we will have to use post() or a custom hybrid of post() and RAISE() that does not cause a
+ // longjmp when any OpenCV functions are on the stack.
+ return 0;
+}
+
+void startup_opencv() {
+ /* CvErrorCallback z = */ cvRedirectError(erreur_handleur);
+ \startall
+}
diff --git a/externals/gridflow/src/png.cxx b/externals/gridflow/src/png.cxx
new file mode 100644
index 00000000..d61361c5
--- /dev/null
+++ b/externals/gridflow/src/png.cxx
@@ -0,0 +1,143 @@
+/*
+ $Id: png.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* !@#$ not handling abort on compress */
+/* !@#$ not handling abort on decompress */
+
+#include "gridflow.hxx.fcs"
+extern "C" {
+#include <libpng12/png.h>
+};
+
+\class FormatPNG : Format {
+ P<BitPacking> bit_packing;
+ png_structp png;
+ png_infop info;
+ \constructor (t_symbol *mode, string filename) {
+ Format::_0_open(0,0,mode,filename);
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+ }
+ \decl 0 bang ();
+ \grin 0 int
+};
+
+GRID_INLET(0) {
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions: rows,columns,channels");
+ int c = in->dim->get(2);
+ if (c!=3) RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_chunk(0);
+} GRID_FLOW {
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png) RAISE("!png");
+ info = png_create_info_struct(png);
+ if (!info) {if (png) png_destroy_write_struct(&png, NULL); RAISE("!info");}
+ if (setjmp(png_jmpbuf(png))) {png_destroy_write_struct(&png, &info); RAISE("png write error");}
+ if (setjmp(png->jmpbuf)) {png_write_destroy(png); free(png); free(info); RAISE("png write error");}
+ png_init_io(png, f);
+ info->width = in->dim->get(1);
+ info->height = in->dim->get(0);
+ info->bit_depth = 8;
+// info->color_type = channels==3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY;
+ info->color_type = PNG_COLOR_TYPE_RGB;
+// info->color_type |= PNG_COLOR_MASK_ALPHA;
+ info->interlace_type = 1;
+ png_write_info(png,info);
+ png_set_packing(png);
+// this would have been the GRID_FLOW section
+ int rowsize = in->dim->get(1)*in->dim->get(2);
+ int rowsize2 = in->dim->get(1)*3;
+ uint8 row[rowsize2];
+ while (n) {
+ post("n=%ld",long(n));
+ bit_packing->pack(in->dim->get(1),data,row);
+ png_write_row(png,row);
+ n-=rowsize; data+=rowsize;
+ }
+// this would have been the GRID_FINISH section
+ post("GRID FINISH 1");
+ png_write_end(png,info);
+ post("GRID FINISH 2");
+ png_write_destroy(png);
+ post("GRID FINISH 3");
+ fflush(f);
+ free(png);
+ free(info);
+ fclose(f);
+} GRID_FINISH {
+} GRID_END
+
+\def 0 bang () {
+ uint8 sig[8];
+ if (!fread(sig, 1, 8, f)) {outlet_bang(bself->te_outlet); return;}
+ if (!png_check_sig(sig, 8)) RAISE("bad signature");
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png) RAISE("!png");
+ info = png_create_info_struct(png);
+ if (!info) {png_destroy_read_struct(&png, NULL, NULL); RAISE("!info");}
+ if (setjmp(png_jmpbuf(png))) {png_destroy_read_struct(&png, &info, NULL); RAISE("png read error");}
+ png_init_io(png, f);
+ png_set_sig_bytes(png, 8); // we already read the 8 signature bytes
+ png_read_info(png, info); // read all PNG info up to image data
+ png_uint_32 width, height;
+ int bit_depth, color_type;
+ png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, 0,0,0);
+
+ png_bytepp row_pointers = 0;
+ if (color_type == PNG_COLOR_TYPE_PALETTE
+ || (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ || png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_expand(png);
+ // 16bpp y, 32bpp ya, 48bpp rgb, 64bpp rgba...
+ if (bit_depth == 16) png_set_strip_16(png);
+
+ double display_gamma = 2.2;
+ double gamma;
+ if (png_get_gAMA(png, info, &gamma))
+ png_set_gamma(png, display_gamma, gamma);
+ png_read_update_info(png, info);
+
+ int rowbytes = png_get_rowbytes(png, info);
+ int channels = (int)png_get_channels(png, info);
+ uint8 *image_data = new uint8[rowbytes*height];
+ row_pointers = new png_bytep[height];
+ //gfpost("png: color_type=%d channels=%d, width=%d, rowbytes=%ld, height=%ld, gamma=%f",
+ // color_type, channels, width, rowbytes, height, gamma);
+ for (int i=0; i<(int)height; i++) row_pointers[i] = image_data + i*rowbytes;
+ if ((uint32)rowbytes != width*channels)
+ RAISE("rowbytes mismatch: %d is not %d*%d=%d", rowbytes, width, channels, width*channels);
+ png_read_image(png, row_pointers);
+ delete[] row_pointers;
+ row_pointers = 0;
+ png_read_end(png, 0);
+ GridOutlet out(this,0,new Dim(height, width, channels), cast);
+ out.send(rowbytes*height,image_data);
+ delete[] image_data;
+ png_destroy_read_struct(&png, &info, NULL);
+}
+
+\classinfo {install_format("#io.png",4,"png");}
+\end class FormatPNG
+void startup_png () {
+ \startall
+}
diff --git a/externals/gridflow/src/pwc-ioctl.h b/externals/gridflow/src/pwc-ioctl.h
new file mode 100644
index 00000000..65805eaa
--- /dev/null
+++ b/externals/gridflow/src/pwc-ioctl.h
@@ -0,0 +1,292 @@
+#ifndef PWC_IOCTL_H
+#define PWC_IOCTL_H
+
+/* (C) 2001-2004 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* This is pwc-ioctl.h belonging to PWC 8.12.1
+ It contains structures and defines to communicate from user space
+ directly to the driver.
+ */
+
+/*
+ Changes
+ 2001/08/03 Alvarado Added ioctl constants to access methods for
+ changing white balance and red/blue gains
+ 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
+ 2003/12/13 Nemosft Unv. Some modifications to make interfacing to
+ PWCX easier
+ */
+
+/* These are private ioctl() commands, specific for the Philips webcams.
+ They contain functions not found in other webcams, and settings not
+ specified in the Video4Linux API.
+
+ The #define names are built up like follows:
+ VIDIOC VIDeo IOCtl prefix
+ PWC Philps WebCam
+ G optional: Get
+ S optional: Set
+ ... the function
+ */
+
+
+ /* Enumeration of image sizes */
+#define PSZ_SQCIF 0x00
+#define PSZ_QSIF 0x01
+#define PSZ_QCIF 0x02
+#define PSZ_SIF 0x03
+#define PSZ_CIF 0x04
+#define PSZ_VGA 0x05
+#define PSZ_MAX 6
+
+
+/* The frame rate is encoded in the video_window.flags parameter using
+ the upper 16 bits, since some flags are defined nowadays. The following
+ defines provide a mask and shift to filter out this value.
+
+ In 'Snapshot' mode the camera freezes its automatic exposure and colour
+ balance controls.
+ */
+#define PWC_FPS_SHIFT 16
+#define PWC_FPS_MASK 0x00FF0000
+#define PWC_FPS_FRMASK 0x003F0000
+#define PWC_FPS_SNAPSHOT 0x00400000
+
+
+/* structure for transfering x & y coordinates */
+struct pwc_coord
+{
+ int x, y; /* guess what */
+ int size; /* size, or offset */
+};
+
+
+/* Used with VIDIOCPWCPROBE */
+struct pwc_probe
+{
+ char name[32];
+ int type;
+};
+
+struct pwc_serial
+{
+ char serial[30]; /* String with serial number. Contains terminating 0 */
+};
+
+/* pwc_whitebalance.mode values */
+#define PWC_WB_INDOOR 0
+#define PWC_WB_OUTDOOR 1
+#define PWC_WB_FL 2
+#define PWC_WB_MANUAL 3
+#define PWC_WB_AUTO 4
+
+/* Used with VIDIOCPWC[SG]AWB (Auto White Balance).
+ Set mode to one of the PWC_WB_* values above.
+ *red and *blue are the respective gains of these colour components inside
+ the camera; range 0..65535
+ When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read;
+ otherwise undefined.
+ 'read_red' and 'read_blue' are read-only.
+*/
+struct pwc_whitebalance
+{
+ int mode;
+ int manual_red, manual_blue; /* R/W */
+ int read_red, read_blue; /* R/O */
+};
+
+/*
+ 'control_speed' and 'control_delay' are used in automatic whitebalance mode,
+ and tell the camera how fast it should react to changes in lighting, and
+ with how much delay. Valid values are 0..65535.
+*/
+struct pwc_wb_speed
+{
+ int control_speed;
+ int control_delay;
+
+};
+
+/* Used with VIDIOCPWC[SG]LED */
+struct pwc_leds
+{
+ int led_on; /* Led on-time; range = 0..25000 */
+ int led_off; /* Led off-time; range = 0..25000 */
+};
+
+/* Image size (used with GREALSIZE) */
+struct pwc_imagesize
+{
+ int width;
+ int height;
+};
+
+/* Defines and structures for Motorized Pan & Tilt */
+#define PWC_MPT_PAN 0x01
+#define PWC_MPT_TILT 0x02
+#define PWC_MPT_TIMEOUT 0x04 /* for status */
+
+/* Set angles; when absolute != 0, the angle is absolute and the
+ driver calculates the relative offset for you. This can only
+ be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
+ absolute angles.
+ */
+struct pwc_mpt_angles
+{
+ int absolute; /* write-only */
+ int pan; /* degrees * 100 */
+ int tilt; /* degress * 100 */
+};
+
+/* Range of angles of the camera, both horizontally and vertically.
+ */
+struct pwc_mpt_range
+{
+ int pan_min, pan_max; /* degrees * 100 */
+ int tilt_min, tilt_max;
+};
+
+struct pwc_mpt_status
+{
+ int status;
+ int time_pan;
+ int time_tilt;
+};
+
+
+/* This is used for out-of-kernel decompression. With it, you can get
+ all the necessary information to initialize and use the decompressor
+ routines in standalone applications.
+ */
+struct pwc_video_command
+{
+ int type; /* camera type (645, 675, 730, etc.) */
+ int release; /* release number */
+
+ int size; /* one of PSZ_* */
+ int alternate;
+ int command_len; /* length of USB video command */
+ unsigned char command_buf[13]; /* Actual USB video command */
+ int bandlength; /* >0 = compressed */
+ int frame_size; /* Size of one (un)compressed frame */
+};
+
+/* Flags for PWCX subroutines. Not all modules honour all flags. */
+#define PWCX_FLAG_PLANAR 0x0001
+#define PWCX_FLAG_BAYER 0x0008
+
+
+/* IOCTL definitions */
+
+ /* Restore user settings */
+#define VIDIOCPWCRUSER _IO('v', 192)
+ /* Save user settings */
+#define VIDIOCPWCSUSER _IO('v', 193)
+ /* Restore factory settings */
+#define VIDIOCPWCFACTORY _IO('v', 194)
+
+ /* You can manipulate the compression factor. A compression preference of 0
+ means use uncompressed modes when available; 1 is low compression, 2 is
+ medium and 3 is high compression preferred. Of course, the higher the
+ compression, the lower the bandwidth used but more chance of artefacts
+ in the image. The driver automatically chooses a higher compression when
+ the preferred mode is not available.
+ */
+ /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */
+#define VIDIOCPWCSCQUAL _IOW('v', 195, int)
+ /* Get preferred compression quality */
+#define VIDIOCPWCGCQUAL _IOR('v', 195, int)
+
+
+/* Retrieve serial number of camera */
+#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial)
+
+ /* This is a probe function; since so many devices are supported, it
+ becomes difficult to include all the names in programs that want to
+ check for the enhanced Philips stuff. So in stead, try this PROBE;
+ it returns a structure with the original name, and the corresponding
+ Philips type.
+ To use, fill the structure with zeroes, call PROBE and if that succeeds,
+ compare the name with that returned from VIDIOCGCAP; they should be the
+ same. If so, you can be assured it is a Philips (OEM) cam and the type
+ is valid.
+ */
+#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe)
+
+ /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
+#define VIDIOCPWCSAGC _IOW('v', 200, int)
+ /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */
+#define VIDIOCPWCGAGC _IOR('v', 200, int)
+ /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */
+#define VIDIOCPWCSSHUTTER _IOW('v', 201, int)
+
+ /* Color compensation (Auto White Balance) */
+#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance)
+#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance)
+
+ /* Auto WB speed */
+#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed)
+#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed)
+
+ /* LEDs on/off/blink; int range 0..65535 */
+#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds)
+#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds)
+
+ /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */
+#define VIDIOCPWCSCONTOUR _IOW('v', 206, int)
+#define VIDIOCPWCGCONTOUR _IOR('v', 206, int)
+
+ /* Backlight compensation; 0 = off, otherwise on */
+#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int)
+#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int)
+
+ /* Flickerless mode; = 0 off, otherwise on */
+#define VIDIOCPWCSFLICKER _IOW('v', 208, int)
+#define VIDIOCPWCGFLICKER _IOR('v', 208, int)
+
+ /* Dynamic noise reduction; 0 off, 3 = high noise reduction */
+#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int)
+#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int)
+
+ /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
+#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize)
+
+ /* Motorized pan & tilt functions */
+#define VIDIOCPWCMPTRESET _IOW('v', 211, int)
+#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range)
+#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles)
+#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles)
+#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status)
+
+ /* Get the USB set-video command; needed for initializing libpwcx */
+#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command)
+struct pwc_table_init_buffer {
+ int len;
+ char *buffer;
+
+};
+#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer)
+
+#endif
diff --git a/externals/gridflow/src/quartz.m b/externals/gridflow/src/quartz.m
new file mode 100644
index 00000000..91bcb84d
--- /dev/null
+++ b/externals/gridflow/src/quartz.m
@@ -0,0 +1,224 @@
+/*
+ $Id: quartz.m 4517 2009-10-30 16:01:30Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2008 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+ This is written in Objective C++, which is the union of C++ and Objective C;
+ Their intersection is C or almost. They add quite different sets of features.
+ I need Objective C here because the Cocoa API is for Objective C and Java only,
+ and the Objective C one was the easiest to integrate in GridFlow.
+
+ The next best possibility may be using RubyCocoa, a port of the Cocoa API to Ruby;
+ However I haven't checked whether Quartz is wrapped, and how easy it is to
+ process images.
+*/
+
+#include <stdio.h>
+#include <objc/Object.h>
+#include <Cocoa/Cocoa.h>
+
+#include "gridflow.hxx.fcs"
+
+@interface GFView: NSView {
+ uint8 *imdata;
+ int imwidth;
+ int imheight;
+}
+- (id) drawRect: (NSRect)rect;
+- (id) imageHeight: (int)w width: (int)h;
+- (int) imageHeight;
+- (int) imageWidth;
+- (uint8 *) imageData;
+- (int) imageDataSize;
+@end
+
+@implementation GFView
+
+- (uint8 *) imageData {return imdata;}
+- (int) imageDataSize {return imwidth*imheight*4;}
+- (int) imageHeight {return imheight;}
+- (int) imageWidth {return imwidth;}
+
+- (id) imageHeight: (int)h width: (int)w {
+ if (imheight==h && imwidth==w) return self;
+ //post("new size: y=%d x=%d",h,w);
+ imheight=h;
+ imwidth=w;
+ if (imdata) delete imdata;
+ int size = [self imageDataSize];
+ imdata = new uint8[size];
+ CLEAR(imdata,size);
+ NSSize s = {w,h};
+ [[self window] setContentSize: s];
+ return self;
+}
+
+- (id) initWithFrame: (NSRect)r {
+ [super initWithFrame: r];
+ imdata=0; imwidth=-1; imheight=-1;
+ [self imageHeight: 240 width: 320];
+ return self;
+}
+
+- (id) drawRect: (NSRect)rect {
+ [super drawRect: rect];
+ if (![self lockFocusIfCanDraw]) return self;
+ CGContextRef g = (CGContextRef)
+ [[NSGraphicsContext graphicsContextWithWindow: [self window]]
+ graphicsPort];
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ CGDataProviderRef dp = CGDataProviderCreateWithData(
+ NULL, imdata, imheight*imwidth*4, NULL);
+ CGImageRef image = CGImageCreate(imwidth, imheight, 8, 32, imwidth*4,
+ cs, kCGImageAlphaFirst, dp, NULL, 0, kCGRenderingIntentDefault);
+ CGDataProviderRelease(dp);
+ CGColorSpaceRelease(cs);
+ CGRect rectangle = CGRectMake(0,0,imwidth,imheight);
+ CGContextDrawImage(g,rectangle,image);
+ CGImageRelease(image);
+ [self unlockFocus];
+ return self;
+}
+@end
+
+/* workaround: bus error in gcc */
+uint8 *GFView_imageData(GFView *self) {return (uint8 *)[self imageData];}
+
+void GFView_imageHeight_width(GFView *self, int height, int width) {
+ [self imageHeight: height width: width];
+}
+
+void GFView_display(GFView *self) {
+ NSRect r = {{0,0},{[self imageHeight],[self imageWidth]}};
+ [self displayRect: r];
+ [self setNeedsDisplay: YES];
+ [self display];
+}
+
+struct FormatQuartz;
+void FormatQuartz_call(FormatQuartz *self);
+
+\class FormatQuartz : Format {
+ NSWindow *window;
+ NSWindowController *wc;
+ GFView *widget; /* GridFlow's Cocoa widget */
+ t_clock *clock;
+ \constructor (t_symbol *mode) {
+ NSRect r = {{0,0}, {320,240}};
+ window = [[NSWindow alloc]
+ initWithContentRect: r
+ styleMask: NSTitledWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask
+ backing: NSBackingStoreBuffered
+ defer: YES];
+ widget = [[GFView alloc] initWithFrame: r];
+ [window setContentView: widget];
+ [window setTitle: @"GridFlow"];
+ [window makeKeyAndOrderFront: NSApp];
+ [window orderFrontRegardless];
+ wc = [[NSWindowController alloc] initWithWindow: window];
+ clock = clock_new(this,(t_method)FormatQuartz_call);
+ [window makeFirstResponder: widget];
+ //post("mainWindow = %08lx",(long)[NSApp mainWindow]);
+ //post(" keyWindow = %08lx",(long)[NSApp keyWindow]);
+ NSColor *color = [NSColor clearColor];
+ [window setBackgroundColor: color];
+ }
+ ~FormatQuartz () {
+ clock_unset(clock);
+ clock_free(clock);
+ clock = 0;
+ [window autorelease];
+ [window setReleasedWhenClosed: YES];
+ [window close];
+ }
+ void call ();
+ \grin 0
+};
+
+static NSDate *distantFuture, *distantPast;
+
+void FormatQuartz::call() {
+ NSEvent *e = [NSApp nextEventMatchingMask: NSAnyEventMask
+ // untilDate: distantFuture // blocking
+ untilDate: distantPast // nonblocking
+ inMode: NSDefaultRunLoopMode
+ dequeue: YES];
+ if (e) {
+ NSLog(@"%@", e);
+ [NSApp sendEvent: e];
+ }
+ [NSApp updateWindows];
+ [this->window flushWindowIfNeeded];
+ clock_delay(clock,20);
+}
+void FormatQuartz_call(FormatQuartz *self) {self->call();}
+
+template <class T, class S>
+static void convert_number_type(int n, T *out, S *in) {
+ for (int i=0; i<n; i++) out[i]=(T)in[i];
+}
+
+GRID_INLET(0) {
+ if (in->dim->n!=3) RAISE("expecting 3 dims, not %d", in->dim->n);
+ int c=in->dim->get(2);
+ if (c!=3&&c!=4) RAISE("expecting 3 or 4 channels, not %d", in->dim->get(2));
+// [widget imageHeight: in->dim->get(0) width: in->dim->get(1) ];
+ GFView_imageHeight_width(widget,in->dim->get(0),in->dim->get(1));
+ in->set_chunk(1);
+} GRID_FLOW {
+ int off = dex/in->dim->prod(2);
+ int c=in->dim->get(2);
+ NSView *w = widget;
+ uint8 *data2 = GFView_imageData(w)+off*4;
+// convert_number_type(n,data2,data);
+ if (c==3) {
+ while(n) {
+ data2[0]=255;
+ data2[1]=(uint8)data[0];
+ data2[2]=(uint8)data[1];
+ data2[3]=(uint8)data[2];
+ data+=3; data2+=4; n-=3;
+ }
+ } else {
+ while(n) {
+ data2[0]=255;
+ data2[1]=(uint8)data[0];
+ data2[2]=(uint8)data[1];
+ data2[3]=(uint8)data[2];
+ data+=4; data2+=4; n-=4;
+ }
+ }
+} GRID_FINISH {
+ GFView_display(widget);
+} GRID_END
+
+\end class FormatQuartz {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ distantFuture = [NSDate distantFuture];
+ distantPast = [NSDate distantPast];
+ [NSApplication sharedApplication];
+ install_format("#io.quartz",2,"");
+}
+void startup_quartz () {
+ \startall
+}
+
diff --git a/externals/gridflow/src/quicktimeapple.cxx b/externals/gridflow/src/quicktimeapple.cxx
new file mode 100644
index 00000000..9b32b85e
--- /dev/null
+++ b/externals/gridflow/src/quicktimeapple.cxx
@@ -0,0 +1,456 @@
+/*
+ $Id: quicktimeapple.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <QuickTime/QuickTime.h>
+#include <QuickTime/Movies.h>
+#include <QuickTime/QuickTimeComponents.h>
+#include "gridflow.hxx.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <CoreServices/CoreServices.h>
+
+typedef ComponentInstance VideoDigitizerComponent, VDC;
+typedef ComponentResult VideoDigitizerError, VDE;
+
+#if 0
+//enum {VDCType='vdig', vdigInterfaceRev=2 };
+//enum {ntscIn=0, currentIn=0, palIn, secamIn, ntscReallyIn };
+//enum {compositeIn, sVideoIn, rgbComponentIn, rgbComponentSyncIn, yuvComponentIn, yuvComponentSyncIn, tvTunerIn, sdiIn};
+//enum {vdPlayThruOff, vdPlayThruOn};
+//enum {vdDigitizerBW, vdDigitizerRGB};
+//enum {vdBroadcastMode, vdVTRMode};
+//enum {vdUseAnyField, vdUseOddField, vdUseEvenField};
+//enum {vdTypeBasic, vdTypeAlpha, vdTypeMask, vdTypeKey};
+/*enum {digiInDoesNTSC, digiInDoesPAL, digiInDoesSECAM, skip 4,
+ digiInDoesGenLock, digiInDoesComposite, digiInDoesSVideo, digiInDoesComponent,
+ digiInVTR_Broadcast, digiInDoesColor, digiInDoesBW, skip 17,
+ digiInSignalLock};*/
+/*bitset {digiOutDoes1, digiOutDoes2, digiOutDoes4,
+ digiOutDoes8, digiOutDoes16, digiOutDoes32,
+ digiOutDoesDither, digiOutDoesStretch, digiOutDoesShrink,
+ digiOutDoesMask, skip 1,
+ digiOutDoesDouble, digiOutDoesQuad, digiOutDoesQuarter, digiOutDoesSixteenth,
+ digiOutDoesRotate, digiOutDoesHorizFlip, digiOutDoesVertFlip, digiOutDoesSkew,
+ digiOutDoesBlend, digiOutDoesWarp, digiOutDoesHW_DMA,
+ digiOutDoesHWPlayThru, digiOutDoesILUT, digiOutDoesKeyColor,
+ digiOutDoesAsyncGrabs, digiOutDoesUnreadableScreenBits,
+ digiOutDoesCompress, digiOutDoesCompressOnly,
+ digiOutDoesPlayThruDuringCompress, digiOutDoesCompressPartiallyVisible,
+ digiOutDoesNotNeedCopyOfCompressData};*/
+/*struct DigitizerInfo {
+ short vdigType;
+ long inputCapabilityFlags, outputCapabilityFlags;
+ long inputCurrentFlags, outputCurrentFlags;
+ short slot;
+ GDHandle gdh, maskgdh;
+ short minDestHeight, minDestWidth;
+ short maxDestHeight, maxDestWidth;
+ short blendLevels;
+ long reserved;};*/
+/*struct VdigType { long digType, reserved;};*/
+/*struct VdigTypeList { short count; VdigType list[1];};*/
+/*struct VdigBufferRec { PixMapHandle dest; Point location; long reserved;};*/
+/*struct VdigBufferRecList {
+ short count; MatrixRecordPtr matrix; RgnHandle mask; VdigBufferRec list[1];};*/
+//typedef VdigBufferRecList *VdigBufferRecListPtr;
+//typedef VdigBufferRecListPtr *VdigBufferRecListHandle;
+//typedef CALLBACK_API(void,VdigIntProcPtr)(long flags, long refcon);
+//typedef STACK_UPP_TYPE(VdigIntProcPtr);
+/*struct VDCompressionList {
+ CodecComponent codec; CodecType cType; Str63 typeName, name;
+ long formatFlags, compressFlags, reserved;};*/
+//typedef VDCompressionList * VDCompressionListPtr;
+//typedef VDCompressionListPtr *VDCompressionListHandle;
+/*bitset {
+ dmaDepth1, dmaDepth2, dmaDepth4, dmaDepth8, dmaDepth16, dmaDepth32,
+ dmaDepth2Gray, dmaDepth4Gray, dmaDepth8Gray};*/
+//enum {kVDIGControlledFrameRate=-1};
+//bitset {vdDeviceFlagShowInputsAsDevices, vdDeviceFlagHideDevice};
+/*bitset {
+ vdFlagCaptureStarting, vdFlagCaptureStopping,
+ vdFlagCaptureIsForPreview, vdFlagCaptureIsForRecord,
+ vdFlagCaptureLowLatency, vdFlagCaptureAlwaysUseTimeBase,
+ vdFlagCaptureSetSettingsBegin, vdFlagCaptureSetSettingsEnd};*/
+/*\class VDC
+VDE VDGetMaxSrcRect (short inputStd, Rect *maxSrcRect)
+VDE VDGetActiveSrcRect(short inputStd, Rect *activeSrcRect)
+VDE VD[GS]etDigitizerRect(Rect *digitizerRect)
+VDE VDGetVBlankRect(short inputStd, Rect *vBlankRect)
+VDE VDGetMaskPixMap(PixMapHandlemaskPixMap)
+VDE VDGetPlayThruDestination(PixMapHandle * dest, Rect *destRect, MatrixRecord * m, RgnHandle *mask)
+VDE VDUseThisCLUT(CTabHandle colorTableHandle)
+VDE VD[SG*]etInputGammaValue(Fixed channel1, Fixed channel2, Fixed channel3)
+VDE VD[GS]etBrightness(uint16 *)
+VDE VD[GS]etContrast(uint16 *)
+VDE VD[GS]etHue(uint16 *)
+VDE VD[GS]etSharpness(uint16 *)
+VDE VD[GS]etSaturation(uint16 *)
+VDE VDGrabOneFrame(VDC ci)
+VDE VDGetMaxAuxBuffer(PixMapHandle *pm, Rect *r)
+VDE VDGetDigitizerInfo(DigitizerInfo *info)
+VDE VDGetCurrentFlags(long *inputCurrentFlag, long *outputCurrentFlag)
+VDE VD[SG*]etKeyColor(long index)
+VDE VDAddKeyColor(long *index)
+VDE VDGetNextKeyColor(long index)
+VDE VD[GS]etKeyColorRange(RGBColor minRGB, RGBColor maxRGB)
+VDE VDSetDigitizerUserInterrupt(long flags, VdigIntUPP userInterruptProc, long refcon)
+VDE VD[SG*]etInputColorSpaceMode(short colorSpaceMode)
+VDE VD[SG*]etClipState(short clipEnable)
+VDE VDSetClipRgn(RgnHandle clipRegion)
+VDE VDClearClipRgn(RgnHandle clipRegion)
+VDE VDGetCLUTInUse(CTabHandle *colorTableHandle)
+VDE VD[SG*]etPLLFilterType(short pllType)
+VDE VDGetMaskandValue(uint16 blendLevel, long *mask, long *value)
+VDE VDSetMasterBlendLevel(uint16 *blendLevel)
+VDE VDSetPlayThruDestination(PixMapHandledest, RectPtr destRect, MatrixRecordPtr m, RgnHandle mask)
+VDE VDSetPlayThruOnOff(short state)
+VDE VD[SG*]etFieldPreference(short fieldFlag)
+VDE VDPreflightDestination(Rect *digitizerRect, PixMap **dest, RectPtr destRect, MatrixRecordPtr m)
+VDE VDPreflightGlobalRect(GrafPtr theWindow, Rect *globalRect)
+VDE VDSetPlayThruGlobalRect(GrafPtr theWindow, Rect *globalRect)
+VDE VDSetInputGammaRecord(VDGamRecPtrinputGammaPtr)
+VDE VDGetInputGammaRecord(VDGamRecPtr *inputGammaPtr)
+VDE VD[SG]etBlackLevelValue(uint16 *)
+VDE VD[SG]etWhiteLevelValue(uint16 *)
+VDE VDGetVideoDefaults(uint16 *blackLevel, uint16 *whiteLevel, uint16 *brightness, uint16 *hue, uint16 *saturation, uint16 *contrast, uint16 *sharpness)
+VDE VDGetNumberOfInputs(short *inputs)
+VDE VDGetInputFormat(short input, short *format)
+VDE VD[SG*]etInput(short input)
+VDE VDSetInputStandard(short inputStandard)
+VDE VDSetupBuffers(VdigBufferRecListHandle bufferList)
+VDE VDGrabOneFrameAsync(short buffer)
+VDE VDDone(short buffer)
+VDE VDSetCompression(OSTypecompressType, short depth, Rect *bounds, CodecQspatialQuality, CodecQtemporalQuality, long keyFrameRate)
+VDE VDCompressOneFrameAsync(VDC ci)
+VDE VDCompressDone(UInt8 *queuedFrameCount, Ptr *theData, long *dataSize, UInt8 *similarity, TimeRecord *t)
+VDE VDReleaseCompressBuffer(Ptr bufferAddr)
+VDE VDGetImageDescription(ImageDescriptionHandle desc)
+VDE VDResetCompressSequence(VDC ci)
+VDE VDSetCompressionOnOff(Boolean)
+VDE VDGetCompressionTypes(VDCompressionListHandle h)
+VDE VDSetTimeBase(TimeBase t)
+VDE VDSetFrameRate(Fixed framesPerSecond)
+VDE VDGetDataRate(long *milliSecPerFrame, Fixed *framesPerSecond, long *bytesPerSecond)
+VDE VDGetSoundInputDriver(Str255 soundDriverName)
+VDE VDGetDMADepths(long *depthArray, long *preferredDepth)
+VDE VDGetPreferredTimeScale(TimeScale *preferred)
+VDE VDReleaseAsyncBuffers(VDC ci)
+VDE VDSetDataRate(long bytesPerSecond)
+VDE VDGetTimeCode(TimeRecord *atTime, void *timeCodeFormat, void *timeCodeTime)
+VDE VDUseSafeBuffers(Boolean useSafeBuffers)
+VDE VDGetSoundInputSource(long videoInput, long *soundInput)
+VDE VDGetCompressionTime(OSTypecompressionType, short depth, Rect *srcRect, CodecQ *spatialQuality, CodecQ *temporalQuality, ulong *compressTime)
+VDE VDSetPreferredPacketSize(long preferredPacketSizeInBytes)
+VDE VD[SG*]etPreferredImageDimensions(long width, long height)
+VDE VDGetInputName(long videoInput, Str255 name)
+VDE VDSetDestinationPort(CGrafPtr destPort)
+VDE VDGetDeviceNameAndFlags(Str255 outName, UInt32 *outNameFlags)
+VDE VDCaptureStateChanging(UInt32inStateFlags)
+VDE VDGetUniqueIDs(UInt64 *outDeviceID, UInt64 *outInputID)
+VDE VDSelectUniqueIDs(const UInt64 *inDeviceID, const UInt64 *inInputID)
+*/
+#endif
+
+static OSErr callback(ComponentInstanceRecord*, char*, long int, long int*, long int, TimeValue, short int, long int) {
+ post("FormatQuickTimeCamera callback");
+ return noErr;
+}
+
+\class FormatQuickTimeCamera : Format {
+ P<Dim> dim;
+ uint8 *buf;
+ uint8 *buf2;
+ VDC vdc;
+ int m_newFrame;
+ SeqGrabComponent m_sg;
+ SGChannel m_vc;
+ short m_pixelDepth;
+ Rect rect;
+ GWorldPtr m_srcGWorld;
+ PixMapHandle m_pixMap;
+ Ptr m_baseAddr;
+ long m_rowBytes;
+ int m_quality;
+//int m_colorspace;
+ \constructor (t_symbol *mode) {
+ //vdc = SGGetVideoDigitizerComponent(c);
+ dim = new Dim(240,320,4);
+ OSErr e;
+ rect.top=rect.left=0;
+ rect.bottom=dim->v[0]; rect.right=dim->v[1];
+ int n=0;
+ Component c = 0;
+ ComponentDescription cd;
+ cd.componentType = SeqGrabComponentType;
+ cd.componentSubType = 0;
+ cd.componentManufacturer = 0;
+ cd.componentFlags = 0;
+ cd.componentFlagsMask = 0;
+ for(;;) {
+ c = FindNextComponent(c, &cd);
+ if (!c) break;
+ ComponentDescription cd2;
+ Ptr name=0,info=0,icon=0;
+ GetComponentInfo(c,&cd2,&name,&info,&icon);
+ post("Component #%d",n);
+ char *t = (char *)&cd.componentType;
+ post(" type='%c%c%c%c'",t[3],t[2],t[1],t[0]);
+ t = (char *)&cd.componentSubType;
+ post(" subtype='%c%c%c%c'",t[3],t[2],t[1],t[0]);
+ post(" name=%08x, *name='%*s'",name, *name, name+1);
+ post(" info=%08x, *info='%*s'",info, *name, info+1);
+ n++;
+ }
+ post("number of components: %d",n);
+ m_sg = OpenDefaultComponent(SeqGrabComponentType, 0);
+ if(!m_sg) RAISE("could not open default component");
+ e=SGInitialize(m_sg);
+ if(e!=noErr) RAISE("could not initialize SG");
+ e=SGSetDataRef(m_sg, 0, 0, seqGrabDontMakeMovie);
+ if (e!=noErr) RAISE("dataref failed");
+ e=SGNewChannel(m_sg, VideoMediaType, &m_vc);
+ if(e!=noErr) post("could not make new SG channel");
+ e=SGSetChannelBounds(m_vc, &rect);
+ if(e!=noErr) post("could not set SG ChannelBounds");
+ e=SGSetChannelUsage(m_vc, seqGrabPreview);
+ if(e!=noErr) post("could not set SG ChannelUsage");
+ e=SGSetDataProc(m_sg,NewSGDataUPP(callback),0);
+ if (e!=noErr) post("could not set SG DataProc");
+ // m_rowBytes = m_vidXSize*4;
+ switch (3) {
+ case 0: e=SGSetChannelPlayFlags(m_vc, channelPlayNormal); break;
+ case 1: e=SGSetChannelPlayFlags(m_vc, channelPlayHighQuality); break;
+ case 2: e=SGSetChannelPlayFlags(m_vc, channelPlayFast); break;
+ case 3: e=SGSetChannelPlayFlags(m_vc, channelPlayAllData); break;
+ }
+ int dataSize = dim->prod();
+ buf = new uint8[dataSize];
+ buf2 = new uint8[dataSize];
+ m_rowBytes = dim->prod(1);
+ e=QTNewGWorldFromPtr (&m_srcGWorld,k32ARGBPixelFormat,&rect,NULL,NULL,0,buf,m_rowBytes);
+ if (0/*yuv*/) {
+ int dataSize = dim->prod()*2/4;
+ buf = new uint8[dataSize];
+ m_rowBytes = dim->prod(1)*2/4;
+ e=QTNewGWorldFromPtr (&m_srcGWorld,k422YpCbCr8CodecType,&rect,NULL,NULL,0,buf,m_rowBytes);
+ }
+ if (e!=noErr) RAISE("error #%d at QTNewGWorldFromPtr",e);
+ if (!m_srcGWorld) RAISE("Could not allocate off screen");
+ SGSetGWorld(m_sg,(CGrafPtr)m_srcGWorld, NULL);
+ //SGStartPreview(m_sg);
+ e=SGStartRecord(m_sg);
+ if (e!=noErr) RAISE("error #%d at SGStartRecord",e);
+ }
+ ~FormatQuickTimeCamera() {
+ if (m_vc) if (::SGDisposeChannel(m_sg, m_vc)) RAISE("SGDisposeChannel");
+ if (m_sg) {
+ if (::CloseComponent(m_sg)) RAISE("CloseComponent");
+ if (m_srcGWorld) ::DisposeGWorld(m_srcGWorld);
+ }
+ }
+ \decl 0 bang ();
+ \grin 0 int
+};
+
+// /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Components.h
+
+static int nn(int c) {return c?c:' ';}
+
+/*
+pascal Boolean pix_videoDarwin :: SeqGrabberModalFilterProc (DialogPtr theDialog, const EventRecord *theEvent, short *itemHit, long refCon){
+ Boolean handled = false;
+ if ((theEvent->what == updateEvt) &&
+ ((WindowPtr) theEvent->message == (WindowPtr) refCon)) {
+ BeginUpdate ((WindowPtr) refCon);
+ EndUpdate ((WindowPtr) refCon);
+ handled = true;
+ }
+ WindowRef awin = GetDialogWindow(theDialog);
+ ShowWindow (awin);
+ SetWindowClass(awin,kUtilityWindowClass);
+ //ChangeWindowAttributes(awin,kWindowStandardHandlerAttribute,0);
+ //SGPanelEvent(m_sg,m_vc,theDialog,0,theEvent,itemHit,&handled);
+ //AEProcessAppleEvent (theEvent);
+ return handled;
+}
+void pix_videoDarwin :: DoVideoSettings() {
+ Rect newActiveVideoRect;
+ Rect curBounds, curVideoRect, newVideoRect;
+ ComponentResult err;
+ SGModalFilterUPP seqGragModalFilterUPP;
+ err = SGGetChannelBounds (m_vc, &curBounds);
+ err = SGGetVideoRect (m_vc, &curVideoRect);
+ err = SGPause (m_sg, true);
+ seqGragModalFilterUPP = (SGModalFilterUPP)NewSGModalFilterUPP(SeqGrabberModalFilterProc);
+ err = SGSettingsDialog(m_sg, m_vc, 0, NULL, seqGrabSettingsPreviewOnly, seqGragModalFilterUPP, (long)m_srcGWorld);
+ DisposeSGModalFilterUPP(seqGragModalFilterUPP);
+ err = SGGetVideoRect (m_vc, &newVideoRect);
+ err = SGGetSrcVideoBounds (m_vc, &newActiveVideoRect);
+ err = SGPause (m_sg, false);
+}
+*/
+
+\def 0 bang () {
+ GridOutlet out(this,0,dim);
+ int n = dim->prod()/4;
+ for (int i=0; i<n; i++) ((uint32 *)buf2)[i] = ((uint32 *)buf)[i] >> 8;
+ out.send(dim->prod(),buf2);
+ SGIdle(m_sg);
+}
+
+GRID_INLET(0) {
+ RAISE("Unimplemented. Sorry.");
+//!@#$
+ if (in->dim->n != 3) RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3) RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_chunk(0);
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+\end class FormatQuickTimeCamera {install_format("#io.quicktimecamera",4,"");}
+
+\class FormatQuickTimeApple : Format {
+ Movie movie;
+ TimeValue time;
+ short movie_file;
+ GWorldPtr gw; /* just like an X11 Image or Pixmap, maybe. */
+ uint8 *buffer;
+ P<Dim> dim;
+ int nframe, nframes;
+ \constructor (t_symbol *mode, string filename) {
+ /*vdc=0;*/ movie=0; time=0; movie_file=0; gw=0; buffer=0; dim=0; nframe=0; nframes=0;
+ int err;
+ filename = gf_find_file(filename);
+ FSSpec fss;
+ FSRef fsr;
+ err = FSPathMakeRef((const UInt8 *)filename.data(), &fsr, NULL); if (err) goto err;
+ err = FSGetCatalogInfo(&fsr, kFSCatInfoNone, NULL, NULL, &fss, NULL); if (err) goto err;
+ err = OpenMovieFile(&fss,&movie_file,fsRdPerm); if (err) goto err;
+ NewMovieFromFile(&movie, movie_file, NULL, NULL, newMovieActive, NULL);
+ Rect r;
+ GetMovieBox(movie, &r);
+ post("handle=%d movie=%d tracks=%d", movie_file, movie, GetMovieTrackCount(movie));
+ post("duration=%d; timescale=%d cHz", (long)GetMovieDuration(movie), (long)GetMovieTimeScale(movie));
+ nframes = GetMovieDuration(movie); /* i don't think so */
+ post("rect=((%d..%d),(%d..%d))", r.top, r.bottom, r.left, r.right);
+ OffsetRect(&r, -r.left, -r.top);
+ SetMovieBox(movie, &r);
+ dim = new Dim(r.bottom-r.top, r.right-r.left, 4);
+ SetMoviePlayHints(movie, hintsHighQuality, hintsHighQuality);
+ buffer = new uint8[dim->prod()];
+ err = QTNewGWorldFromPtr(&gw, k32ARGBPixelFormat, &r, NULL, NULL, 0, buffer, dim->prod(1));
+ if (err) goto err;
+ return;
+ err:
+ // RAISE("can't open file `%s': error #%d (%s)", filename.data(), err, rb_str_ptr(rb_funcall(mGridFlow,SI(macerr),1,INT2NUM(err))));
+ RAISE("can't open file `%s': error #%d (0x%08x)", filename.data(), err, err);
+ }
+ ~FormatQuickTimeApple() {
+ if (movie) {
+ DisposeMovie(movie);
+ DisposeGWorld(gw);
+ CloseMovieFile(movie_file);
+ }
+ }
+ \decl 0 codec (string c);
+ \decl 0 colorspace (string c);
+ \decl 0 bang ();
+ \decl 0 seek (int frame);
+ \decl 0 rewind ();
+ \grin 0
+};
+
+\def 0 seek (int frame) {nframe=frame;}
+\def 0 rewind () {_0_seek(0,0,0);}
+
+\def 0 bang () {
+ CGrafPtr savedPort;
+ GDHandle savedDevice;
+ SetMovieGWorld(movie,gw,GetGWorldDevice(gw));
+ Rect r;
+ GetMovieBox(movie,&r);
+ PixMapHandle pixmap = GetGWorldPixMap(gw);
+ short flags = nextTimeStep;
+ if (nframe>=nframes) {outlet_bang(bself->te_outlet); return;}
+ if (nframe==0) flags |= nextTimeEdgeOK;
+ TimeValue duration;
+ OSType mediaType = VisualMediaCharacteristic;
+ GetMovieNextInterestingTime(movie,
+ flags,1,&mediaType,time,0,&time,&duration);
+ if (time<0) {
+ time=0;
+ outlet_bang(bself->te_outlet);
+ return;
+ }
+// post("quicktime frame #%d; time=%d duration=%d", nframe, (long)time, (long)duration);
+ SetMovieTimeValue(movie,nframe*duration);
+ MoviesTask(movie,0);
+ GridOutlet out(this,0,dim);
+ uint32 *bufu32 = (uint32 *)buffer;
+ int n = dim->prod()/4;
+ int i;
+ if (is_le()) {
+ for (; i<n; i++) {
+ bufu32[i+0]=bufu32[i+0]>>8;
+ }
+ } else {
+ for (i=0; i<n&-4; i+=4) {
+ bufu32[i+0]=(bufu32[i+0]<<8)+(bufu32[i+0]>>24);
+ bufu32[i+1]=(bufu32[i+1]<<8)+(bufu32[i+1]>>24);
+ bufu32[i+2]=(bufu32[i+2]<<8)+(bufu32[i+2]>>24);
+ bufu32[i+3]=(bufu32[i+3]<<8)+(bufu32[i+3]>>24);
+ }
+ for (; i<n; i++) {
+ bufu32[i+0]=(bufu32[i+0]<<8)+(bufu32[i+0]>>24);
+ }
+ }
+ out.send(dim->prod(),buffer);
+ int nf=nframe;
+ nframe++;
+ //return INT2NUM(nf);
+}
+
+GRID_INLET(0) {
+ RAISE("Unimplemented. Sorry.");
+//!@#$
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels (got %d)",in->dim->get(2));
+ in->set_chunk(0);
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\def 0 codec (string c) { RAISE("Unimplemented. Sorry."); }
+\def 0 colorspace (string c) { RAISE("Unimplemented. Sorry."); }
+
+\classinfo {
+ EnterMovies();
+ install_format("#io.quicktime",4,"mov");
+}
+\end class FormatQuickTimeApple
+void startup_quicktimeapple () {
+ \startall
+}
diff --git a/externals/gridflow/src/quicktimehw.cxx b/externals/gridflow/src/quicktimehw.cxx
new file mode 100644
index 00000000..38dfcbbb
--- /dev/null
+++ b/externals/gridflow/src/quicktimehw.cxx
@@ -0,0 +1,246 @@
+/*
+ $Id: quicktimehw.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define QUICKTIMEHW_INCLUDE_HERE
+#include "gridflow.hxx.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <map>
+#include <vector>
+
+static std::map<string,std::vector<string> *> codecs;
+static std::map<string,string> fourccs;
+
+\class FormatQuickTimeHW : Format {
+ quicktime_t *anim;
+ int track;
+ P<Dim> dim;
+ char *codec;
+ int colorspace;
+ int channels;
+ bool started;
+ P<Dim> force;
+ float64 framerate;
+ P<BitPacking> bit_packing;
+ int jpeg_quality; // in theory we shouldn't need this, but...
+ ~FormatQuickTimeHW() {if (anim) quicktime_close(anim);}
+ \constructor (t_symbol *mode, string filename) {
+ track=0; dim=0; codec=const_cast<char *>(QUICKTIME_RAW);
+ started=false; force=0; framerate=29.97; bit_packing=0; jpeg_quality=75;
+// libquicktime may be nice, but it won't take a filehandle, only filename
+ filename = gf_find_file(filename);
+ anim = quicktime_open((char *)filename.data(),mode==gensym("in"),mode==gensym("out"));
+ if (!anim) RAISE("can't open file `%s': %s (or some other reason that libquicktime won't tell us)",
+ filename.data(), strerror(errno));
+ if (mode==gensym("in")) {
+ /* This doesn't really work: (is it just for encoding?)
+ if (!quicktime_supported_video(anim,track))
+ RAISE("quicktime: unsupported codec: %s",
+ quicktime_video_compressor(anim,track));
+ */
+ }
+ _0_colorspace(0,0,string("rgb"));
+ quicktime_set_cpus(anim,1);
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+ }
+ \decl 0 bang ();
+ \decl 0 seek (int32 frame);
+ \decl 0 rewind ();
+ \decl 0 force_size (int32 height, int32 width);
+ \decl 0 codec (string c);
+ \decl 0 colorspace (string c);
+ \decl 0 parameter (string name, int32 value);
+ \decl 0 framerate (float64 f);
+ \decl 0 size (int32 height, int32 width);
+ \decl 0 get ();
+ \grin 0 int
+};
+
+\def 0 force_size (int32 height, int32 width) { force = new Dim(height, width); }
+\def 0 seek (int32 frame) {
+ quicktime_set_video_position(anim,clip(frame,int32(0),int32(quicktime_video_length(anim,track)-1)),track);
+}
+\def 0 rewind () {_0_seek(0,0,0);}
+
+\def 0 bang () {
+ long length = quicktime_video_length(anim,track);
+ long nframe = quicktime_video_position(anim,track);
+ if (nframe >= length) {outlet_bang(bself->te_outlet); return;}
+ /* if it works, only do it once, to avoid silly stderr messages forgotten in LQT */
+ if (!quicktime_reads_cmodel(anim,colorspace,0) && !started) {
+ RAISE("LQT says this video cannot be decoded into the chosen colorspace");
+ }
+ int sx = quicktime_video_width(anim,track);
+ int sy = quicktime_video_height(anim,track);
+ int sz = quicktime_video_depth(anim,track);
+ channels = sz/8; // hack. how do i get the video's native colormodel ?
+ switch (sz) {
+ case 24: colorspace=BC_RGB888; break;
+ case 32: colorspace=BC_RGBA8888; break;
+ default: post("strange quicktime. ask matju."); break;
+ }
+ if (force) {
+ sy = force->get(0);
+ sx = force->get(1);
+ }
+ uint8 buf[sy*sx*channels];
+ uint8 *rows[sy]; for (int i=0; i<sy; i++) rows[i]=buf+i*sx*channels;
+ quicktime_decode_scaled(anim,0,0,sx,sy,sx,sy,colorspace,rows,track);
+ GridOutlet out(this,0,new Dim(sy,sx,channels),cast);
+ out.send(sy*sx*channels,buf);
+ started=true;
+// return INT2NUM(nframe);
+}
+
+//!@#$ should also support symbol values (how?)
+\def 0 parameter (string name, int32 value) {
+ int val = value;
+ //post("quicktime_set_parameter %s %d",name.data(), val);
+ quicktime_set_parameter(anim, const_cast<char *>(name.data()), &val);
+ if (name=="jpeg_quality") jpeg_quality=value;
+}
+
+\def 0 framerate (float64 f) {
+ framerate=f;
+ quicktime_set_framerate(anim, f);
+}
+
+\def 0 size (int32 height, int32 width) {
+ if (dim) RAISE("video size already set!");
+ // first frame: have to do setup
+ dim = new Dim(height, width, 3);
+ quicktime_set_video(anim,1,dim->get(1),dim->get(0),framerate,codec);
+ quicktime_set_cmodel(anim,colorspace);
+}
+
+GRID_INLET(0) {
+ if (in->dim->n != 3) RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2)!=channels) RAISE("expecting %d channels (got %d)",channels,in->dim->get(2));
+ in->set_chunk(0);
+ if (dim) {
+ if (!dim->equal(in->dim)) RAISE("all frames should be same size");
+ } else {
+ // first frame: have to do setup
+ dim = in->dim;
+ quicktime_set_video(anim,1,dim->get(1),dim->get(0),framerate,codec);
+ quicktime_set_cmodel(anim,colorspace);
+ quicktime_set_depth(anim,8*channels,track);
+ }
+ //post("quicktime jpeg_quality %d", jpeg_quality);
+ quicktime_set_parameter(anim, (char*)"jpeg_quality", &jpeg_quality);
+} GRID_FLOW {
+ int sx = quicktime_video_width(anim,track);
+ int sy = quicktime_video_height(anim,track);
+ uint8 *rows[sy];
+ if (sizeof(T)>1) {
+ uint8 data2[n];
+ bit_packing->pack(sx*sy,data,(uint8 *)data2);
+ for (int i=0; i<sy; i++) rows[i]=data2+i*sx*channels;
+ quicktime_encode_video(anim,rows,track);
+ } else {
+ for (int i=0; i<sy; i++) rows[i]=(uint8 *)data+i*sx*channels;
+ quicktime_encode_video(anim,rows,track);
+ }
+} GRID_FINISH {
+} GRID_END
+
+\def 0 codec (string c) {
+#ifdef LQT_VERSION
+ char buf[5];
+ strncpy(buf,c.data(),4);
+ for (int i=c.length(); i<4; i++) buf[i]=' ';
+ buf[4]=0;
+ if (fourccs.find(string(buf))==fourccs.end())
+ RAISE("warning: unknown fourcc '%s'" /*" (%s)"*/, buf /*, rb_str_ptr(rb_inspect(rb_funcall(fourccs,SI(keys),0)))*/);
+#endif
+ codec = strdup(buf);
+}
+
+\def 0 colorspace (string c) {
+ if (0) {
+ } else if (c=="rgb") { channels=3; colorspace=BC_RGB888;
+ } else if (c=="rgba") { channels=4; colorspace=BC_RGBA8888;
+ } else if (c=="bgr") { channels=3; colorspace=BC_BGR888;
+ } else if (c=="bgrn") { channels=4; colorspace=BC_BGR8888;
+// } else if (c=="yuv") { channels=3; colorspace=BC_YUV888;
+ } else if (c=="yuva") { channels=4; colorspace=BC_YUVA8888;
+ } else if (c=="YUV420P") { channels=3; colorspace=BC_YUV420P;
+ } else RAISE("unknown colorspace '%s' (supported: rgb, rgba, bgr, bgrn, yuv, yuva)",c.data());
+}
+
+\def 0 get () {
+/* t_atom a[1];
+ SETFLOAT(a,(float)length);
+ outlet_anything(bself->te_outlet,gensym("frames"),1,a);
+*/
+ t_atom a[1];
+ SETFLOAT(a,quicktime_video_length(anim,track));
+ outlet_anything(bself->outlets[0],gensym("frames"),1,a);
+ SETFLOAT(a,quicktime_frame_rate(anim,track));
+ outlet_anything(bself->outlets[0],gensym("framerate"),1,a);
+ SETFLOAT(a,quicktime_video_height(anim,track));
+ outlet_anything(bself->outlets[0],gensym("height"),1,a);
+ SETFLOAT(a,quicktime_video_width(anim,track));
+ outlet_anything(bself->outlets[0],gensym("width"),1,a);
+ SETFLOAT(a,quicktime_video_depth(anim,track));
+ outlet_anything(bself->outlets[0],gensym("depth"),1,a);
+ SETSYMBOL(a,gensym(quicktime_video_compressor(anim,track)));
+ outlet_anything(bself->outlets[0],gensym("codec"),1,a);
+ //SUPER;
+}
+
+\classinfo {install_format("#io.quicktime",6,"mov avi");
+// def self.info; %[codecs: #{@codecs.keys.join' '}] end
+//#define L fprintf(stderr,"%s:%d in %s\n",__FILE__,__LINE__,__PRETTY_FUNCTION__);
+#ifdef LQT_VERSION
+ lqt_registry_init();
+ int n = lqt_get_num_video_codecs();
+ for (int i=0; i<n; i++) {
+ const lqt_codec_info_t *s = lqt_get_video_codec_info(i);
+ if (!s->name) {
+ fprintf(stderr,"[#in quicktime]: skipping codec with null name!\n");
+ continue;
+ }
+ string name = string(s->name);
+ std::vector<string> *f = new std::vector<string>(s->num_fourccs);
+ if (!s->fourccs) {
+ post("WARNING: no fourccs (quicktime library is broken?)");
+ goto hell;
+ }
+ //fprintf(stderr,"num_fourccs=%d fourccs=%p\n",s->num_fourccs,s->fourccs);
+ for (int j=0; j<s->num_fourccs; j++) {
+ string fn = string(s->fourccs[j]);
+ f->push_back(fn);
+ fourccs[fn]=name;
+ }
+ codecs[name]=f;
+ hell:;
+ }
+#endif
+}
+\end class FormatQuickTimeHW
+void startup_quicktimehw () {
+ \startall
+}
diff --git a/externals/gridflow/src/sdl.cxx b/externals/gridflow/src/sdl.cxx
new file mode 100644
index 00000000..7406611c
--- /dev/null
+++ b/externals/gridflow/src/sdl.cxx
@@ -0,0 +1,209 @@
+/*
+ $Id: sdl.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "gridflow.hxx.fcs"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <SDL/SDL.h>
+
+struct FormatSDL;
+void FormatSDL_call(FormatSDL *self);
+static bool in_use = false;
+static bool full_screen = false;
+static int mousex,mousey,mousem;
+SDL_Surface *screen;
+FObject *instance;
+
+static t_symbol *keyboard[SDLK_LAST];
+
+static void KEYS_ARE (int i, const char *s__) {
+ char *s_ = strdup(s__);
+ char *s = s_;
+ while (*s) {
+ char *t = strchr(s,' ');
+ if (t) *t=0;
+ keyboard[i] = gensym(s);
+ if (!t) break;
+ s=t+1; i++;
+ }
+ free(s_);
+}
+
+static void build_keyboard () {
+ KEYS_ARE(8,"BackSpace Tab");
+ KEYS_ARE(13,"Return");
+ KEYS_ARE(27,"Escape");
+ KEYS_ARE(32,"space exclam quotedbl numbersign dollar percent ampersand apostrophe");
+ KEYS_ARE(40,"parenleft parenright asterisk plus comma minus period slash");
+ KEYS_ARE(48,"D0 D1 D2 D3 D4 D5 D6 D7 D8 D9");
+ KEYS_ARE(58,"colon semicolon less equal greater question at");
+ //KEYS_ARE(65,"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
+ KEYS_ARE(91,"bracketleft backslash bracketright asciicircum underscore grave quoteleft");
+ KEYS_ARE(97,"a b c d e f g h i j k l m n o p q r s t u v w x y z");
+ //SDLK_DELETE = 127
+ KEYS_ARE(256,"KP_0 KP_1 KP_2 KP_3 KP_4 KP_5 KP_6 KP_7 KP_8 KP_9");
+ KEYS_ARE(266,"KP_Decimal KP_Divide KP_Multiply KP_Subtract KP_Add KP_Enter KP_Equal");
+ KEYS_ARE(273,"KP_Up KP_Down KP_Right KP_Left KP_Insert KP_Home KP_End KP_Prior KP_Next");
+ KEYS_ARE(282,"F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15");
+ KEYS_ARE(300,"Num_Lock Caps_Lock Scroll_Lock");
+ KEYS_ARE(303,"Shift_R Shift_L Control_R Control_L Alt_R Alt_L Meta_L Meta_R");
+ KEYS_ARE(311,"Super_L Super_R Mode_switch Multi_key");
+}
+
+static void report_pointer () {
+ t_atom a[3];
+ SETFLOAT(a+0,mousey);
+ SETFLOAT(a+1,mousex);
+ SETFLOAT(a+2,mousem);
+ outlet_anything(instance->bself->outlets[0],gensym("position"),COUNT(a),a);
+}
+
+static void HandleEvent () {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_KEYDOWN: case SDL_KEYUP: {
+ int key = event.key.keysym.sym;
+ int mod = event.key.keysym.mod;
+ if (event.type==SDL_KEYDOWN && (key==SDLK_F11 || key==SDLK_ESCAPE || key=='f')) {
+ full_screen = !full_screen;
+ SDL_WM_ToggleFullScreen(screen);
+ break;
+ }
+ t_symbol *sel = gensym(const_cast<char *>(event.type==SDL_KEYDOWN ? "keypress" : "keyrelease"));
+ t_atom at[4];
+ mousem &= ~0xFF;
+ mousem |= mod;
+ SETFLOAT(at+0,mousey);
+ SETFLOAT(at+1,mousex);
+ SETFLOAT(at+2,mousem);
+ SETSYMBOL(at+3,keyboard[event.key.keysym.sym]);
+ outlet_anything(instance->bself->outlets[0],sel,4,at);
+ } break;
+ case SDL_MOUSEBUTTONDOWN: SDL_MOUSEBUTTONUP: {
+ if (SDL_MOUSEBUTTONDOWN) mousem |= (128<<event.button.button);
+ else mousem &= ~(128<<event.button.button);
+ //post("mousem=%d",mousem);
+ report_pointer();
+ } break;
+ case SDL_MOUSEMOTION: {
+ mousey = event.motion.y;
+ mousex = event.motion.x;
+ report_pointer();
+ } break;
+ case SDL_VIDEORESIZE: {
+ } break;
+ }
+ }
+}
+
+\class FormatSDL : Format {
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ t_clock *clock;
+ void resize_window (int sx, int sy);
+ void call ();
+ \decl 0 setcursor (int shape);
+ \decl 0 hidecursor ();
+ \decl 0 title (string title);
+ \constructor (t_symbol *mode) {
+ dim=0;screen=0;
+ if (in_use) RAISE("only one FormatSDL object at a time; sorry");
+ in_use=true;
+ if (SDL_Init(SDL_INIT_VIDEO)<0) RAISE("SDL_Init() error: %s",SDL_GetError());
+ atexit(SDL_Quit);
+ resize_window(320,240);
+ SDL_PixelFormat *f = screen->format;
+ uint32 mask[3] = {f->Rmask,f->Gmask,f->Bmask};
+ switch (f->BytesPerPixel) {
+ case 1: RAISE("8 bpp not supported"); break;
+ case 2: case 3: case 4:
+ bit_packing = new BitPacking(is_le(),f->BytesPerPixel,3,mask);
+ break;
+ default: RAISE("%d bytes/pixel: how do I deal with that?",f->BytesPerPixel); break;
+ }
+ instance=this;
+ clock = clock_new(this,(t_method)FormatSDL_call);
+ clock_delay(clock,0);
+ _0_title(0,0,string("GridFlow SDL"));
+ }
+ \grin 0 int
+ ~FormatSDL () {
+ clock_unset(clock);
+ clock_free(clock);
+ SDL_Quit();
+ instance=0;
+ in_use=false;
+ }
+};
+
+\def 0 title (string title) {
+ SDL_WM_SetCaption(title.data(),title.data());
+}
+
+void FormatSDL::call() {HandleEvent(); clock_delay(clock,20);}
+void FormatSDL_call(FormatSDL *self) {self->call();}
+
+void FormatSDL::resize_window (int sx, int sy) {
+ dim = new Dim(sy,sx,3);
+ screen = SDL_SetVideoMode(sx,sy,0,SDL_SWSURFACE);
+ if (!screen)
+ RAISE("Can't switch to (%d,%d,%dbpp): %s", sy,sx,24, SDL_GetError());
+}
+
+GRID_INLET(0) {
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2) != 3)
+ RAISE("expecting 3 channels: red,green,blue (got %d)",in->dim->get(2));
+ int sx = in->dim->get(1), osx = dim->get(1);
+ int sy = in->dim->get(0), osy = dim->get(0);
+ in->set_chunk(1);
+ if (sx!=osx || sy!=osy) resize_window(sx,sy);
+} GRID_FLOW {
+ int bypl = screen->pitch;
+ int sxc = in->dim->prod(1);
+ int sx = in->dim->get(1);
+ int y = dex/sxc;
+ if (SDL_MUSTLOCK(screen)) if (SDL_LockSurface(screen) < 0) return; //???
+ for (; n>0; y++, data+=sxc, n-=sxc) {
+ /* convert line */
+ bit_packing->pack(sx, data, (uint8 *)screen->pixels+y*bypl);
+ }
+ if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
+} GRID_FINISH {
+ SDL_UpdateRect(screen,0,0,in->dim->get(1),in->dim->get(0));
+} GRID_END
+
+\def 0 setcursor (int shape) {SDL_ShowCursor(SDL_ENABLE);}
+\def 0 hidecursor () {SDL_ShowCursor(SDL_DISABLE);}
+
+\end class FormatSDL {install_format("#io.sdl",2,"");}
+void startup_sdl () {
+ \startall
+ build_keyboard();
+}
diff --git a/externals/gridflow/src/source_filter.rb b/externals/gridflow/src/source_filter.rb
new file mode 100644
index 00000000..9dfef776
--- /dev/null
+++ b/externals/gridflow/src/source_filter.rb
@@ -0,0 +1,311 @@
+#!/usr/bin/env ruby
+=begin
+ $Id: source_filter.rb 4452 2009-10-27 15:59:57Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+=end
+
+$stack = []
+$classes = []
+$exit = 0
+
+ClassDecl = Struct.new(:name,:supername,:methods,:grins,:attrs,:info)
+MethodDecl = Struct.new(:rettype,:selector,:arglist,:minargs,:maxargs,:where)
+Arg = Struct.new(:type,:name,:default)
+Attr = Struct.new(:type,:name,:default,:virtual)
+
+class MethodDecl
+ def ==(o)
+ return false unless rettype==o.rettype && maxargs==o.maxargs
+ arglist.each_index{|i| arglist[i] == o.arglist[i] or return false }
+ return true
+ end
+ def ===(o)
+ return false unless rettype==o.rettype && maxargs==o.maxargs
+ arglist.each_index{|i| arglist[i].type == o.arglist[i].type and arglist[i].default == o.arglist[i].default or return false }
+ return true
+ end
+ attr_accessor :done
+end
+
+class Arg
+ def canon(type)
+ type="Grid *" if type=="PtrGrid"
+ type
+ end
+ def ==(o) canon(type)==canon(o.type) && name==o.name end
+end
+
+In = File.open ARGV[0], "r"
+Out = File.open ARGV[1], "w"
+
+def handle_class(line)
+ raise "already in class #{where}" if $stack[-1] and ClassDecl===$stack[-1]
+ /^(\w+)(?:\s*[:<]\s*(\w+))?\s*(\{.*)?/.match line or raise "syntax error #{where}"
+ classname = $1
+ superclassname = $2
+ rest = $3
+ q=ClassDecl.new(classname,superclassname,{},{},{},false)
+ $stack << q
+ $classes << q
+ Out.print "#define THISCLASS #{classname}\n\# #{$linenumber}\n"
+ if rest and /^\{/ =~ rest then
+ Out.print "struct #{classname} "
+ Out.print ": #{superclassname}" if superclassname
+ Out.print rest
+ end
+end
+
+def parse_methoddecl(line,term)
+ /^(\w+(?:\s*\*)?)\s+(\w+)\s*\(([^\)]*)\)\s*#{term}/.match line or
+ raise "syntax error #{where} #{line}"
+ rettype,selector,arglist = $1,$2,$3,$4
+ if /^\d+$/ =~ rettype then
+ selector = "_"+rettype+"_"+selector
+ rettype = "void"
+ end
+ arglist,minargs,maxargs = parse_arglist arglist
+ MethodDecl.new(rettype,selector,arglist,minargs,maxargs,where)
+end
+
+def parse_arglist(arglist)
+ arglist = arglist.split(/,/)
+ maxargs = arglist.length
+ args = arglist.map {|arg|
+ if /^\s*\.\.\.\s*$/.match arg then maxargs=-1; next end
+ /^\s*([\w\s\*<>]+)\s*\b(\w+)\s*(?:\=(.*))?/.match arg or
+ raise "syntax error in \"#{arg}\" #{where}"
+ type,name,default=$1,$2,$3
+ Arg.new(type.sub(/\s+$/,""),name,default)
+ }.compact
+ minargs = args.length
+ minargs-=1 while minargs>0 and args[minargs-1].default
+ [args,minargs,maxargs]
+end
+
+def unparse_arglist(arglist,with_default=true)
+ arglist.map {|arg|
+ x="#{arg.type} #{arg.name}"
+ x << '=' << arg.default if with_default and arg.default
+ x
+ }.join(", ")
+end
+
+def where; "[#{ARGV[0]}:#{$linenumber}]" end
+
+def handle_attr(line)
+ line.gsub!(/\/\/.*$/,"") # remove comment
+ frame = $stack[-1]
+ type = line.gsub(%r"//.*$","").gsub(%r"/\*.*\*/","").gsub(%r";?\s*$","")
+ virtual = !!type.slice!(/\(\)$/)
+ name = type.slice!(/\w+$/)
+ raise "missing \\class #{where}" if not $stack[-1] or not ClassDecl===frame
+ handle_decl "void ___get(t_symbol *s);" if frame.attrs.size==0
+ frame.attrs[name]=Attr.new(type,name,nil,virtual)
+ if virtual then
+ handle_decl "#{type} #{name}();"
+ else
+ Out.print line
+ end
+ type.gsub!(/\s+$/,"")
+ type.gsub!(/^\s+/,"")
+ if type=="bool" then
+ handle_decl "0 #{name} (#{type} #{name}=true);"
+ else
+ handle_decl "0 #{name} (#{type} #{name});"
+ end
+end
+
+def handle_decl(line)
+ frame = $stack[-1]
+ raise "missing \\class #{where}" if not frame or not ClassDecl===frame
+ classname = frame.name
+ m = parse_methoddecl(line,";\s*$")
+ frame.methods[m.selector] = m
+ Out.print "#{m.rettype} #{m.selector}(VA"
+ Out.print ", #{unparse_arglist m.arglist}" if m.arglist.length>0
+ Out.print "); static void #{m.selector}_wrap(#{classname} *self, VA); "
+end
+
+def handle_def(line)
+ m = parse_methoddecl(line,"\\{?.*$")
+ term = line[/\{.*/]
+ qlass = $stack[-1]
+ raise "missing \\class #{where}" if not qlass or not ClassDecl===qlass
+ classname = qlass.name
+ n = m
+ if qlass.methods[m.selector]
+ m = qlass.methods[m.selector]
+ if !m===n then
+ STDERR.puts "ERROR: def does not match decl:"
+ STDERR.puts "#{m.where}: \\decl #{m.inspect}"
+ STDERR.puts "#{n.where}: \\def #{n.inspect}"
+ $exit = 1
+ end
+ else
+ qlass.methods[m.selector] = m
+ end
+ Out.print "void #{classname}::#{m.selector}_wrap(#{classname} *self, VA) {"
+ Out.print "static const char *methodspec = \"#{qlass.name}::#{m.selector}(#{unparse_arglist m.arglist,false})\";"
+ Out.print "#{m.rettype} foo;" if m.rettype!="void"
+ Out.print "if (argc<#{m.minargs}"
+ Out.print "||argc>#{m.maxargs}" if m.maxargs!=-1
+ Out.print ") RAISE(\"got %d args instead of %d..%d in %s\",argc,#{m.minargs},#{m.maxargs},methodspec);"
+ Out.print "foo = " if m.rettype!="void"
+ Out.print " self->#{m.selector}(argc,argv"
+ m.arglist.each_with_index{|arg,i|
+ if arg.default then
+ Out.print ",argc<#{i+1}?#{arg.default}:convert(argv[#{i}],(#{arg.type}*)0)"
+ else
+ Out.print ",convert(argv[#{i}],(#{arg.type}*)0)"
+ end
+ }
+ Out.print ");} #{m.rettype} #{classname}::#{m.selector}(VA"
+ #puts "m=#{m} n=#{n}"
+ Out.print ","+unparse_arglist(n.arglist,false) if m.arglist.length>0
+ Out.print ")#{term} "
+ qlass.methods[m.selector].done=true
+end
+
+def handle_constructor(line)
+ frame = $stack[-1]
+ raise "missing \\class #{where}" if not frame or not ClassDecl===frame
+ m = parse_methoddecl("void constructor"+line,"(.*)$")
+ Out.print "#{frame.name}(BFObject *bself, MESSAGE) : #{frame.supername}(bself,MESSAGE2) {"
+ Out.print "static const char *methodspec = \"#{frame.name}::#{m.selector}(#{unparse_arglist m.arglist,false})\";"
+
+ Out.print "if (argc<#{m.minargs}"
+ Out.print "||argc>#{m.maxargs}" if m.maxargs!=-1
+ Out.print ") RAISE(\"got %d args instead of %d..%d in %s\",argc,#{m.minargs},#{m.maxargs},methodspec);"
+ Out.print "#{m.selector}(sel,argc,argv"
+ m.arglist.each_with_index{|arg,i|
+ if arg.default then
+ Out.print ",argc<#{i+1}?#{arg.default}:convert(argv[#{i}],(#{arg.type}*)0)"
+ else
+ Out.print ",convert(argv[#{i}],(#{arg.type}*)0)"
+ end
+ }
+ Out.print ");}"
+ Out.print "#{m.rettype} #{m.selector}(MESSAGE"
+ Out.print ", #{unparse_arglist m.arglist}" if m.arglist.length>0
+ Out.print ") "+line[/\{.*/]
+end
+
+def handle_classinfo(line)
+ frame = $stack[-1]
+ cl = frame.name
+ line="{}" if /^\s*$/ =~ line
+ Out.print "static void #{cl}_startup (FClass *fclass);"
+ Out.print "static FObject *#{cl}_allocator (BFObject *bself, MESSAGE) {return new #{cl}(bself,sel,argc,argv);}"
+ Out.print "static MethodDecl #{cl}_methods[] = {"
+ Out.print frame.methods.map {|foo,method| "{ \"#{method.selector}\",(FMethod)#{frame.name}::#{method.selector}_wrap }" }.join(",")
+ Out.print "}; FClass ci#{cl} = {#{cl}_allocator,#{cl}_startup,#{cl.inspect},COUNT(#{cl}_methods),#{cl}_methods};"
+ get="void ___get(t_symbol *s=0) {t_atom a[1];"
+ frame.attrs.each {|name,attr|
+ virtual = if attr.virtual then "(0,0)" else "" end
+ get << "if (s==gensym(\"#{name}\")) set_atom(a,#{name}#{virtual}); else "
+ if frame.methods["_0_"+name].done then
+ #STDERR.puts "skipping already defined \\attr #{name}"
+ next
+ end
+ type,name,default = attr.to_a
+ handle_def "0 #{name} (#{type} #{name}) {this->#{name}=#{name}; changed(gensym(\"#{name}\"));}"
+ }
+ line.gsub!(/^\s*(\w+\s*)?\{/,"")
+ get << "RAISE(\"unknown attr %s\",s->s_name); outlet_anything(bself->outlets[bself->noutlets-1],s,1,a);}"
+ handle_def get if frame.attrs.size>0
+ Out.print "void #{frame.name}_startup (FClass *fclass) {"
+ frame.attrs.each {|name,attr| Out.print "fclass->attrs[\"#{name}\"] = new AttrDecl(\"#{name}\",\"#{attr.type}\");" }
+ Out.print line.chomp
+end
+
+def handle_grin(line)
+ fields = line.split(/\s+/)
+ i = fields[0].to_i
+ c = $stack[-1].name
+ frame = $stack[-1]
+ Out.print "template <class T> void grin_#{i}(GRIDHANDLER_ARGS(T));"
+ Out.print "template <class T> static void grinw_#{i} (GRIDHANDLER_ARGS(T));"
+ Out.print "static GridHandler grid_#{i}_hand;"
+ handle_decl "#{i} grid(GridOutlet *foo);"
+ handle_decl "#{i} list(...);"
+ handle_decl "#{i} float(float f);"
+ $stack[-1].grins[i] = fields.dup
+end
+
+def handle_end(line)
+ frame = $stack.pop
+ fields = line.split(/\s+/)
+ n = fields.length
+ if not ClassDecl===frame then raise "\\end: frame is not a \\class" end
+ cl = frame.name
+ if fields[0]!="class" or (n>1 and not /^\{/ =~ fields[1] and fields[1]!=cl) then raise "end not matching #{where}" end
+ $stack.push frame
+ frame.grins.each {|i,v|
+ cli = "#{cl}::grinw_#{i}"
+ k = case v[1]
+ when nil ; [1,1,1,1,1,1]
+ when 'int32'; [0,0,1,0,0,0]
+ when 'int' ; [1,1,1,1,0,0]
+ when 'float' ; [0,0,0,0,1,1]
+ when 'float32'; [0,0,0,0,1,0]
+ when 'float64'; [0,0,0,0,0,1]
+ else raise 'BORK BORK BORK' end
+ ks = k.map{|ke| if ke==0 then 0 else cli end}.join(",")
+ Out.print "static GridHandler #{cl}_grid_#{i}_hand = GRIN(#{ks});"
+ handle_def "#{i} grid(GridOutlet *foo) {CHECK_GRIN(#{cl},#{i});"+
+ "in[#{i}]->begin(foo);}"
+ handle_def "#{i} list(...) {CHECK_GRIN(#{cl},#{i});"+
+ "in[#{i}]->from_list(argc,argv,int32_e);}" if not frame.methods["_#{i}_list"].done
+ handle_def "#{i} float(float f) {CHECK_GRIN(#{cl},#{i});"+
+ "t_atom2 a[1]; SETFLOAT(a,f);"+
+ "in[#{i}]->from_atom(1,a);}" if not frame.methods["_#{i}_float"].done
+ }
+ if /^class\s*(\w+\s+)?\{(.*)/ =~ line then handle_classinfo("{"+$2) end
+ $stack.pop
+ Out.print "\n#undef THISCLASS\n\# #{$linenumber}\n"
+end
+
+def handle_startall(line)
+ $classes.each {|q|
+ Out.print "fclass_install(&ci#{q.name},"
+ if q.supername then Out.print "&ci#{q.supername}" else Out.print "0" end
+ Out.print ",sizeof(#{q.name}));"
+ }
+end
+
+$linenumber=1
+loop{
+ x = In.gets
+ break if not x
+ if /^\s*\\(\w+)\s*(.*)$/.match x then
+ begin
+ send("handle_#{$1}",$2)
+ Out.puts "//FCS"
+ rescue StandardError => e
+ STDERR.puts e.inspect, "at line #{$linenumber}", e.backtrace
+ File.unlink ARGV[1]
+ exit 1
+ end
+ else Out.puts x end
+ $linenumber+=1
+}
+
+exit $exit
diff --git a/externals/gridflow/src/videodev.cxx b/externals/gridflow/src/videodev.cxx
new file mode 100644
index 00000000..d908cecf
--- /dev/null
+++ b/externals/gridflow/src/videodev.cxx
@@ -0,0 +1,793 @@
+/*
+ $Id: videodev.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* bt878 on matju's comp supports only palette 4 */
+/* bt878 on heri's comp supports palettes 3, 6, 7, 8, 9, 13 */
+/* pwc supports palettes 12 and 15 */
+
+#include "gridflow.hxx.fcs"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/videodev.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include "pwc-ioctl.h"
+
+//#define error post
+static bool debug=0;
+
+/* **************************************************************** */
+
+typedef video_capability VideoCapability;
+typedef video_channel VideoChannel ;
+typedef video_tuner VideoTuner ;
+typedef video_window VideoWindow ;
+typedef video_picture VideoPicture ;
+typedef video_mbuf VideoMbuf ;
+typedef video_mmap VideoMmap ;
+
+#define FLAG(_num_,_name_,_desc_) #_name_,
+#define OPT(_num_,_name_,_desc_) #_name_,
+
+/*
+static const char *video_type_flags[] = {
+ FLAG( 0,CAPTURE, "Can capture")
+ FLAG( 1,TUNER, "Can tune")
+ FLAG( 2,TELETEXT, "Does teletext")
+ FLAG( 3,OVERLAY, "Overlay onto frame buffer")
+ FLAG( 4,CHROMAKEY, "Overlay by chromakey")
+ FLAG( 5,CLIPPING, "Can clip")
+ FLAG( 6,FRAMERAM, "Uses the frame buffer memory")
+ FLAG( 7,SCALES, "Scalable")
+ FLAG( 8,MONOCHROME, "Monochrome only")
+ FLAG( 9,SUBCAPTURE, "Can capture subareas of the image")
+ FLAG(10,MPEG_DECODER, "Can decode MPEG streams")
+ FLAG(11,MPEG_ENCODER, "Can encode MPEG streams")
+ FLAG(12,MJPEG_DECODER, "Can decode MJPEG streams")
+ FLAG(13,MJPEG_ENCODER, "Can encode MJPEG streams")
+};
+*/
+
+static const char *tuner_flags[] = {
+ FLAG(0,PAL, "")
+ FLAG(1,NTSC, "")
+ FLAG(2,SECAM, "")
+ FLAG(3,LOW, "Uses KHz not MHz")
+ FLAG(4,NORM, "Tuner can set norm")
+ FLAG(5,DUMMY5, "")
+ FLAG(6,DUMMY6, "")
+ FLAG(7,STEREO_ON,"Tuner is seeing stereo")
+ FLAG(8,RDS_ON, "Tuner is seeing an RDS datastream")
+ FLAG(9,MBS_ON, "Tuner is seeing an MBS datastream")
+};
+
+static const char *channel_flags[] = {
+ FLAG(0,TUNER,"")
+ FLAG(1,AUDIO,"")
+ FLAG(2,NORM ,"")
+};
+
+static const char *video_palette_choice[] = {
+ OPT( 0,NIL, "(nil)")
+ OPT( 1,GREY, "Linear greyscale")
+ OPT( 2,HI240, "High 240 cube (BT848)")
+ OPT( 3,RGB565, "565 16 bit RGB")
+ OPT( 4,RGB24, "24bit RGB")
+ OPT( 5,RGB32, "32bit RGB")
+ OPT( 6,RGB555, "555 15bit RGB")
+ OPT( 7,YUV422, "YUV422 capture")
+ OPT( 8,YUYV, "")
+ OPT( 9,UYVY, "The great thing about standards is ...")
+ OPT(10,YUV420, "")
+ OPT(11,YUV411, "YUV411 capture")
+ OPT(12,RAW, "RAW capture (BT848)")
+ OPT(13,YUV422P, "YUV 4:2:2 Planar")
+ OPT(14,YUV411P, "YUV 4:1:1 Planar")
+ OPT(15,YUV420P, "YUV 4:2:0 Planar")
+ OPT(16,YUV410P, "YUV 4:1:0 Planar")
+};
+
+static const char *video_mode_choice[] = {
+ OPT( 0,PAL, "pal")
+ OPT( 1,NTSC, "ntsc")
+ OPT( 2,SECAM,"secam")
+ OPT( 3,AUTO, "auto")
+};
+
+#define WH(_field_,_spec_) \
+ sprintf(buf+strlen(buf), "%s=" _spec_ " ", #_field_, self->_field_);
+#define WHYX(_name_,_fieldy_,_fieldx_) \
+ sprintf(buf+strlen(buf), "%s=(%d %d) ", #_name_, self->_fieldy_, self->_fieldx_);
+#define WHFLAGS(_field_,_table_) { \
+ char *foo; \
+ sprintf(buf+strlen(buf), "%s:%s ", #_field_, \
+ foo=flags_to_s(self->_field_,COUNT(_table_),_table_)); \
+ free(foo);}
+#define WHCHOICE(_field_,_table_) { \
+ char *foo; \
+ sprintf(buf+strlen(buf), "%s=%s; ", #_field_, \
+ foo=choice_to_s(self->_field_,COUNT(_table_),_table_));\
+ free(foo);}
+
+static char *flags_to_s(int value, int n, const char **table) {
+ char foo[256];
+ *foo = 0;
+ for(int i=0; i<n; i++) {
+ if ((value & (1<<i)) == 0) continue;
+ if (*foo) strcat(foo," | ");
+ strcat(foo,table[i]);
+ }
+ if (!*foo) strcat(foo,"0");
+ return strdup(foo);
+}
+static char *choice_to_s(int value, int n, const char **table) {
+ if (value < 0 || value >= n) {
+ char foo[64];
+ sprintf(foo,"(Unknown #%d)",value);
+ return strdup(foo);
+ } else {
+ return strdup(table[value]);
+ }
+}
+static void gfpost(VideoChannel *self) {
+ char buf[256] = "[VideoChannel] ";
+ WH(channel,"%d");
+ WH(name,"\"%.32s\"");
+ WH(tuners,"%d");
+ WHFLAGS(flags,channel_flags);
+ WH(type,"0x%04x");
+ WH(norm,"%d");
+ post("%s",buf);
+}
+static void gfpost(VideoTuner *self) {
+ char buf[256] = "[VideoTuner] ";
+ WH(tuner,"%d");
+ WH(name,"\"%.32s\"");
+ WH(rangelow,"%lu");
+ WH(rangehigh,"%lu");
+ WHFLAGS(flags,tuner_flags);
+ WHCHOICE(mode,video_mode_choice);
+ WH(signal,"%d");
+ post("%s",buf);
+}
+static void gfpost(VideoWindow *self) {
+ char buf[256] = "[VideoWindow] ";
+ WHYX(pos,y,x);
+ WHYX(size,height,width);
+ WH(chromakey,"0x%08x");
+ WH(flags,"0x%08x");
+ WH(clipcount,"%d");
+ post("%s",buf);
+}
+static void gfpost(VideoMbuf *self) {
+ char buf[256] = "[VideoMBuf] ";
+ WH(size,"%d");
+ WH(frames,"%d");
+ sprintf(buf+strlen(buf), "offsets=[");
+ for (int i=0; i<self->frames; i++) {
+ /* WH(offsets[i],"%d"); */
+ sprintf(buf+strlen(buf), "%d%s", self->offsets[i],
+ i+1==self->frames?"]":", ");
+ }
+ post("%s",buf);
+}
+static void gfpost(VideoMmap *self) {
+ char buf[256] = "[VideoMMap] ";
+ WH(frame,"%u");
+ WHYX(size,height,width);
+ WHCHOICE(format,video_palette_choice);
+ post("%s",buf);
+};
+
+/* **************************************************************** */
+
+\class FormatVideoDev : Format {
+ VideoCapability vcaps;
+ VideoPicture vp;
+ VideoMbuf vmbuf;
+ VideoMmap vmmap;
+ uint8 *image;
+ int queue[8], queuesize, queuemax, next_frame;
+ int current_channel, current_tuner;
+ bool use_mmap, use_pwc;
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ bool has_frequency, has_tuner, has_norm;
+ int fd;
+ int palettes; /* bitfield */
+
+ \constructor (string mode, string filename) {
+ queuesize=0; queuemax=2; next_frame=0; use_mmap=true; use_pwc=false; bit_packing=0; dim=0;
+ has_frequency=false;
+ has_tuner=false;
+ has_norm=false;
+ image=0;
+ f = fopen(filename.data(),"r+");
+ if (!f) RAISE("can't open device '%s': %s",filename.data(),strerror(errno));
+ fd = fileno(f);
+ initialize2();
+ }
+ void frame_finished (uint8 *buf);
+
+ void alloc_image ();
+ void dealloc_image ();
+ void frame_ask ();
+ void initialize2 ();
+ ~FormatVideoDev () {if (image) dealloc_image();}
+
+ \decl 0 bang ();
+ \grin 0 int
+
+ \attr int channel();
+ \attr int tuner();
+ \attr int norm();
+ \decl 0 size (int sy, int sx);
+ \decl 0 transfer (string sym, int queuemax=2);
+
+ \attr t_symbol *colorspace;
+ \attr int32 frequency();
+ \attr uint16 brightness();
+ \attr uint16 hue();
+ \attr uint16 colour();
+ \attr uint16 contrast();
+ \attr uint16 whiteness();
+
+ \attr bool pwc(); /* 0..1 */
+ \attr uint16 framerate();
+ \attr uint16 white_mode(); /* 0..1 */
+ \attr uint16 white_red();
+ \attr uint16 white_blue();
+ \attr uint16 white_speed();
+ \attr uint16 white_delay();
+ \attr int auto_gain();
+ \attr int noise_reduction(); /* 0..3 */
+ \attr int compression(); /* 0..3 */
+ \attr t_symbol *name;
+
+ \decl 0 get (t_symbol *s=0);
+};
+
+#define DEBUG(args...) 42
+//#define DEBUG(args...) post(args)
+
+#define IOCTL( F,NAME,ARG) \
+ (DEBUG("fd%d.ioctl(0x%08x,0x%08x)",F,NAME,ARG), ioctl(F,NAME,ARG))
+#define WIOCTL( F,NAME,ARG) \
+ (IOCTL(F,NAME,ARG)<0 && (error("ioctl %s: %s",#NAME,strerror(errno)),1))
+#define WIOCTL2(F,NAME,ARG) \
+ (IOCTL(F,NAME,ARG)<0 && (error("ioctl %s: %s",#NAME,strerror(errno)), RAISE("ioctl error"), 0))
+
+\def 0 get (t_symbol *s=0) {
+ // this is abnormal for a get-function
+ if (s==gensym("frequency") && !has_frequency ) return;
+ if (s==gensym("tuner") && !has_tuner ) return;
+ if (s==gensym("norm") && !has_norm ) return;
+ if (s==gensym("channel") && vcaps.channels<2) return;
+ if (!use_pwc && (s==gensym("white_mode") || s==gensym("white_red") || s==gensym("white_blue") ||
+ s==gensym("white_speed") || s==gensym("white_delay") || s==gensym("auto_gain") ||
+ s==gensym("noise_reduction") || s==gensym("compression") || s==gensym("framerate"))) return;
+ FObject::_0_get(argc,argv,s);
+ if (!s) {
+ t_atom a[2];
+ SETFLOAT(a+0,vcaps.minheight);
+ SETFLOAT(a+1,vcaps.minwidth);
+ outlet_anything(bself->outlets[0],gensym("minsize"),2,a);
+ SETFLOAT(a+0,vcaps.maxheight);
+ SETFLOAT(a+1,vcaps.maxwidth);
+ outlet_anything(bself->outlets[0],gensym("maxsize"),2,a);
+ char *foo = choice_to_s(vp.palette,COUNT(video_palette_choice),video_palette_choice);
+ SETSYMBOL(a,gensym(foo));
+ free(foo);
+ outlet_anything(bself->outlets[0],gensym("palette"),1,a);
+ SETSYMBOL(a,use_mmap ? gensym("mmap") : gensym("read"));
+ outlet_anything(bself->outlets[0],gensym("transfer"),1,a);
+ SETFLOAT(a+0,dim->v[0]);
+ SETFLOAT(a+1,dim->v[1]);
+ outlet_anything(bself->outlets[0],gensym("size"),2,a); // abnormal (does not use nested list)
+ }
+}
+
+\def 0 size (int sy, int sx) {
+ VideoWindow grab_win;
+ // !@#$ bug here: won't flush the frame queue
+ dim = new Dim(sy,sx,3);
+ WIOCTL(fd, VIDIOCGWIN, &grab_win);
+ if (debug) gfpost(&grab_win);
+ grab_win.clipcount = 0;
+ grab_win.flags = 0;
+ if (sy && sx) {
+ grab_win.height = sy;
+ grab_win.width = sx;
+ }
+ if (debug) gfpost(&grab_win);
+ WIOCTL(fd, VIDIOCSWIN, &grab_win);
+ WIOCTL(fd, VIDIOCGWIN, &grab_win);
+ if (debug) gfpost(&grab_win);
+}
+
+void FormatVideoDev::dealloc_image () {
+ if (!image) return;
+ if (use_mmap) {
+ munmap(image, vmbuf.size);
+ image=0;
+ } else {
+ delete[] (uint8 *)image;
+ }
+}
+
+void FormatVideoDev::alloc_image () {
+ if (use_mmap) {
+ WIOCTL2(fd, VIDIOCGMBUF, &vmbuf);
+ //gfpost(&vmbuf);
+ //size_t size = vmbuf.frames > 4 ? vmbuf.offsets[4] : vmbuf.size;
+ image = (uint8 *)mmap(0,vmbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
+ if (((long)image)==-1) {image=0; RAISE("mmap: %s", strerror(errno));}
+ } else {
+ image = new uint8[dim->prod(0,1)*bit_packing->bytes];
+ }
+}
+
+void FormatVideoDev::frame_ask () {
+ if (queuesize>=queuemax) RAISE("queue is full (queuemax=%d)",queuemax);
+ if (queuesize>=vmbuf.frames) RAISE("queue is full (vmbuf.frames=%d)",vmbuf.frames);
+ vmmap.frame = queue[queuesize++] = next_frame;
+ vmmap.format = vp.palette;
+ vmmap.width = dim->get(1);
+ vmmap.height = dim->get(0);
+ WIOCTL2(fd, VIDIOCMCAPTURE, &vmmap);
+ //gfpost(&vmmap);
+ next_frame = (next_frame+1) % vmbuf.frames;
+}
+
+static uint8 clip(int x) {return x<0?0 : x>255?255 : x;}
+
+void FormatVideoDev::frame_finished (uint8 *buf) {
+ string cs = colorspace->s_name;
+ int downscale = cs=="magic";
+ /* picture is converted here. */
+ int sy = dim->get(0)>>downscale;
+ int sx = dim->get(1)>>downscale;
+ int bs = dim->prod(1)>>downscale;
+ uint8 b2[bs];
+ //post("sy=%d sx=%d bs=%d",sy,sx,bs);
+ //post("frame_finished, vp.palette = %d; colorspace = %s",vp.palette,cs.data());
+ if (vp.palette==VIDEO_PALETTE_YUV420P) {
+ GridOutlet out(this,0,cs=="magic"?new Dim(sy,sx,3):(Dim *)dim,cast);
+ if (cs=="y") {
+ out.send(sy*sx,buf);
+ } else if (cs=="rgb") {
+ for(int y=0; y<sy; y++) {
+ uint8 *bufy = buf+sx* y;
+ uint8 *bufu = buf+sx*sy +(sx/2)*(y/2);
+ uint8 *bufv = buf+sx*sy*5/4+(sx/2)*(y/2);
+ int Y1,Y2,U,V;
+ for (int x=0,xx=0; x<sx; x+=2,xx+=6) {
+ Y1=bufy[x] - 16;
+ Y2=bufy[x+1] - 16;
+ U=bufu[x/2] - 128;
+ V=bufv[x/2] - 128;
+ b2[xx+0]=clip((298*Y1 + 409*V)>>8);
+ b2[xx+1]=clip((298*Y1 - 100*U - 208*V)>>8);
+ b2[xx+2]=clip((298*Y1 + 516*U )>>8);
+ b2[xx+3]=clip((298*Y2 + 409*V)>>8);
+ b2[xx+4]=clip((298*Y2 - 100*U - 208*V)>>8);
+ b2[xx+5]=clip((298*Y2 + 516*U )>>8);
+ }
+ out.send(bs,b2);
+ }
+ } else if (cs=="yuv") {
+ for(int y=0; y<sy; y++) {
+ uint8 *bufy = buf+sx* y;
+ uint8 *bufu = buf+sx*sy +(sx/2)*(y/2);
+ uint8 *bufv = buf+sx*sy*5/4+(sx/2)*(y/2);
+ int U,V;
+ for (int x=0,xx=0; x<sx; x+=2,xx+=6) {
+ U=bufu[x/2];
+ V=bufv[x/2];
+ b2[xx+0]=clip(((bufy[x+0]-16)*298)>>8);
+ b2[xx+1]=clip(128+(((U-128)*293)>>8));
+ b2[xx+2]=clip(128+(((V-128)*293)>>8));
+ b2[xx+3]=clip(((bufy[x+1]-16)*298)>>8);
+ b2[xx+4]=clip(128+(((U-128)*293)>>8));
+ b2[xx+5]=clip(128+(((V-128)*293)>>8));
+ }
+ out.send(bs,b2);
+ }
+ } else if (cs=="magic") {
+ for(int y=0; y<sy; y++) {
+ uint8 *bufy = buf +4*sx*y;
+ uint8 *bufu = buf+4*sx*sy+ sx*y;
+ uint8 *bufv = buf+5*sx*sy+ sx*y;
+ for (int x=0,xx=0; x<sx; x++,xx+=3) {
+ b2[xx+0]=bufy[x+x];
+ b2[xx+1]=bufu[x];
+ b2[xx+2]=bufv[x];
+ }
+ out.send(bs,b2);
+ }
+ }
+ } else if (vp.palette==VIDEO_PALETTE_RGB32 || vp.palette==VIDEO_PALETTE_RGB24 || vp.palette==VIDEO_PALETTE_RGB565) {
+ GridOutlet out(this,0,dim,cast);
+ uint8 rgb[sx*3];
+ uint8 b2[sx*3];
+ if (cs=="y") {
+ for(int y=0; y<sy; y++) {
+ bit_packing->unpack(sx,buf+y*sx*bit_packing->bytes,rgb);
+ for (int x=0,xx=0; x<sx; x+=2,xx+=6) {
+ b2[x+0] = (76*rgb[xx+0]+150*rgb[xx+1]+29*rgb[xx+2])>>8;
+ b2[x+1] = (76*rgb[xx+3]+150*rgb[xx+4]+29*rgb[xx+5])>>8;
+ }
+ out.send(bs,b2);
+ }
+ } else if (cs=="rgb") {
+ for(int y=0; y<sy; y++) {
+ bit_packing->unpack(sx,buf+y*sx*bit_packing->bytes,rgb);
+ out.send(bs,rgb);
+ }
+ } else if (cs=="yuv") {
+ for(int y=0; y<sy; y++) {
+ bit_packing->unpack(sx,buf+y*sx*bit_packing->bytes,rgb);
+ for (int x=0,xx=0; x<sx; x+=2,xx+=6) {
+ b2[xx+0] = clip( (( 76*rgb[xx+0] + 150*rgb[xx+1] + 29*rgb[xx+2])>>8));
+ b2[xx+1] = clip(128+((- 44*rgb[xx+0] - 85*rgb[xx+1] + 108*rgb[xx+2])>>8));
+ b2[xx+2] = clip(128+(( 128*rgb[xx+0] - 108*rgb[xx+1] - 21*rgb[xx+2])>>8));
+ b2[xx+3] = clip( (( 76*rgb[xx+3] + 150*rgb[xx+4] + 29*rgb[xx+5])>>8));
+ b2[xx+4] = clip(128+((- 44*rgb[xx+3] - 85*rgb[xx+4] + 108*rgb[xx+5])>>8));
+ b2[xx+5] = clip(128+(( 128*rgb[xx+3] - 108*rgb[xx+4] - 21*rgb[xx+5])>>8));
+ }
+ out.send(bs,b2);
+ }
+ } else if (cs=="magic") {
+ RAISE("magic colorspace not supported with a RGB palette");
+ }
+ } else {
+ RAISE("unsupported palette %d",vp.palette);
+ }
+}
+
+/* these are factors for RGB to analog YUV */
+// Y = 66*R + 129*G + 25*B
+// U = - 38*R - 74*G + 112*B
+// V = 112*R - 94*G - 18*B
+
+// strange that read2 is not used and read3 is used instead
+static int read2(int fd, uint8 *image, int n) {
+ int r=0;
+ while (n>0) {
+ int rr=read(fd,image,n);
+ if (rr<0) return rr; else {r+=rr; image+=rr; n-=rr;}
+ }
+ return r;
+}
+
+static int read3(int fd, uint8 *image, int n) {
+ int r=read(fd,image,n);
+ if (r<0) return r;
+ return n;
+}
+
+\def 0 bang () {
+ if (!image) alloc_image();
+ if (!use_mmap) {
+ /* picture is read at once by frame() to facilitate debugging. */
+ int tot = dim->prod(0,1) * bit_packing->bytes;
+ int n = (int) read3(fd,image,tot);
+ if (n==tot) frame_finished(image);
+ if (0> n) RAISE("error reading: %s", strerror(errno));
+ if (n < tot) RAISE("unexpectedly short picture: %d of %d",n,tot);
+ return;
+ }
+ while(queuesize<queuemax) frame_ask();
+ vmmap.frame = queue[0];
+ //uint64 t0 = gf_timeofday();
+ WIOCTL2(fd, VIDIOCSYNC, &vmmap);
+ //uint64 t1 = gf_timeofday();
+ //if (t1-t0 > 100) gfpost("VIDIOCSYNC delay: %d us",t1-t0);
+ frame_finished(image+vmbuf.offsets[queue[0]]);
+ queuesize--;
+ for (int i=0; i<queuesize; i++) queue[i]=queue[i+1];
+ frame_ask();
+}
+
+GRID_INLET(0) {
+ RAISE("can't write.");
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\def 0 norm (int value) {
+ VideoTuner vtuner;
+ vtuner.tuner = current_tuner;
+ if (value<0 || value>3) RAISE("norm must be in range 0..3");
+ if (0> IOCTL(fd, VIDIOCGTUNER, &vtuner)) {
+ post("no tuner #%d", value);
+ } else {
+ vtuner.mode = value;
+ gfpost(&vtuner);
+ WIOCTL(fd, VIDIOCSTUNER, &vtuner);
+ }
+}
+
+\def int norm () {
+ VideoTuner vtuner;
+ vtuner.tuner = current_tuner;
+ if (0> IOCTL(fd, VIDIOCGTUNER, &vtuner)) {post("no tuner #%d", current_tuner); return -1;}
+ return vtuner.mode;
+}
+
+\def 0 tuner (int value) {
+ VideoTuner vtuner;
+ vtuner.tuner = current_tuner = value;
+ if (0> IOCTL(fd, VIDIOCGTUNER, &vtuner)) RAISE("no tuner #%d", value);
+ vtuner.mode = VIDEO_MODE_NTSC; //???
+ gfpost(&vtuner);
+ WIOCTL(fd, VIDIOCSTUNER, &vtuner);
+ has_norm = (vtuner.mode<=3);
+ int meuh;
+ has_frequency = (ioctl(fd, VIDIOCGFREQ, &meuh)>=0);
+}
+\def int tuner () {return current_tuner;}
+
+#define warn(fmt,stuff...) post("warning: " fmt,stuff)
+
+\def 0 channel (int value) {
+ VideoChannel vchan;
+ vchan.channel = value;
+ current_channel = value;
+ if (0> IOCTL(fd, VIDIOCGCHAN, &vchan)) warn("no channel #%d", value);
+ //gfpost(&vchan);
+ WIOCTL(fd, VIDIOCSCHAN, &vchan);
+ if (vcaps.type & VID_TYPE_TUNER) _0_tuner(0,0,0);
+ has_tuner = (vcaps.type & VID_TYPE_TUNER && vchan.tuners > 1);
+}
+\def int channel () {return current_channel;}
+
+\def 0 transfer (string sym, int queuemax=2) {
+ if (sym=="read") {
+ dealloc_image();
+ use_mmap = false;
+ post("transfer read");
+ } else if (sym=="mmap") {
+ dealloc_image();
+ use_mmap = true;
+ alloc_image();
+ queuemax=min(8,min(queuemax,vmbuf.frames));
+ post("transfer mmap with queuemax=%d (max max is vmbuf.frames=%d)", queuemax,vmbuf.frames);
+ this->queuemax=queuemax;
+ } else RAISE("don't know that transfer mode");
+}
+
+#define PICTURE_ATTR(_name_) {\
+ WIOCTL(fd, VIDIOCGPICT, &vp); \
+ vp._name_ = _name_; \
+ WIOCTL(fd, VIDIOCSPICT, &vp);}
+
+#define PICTURE_ATTRGET(_name_) { \
+ WIOCTL(fd, VIDIOCGPICT, &vp); \
+ /*gfpost("getting %s=%d",#_name_,vp._name_);*/ \
+ return vp._name_;}
+
+\def uint16 brightness () {PICTURE_ATTRGET(brightness)}
+\def 0 brightness (uint16 brightness){PICTURE_ATTR( brightness)}
+\def uint16 hue () {PICTURE_ATTRGET(hue)}
+\def 0 hue (uint16 hue) {PICTURE_ATTR( hue)}
+\def uint16 colour () {PICTURE_ATTRGET(colour)}
+\def 0 colour (uint16 colour) {PICTURE_ATTR( colour)}
+\def uint16 contrast () {PICTURE_ATTRGET(contrast)}
+\def 0 contrast (uint16 contrast) {PICTURE_ATTR( contrast)}
+\def uint16 whiteness () {PICTURE_ATTRGET(whiteness)}
+\def 0 whiteness (uint16 whiteness) {PICTURE_ATTR( whiteness)}
+\def int32 frequency () {
+ int32 value;
+ //if (ioctl(fd, VIDIOCGFREQ, &value)<0) {has_frequency=false; return 0;}
+ WIOCTL(fd, VIDIOCGFREQ, &value);
+ return value;
+}
+\def 0 frequency (int32 frequency) {
+ long frequency_ = frequency;
+ WIOCTL(fd, VIDIOCSFREQ, &frequency_);
+}
+
+\def 0 colorspace (t_symbol *colorspace) { /* y yuv rgb magic */
+ string c = colorspace->s_name;
+ if (c=="y") {}
+ else if (c=="yuv") {}
+ else if (c=="rgb") {}
+ else if (c=="magic") {}
+ else RAISE("got '%s' but supported colorspaces are: y yuv rgb magic",c.data());
+ WIOCTL(fd, VIDIOCGPICT, &vp);
+ int palette = (palettes&(1<<VIDEO_PALETTE_RGB24)) ? VIDEO_PALETTE_RGB24 :
+ (palettes&(1<<VIDEO_PALETTE_RGB32)) ? VIDEO_PALETTE_RGB32 :
+ (palettes&(1<<VIDEO_PALETTE_RGB565)) ? VIDEO_PALETTE_RGB565 :
+ VIDEO_PALETTE_YUV420P;
+ vp.palette = palette;
+ WIOCTL(fd, VIDIOCSPICT, &vp);
+ WIOCTL(fd, VIDIOCGPICT, &vp);
+ if (vp.palette != palette) {
+ post("this driver is unsupported: it wants palette %d instead of %d",vp.palette,palette);
+ return;
+ }
+ if (palette == VIDEO_PALETTE_RGB565) {
+ //uint32 masks[3] = { 0x00fc00,0x003e00,0x00001f };
+ uint32 masks[3] = { 0x00f800,0x007e0,0x00001f };
+ bit_packing = new BitPacking(is_le(),2,3,masks);
+ } else if (palette == VIDEO_PALETTE_RGB32) {
+ uint32 masks[3] = { 0xff0000,0x00ff00,0x0000ff };
+ bit_packing = new BitPacking(is_le(),4,3,masks);
+ } else {
+ uint32 masks[3] = { 0xff0000,0x00ff00,0x0000ff };
+ bit_packing = new BitPacking(is_le(),3,3,masks);
+ }
+ this->colorspace=gensym(c.data());
+ dim = new Dim(dim->v[0],dim->v[1],c=="y"?1:3);
+}
+
+\def bool pwc () {return use_pwc;}
+\def 0 pwc (bool pwc) {use_pwc=pwc;}
+
+void set_pan_and_tilt(int fd, char what, int pan, int tilt) { /*unused*/
+ // if (!use_pwc) return;
+ struct pwc_mpt_angles pma;
+ pma.absolute=1;
+ WIOCTL(fd, VIDIOCPWCMPTGANGLE, &pma);
+ pma.pan = pan;
+ pma.tilt = tilt;
+ WIOCTL(fd, VIDIOCPWCMPTSANGLE, &pma);
+}
+
+\def uint16 framerate() {
+ if (!use_pwc) return 0;
+ struct video_window vwin;
+ WIOCTL(fd, VIDIOCGWIN, &vwin);
+ return (vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT;
+}
+
+\def 0 framerate(uint16 framerate) {
+ if (!use_pwc) return;
+ struct video_window vwin;
+ WIOCTL(fd, VIDIOCGWIN, &vwin);
+ vwin.flags &= ~PWC_FPS_FRMASK;
+ vwin.flags |= (framerate << PWC_FPS_SHIFT) & PWC_FPS_FRMASK;
+ WIOCTL(fd, VIDIOCSWIN, &vwin);
+}
+
+/* those functions are still mostly unused */
+//void set_compression_preference(int fd, int pref) {if (use_pwc) WIOCTL(fd, VIDIOCPWCSCQUAL, &pref);}
+
+\def int auto_gain() {int auto_gain=0; if (use_pwc) WIOCTL(fd, VIDIOCPWCGAGC, &auto_gain); return auto_gain;}
+\def 0 auto_gain (int auto_gain) {if (use_pwc) WIOCTL(fd, VIDIOCPWCSAGC, &auto_gain);}
+
+//void set_shutter_speed(int fd, int pref) {if (use_pwc) WIOCTL(fd, VIDIOCPWCSSHUTTER, &pref);}
+
+\def uint16 white_mode () {
+ if (!use_pwc) return 0;
+ struct pwc_whitebalance pwcwb;
+ WIOCTL(fd, VIDIOCPWCGAWB, &pwcwb);
+ if (pwcwb.mode==PWC_WB_AUTO) return 0;
+ if (pwcwb.mode==PWC_WB_MANUAL) return 1;
+ return 2;
+}
+
+\def 0 white_mode (uint16 white_mode) {
+ if (!use_pwc) return;
+ struct pwc_whitebalance pwcwb;
+ WIOCTL(fd, VIDIOCPWCGAWB, &pwcwb);
+ if (white_mode==0) pwcwb.mode = PWC_WB_AUTO;
+ else if (white_mode==1) pwcwb.mode = PWC_WB_MANUAL;
+ /*else if (strcasecmp(mode, "indoor") == 0) pwcwb.mode = PWC_WB_INDOOR;*/
+ /*else if (strcasecmp(mode, "outdoor") == 0) pwcwb.mode = PWC_WB_OUTDOOR;*/
+ /*else if (strcasecmp(mode, "fl") == 0) pwcwb.mode = PWC_WB_FL;*/
+ else {error("unknown mode number %d", white_mode); return;}
+ WIOCTL(fd, VIDIOCPWCSAWB, &pwcwb);}
+
+\def uint16 white_red() {if (!use_pwc) return 0;
+ struct pwc_whitebalance pwcwb; WIOCTL(fd, VIDIOCPWCGAWB, &pwcwb); return pwcwb.manual_red;}
+\def uint16 white_blue() {if (!use_pwc) return 0;
+ struct pwc_whitebalance pwcwb; WIOCTL(fd, VIDIOCPWCGAWB, &pwcwb); return pwcwb.manual_blue;}
+\def 0 white_red(uint16 white_red) {if (!use_pwc) return;
+ struct pwc_whitebalance pwcwb; WIOCTL(fd, VIDIOCPWCGAWB, &pwcwb);
+ pwcwb.manual_red = white_red; WIOCTL(fd, VIDIOCPWCSAWB, &pwcwb);}
+\def 0 white_blue(uint16 white_blue) {if (!use_pwc) return;
+ struct pwc_whitebalance pwcwb; WIOCTL(fd, VIDIOCPWCGAWB, &pwcwb);
+ pwcwb.manual_blue = white_blue;WIOCTL(fd, VIDIOCPWCSAWB, &pwcwb);}
+
+\def uint16 white_speed() {if (!use_pwc) return 0;
+ struct pwc_wb_speed pwcwbs; WIOCTL(fd, VIDIOCPWCGAWBSPEED, &pwcwbs); return pwcwbs.control_speed;}
+\def uint16 white_delay() {if (!use_pwc) return 0;
+ struct pwc_wb_speed pwcwbs; WIOCTL(fd, VIDIOCPWCGAWBSPEED, &pwcwbs); return pwcwbs.control_delay;}
+\def 0 white_speed(uint16 white_speed) {if (!use_pwc) return;
+ struct pwc_wb_speed pwcwbs; WIOCTL(fd, VIDIOCPWCGAWBSPEED, &pwcwbs);
+ pwcwbs.control_speed = white_speed; WIOCTL(fd, VIDIOCPWCSAWBSPEED, &pwcwbs);}
+\def 0 white_delay(uint16 white_delay) {if (!use_pwc) return;
+ struct pwc_wb_speed pwcwbs; WIOCTL(fd, VIDIOCPWCGAWBSPEED, &pwcwbs);
+ pwcwbs.control_delay = white_delay; WIOCTL(fd, VIDIOCPWCSAWBSPEED, &pwcwbs);}
+
+void set_led_on_time(int fd, int val) {
+ struct pwc_leds pwcl; WIOCTL(fd, VIDIOCPWCGLED, &pwcl);
+ pwcl.led_on = val; WIOCTL(fd, VIDIOCPWCSLED, &pwcl);}
+void set_led_off_time(int fd, int val) {
+ struct pwc_leds pwcl; WIOCTL(fd, VIDIOCPWCGLED, &pwcl);
+ pwcl.led_off = val; WIOCTL(fd, VIDIOCPWCSLED, &pwcl);}
+void set_sharpness(int fd, int val) {WIOCTL(fd, VIDIOCPWCSCONTOUR, &val);}
+void set_backlight_compensation(int fd, int val) {WIOCTL(fd, VIDIOCPWCSBACKLIGHT, &val);}
+void set_antiflicker_mode(int fd, int val) {WIOCTL(fd, VIDIOCPWCSFLICKER, &val);}
+
+\def int noise_reduction() {
+ if (!use_pwc) return 0;
+ int noise_reduction;
+ WIOCTL(fd, VIDIOCPWCGDYNNOISE, &noise_reduction);
+ return noise_reduction;
+}
+\def 0 noise_reduction(int noise_reduction) {
+ if (!use_pwc) return;
+ WIOCTL(fd, VIDIOCPWCSDYNNOISE, &noise_reduction);
+}
+\def int compression() {
+ if (!use_pwc) return 0;
+ int compression;
+ WIOCTL(fd, VIDIOCPWCSCQUAL, &compression);
+ return compression;
+}
+\def 0 compression(int compression) {
+ if (!use_pwc) return;
+ WIOCTL(fd, VIDIOCPWCGCQUAL, &compression);
+}
+
+void FormatVideoDev::initialize2 () {
+ WIOCTL(fd, VIDIOCGCAP, &vcaps);
+ _0_size(0,0,vcaps.maxheight,vcaps.maxwidth);
+ char namebuf[33];
+ memcpy(namebuf,vcaps.name,sizeof(vcaps.name));
+ int i;
+ for (i=32; i>=1; i--) if (!namebuf[i] || !isspace(namebuf[i])) break;
+ namebuf[i]=0;
+ while (--i>=0) if (isspace(namebuf[i])) namebuf[i]='_';
+ name = gensym(namebuf);
+ WIOCTL(fd, VIDIOCGPICT,&vp);
+ palettes=0;
+ int checklist[] = {VIDEO_PALETTE_RGB565,VIDEO_PALETTE_RGB24,VIDEO_PALETTE_RGB32,VIDEO_PALETTE_YUV420P};
+#if 1
+ for (size_t i=0; i<sizeof(checklist)/sizeof(*checklist); i++) {
+ int p = checklist[i];
+#else
+ for (size_t p=0; p<17; p++) {
+#endif
+ vp.palette = p;
+ ioctl(fd, VIDIOCSPICT,&vp);
+ ioctl(fd, VIDIOCGPICT,&vp);
+ if (vp.palette == p) {
+ palettes |= 1<<p;
+ post("palette %d supported",p);
+ }
+ }
+ _0_colorspace(0,0,gensym("rgb"));
+ _0_channel(0,0,0);
+}
+
+\end class FormatVideoDev {install_format("#io.videodev",4,"");}
+void startup_videodev () {
+ \startall
+}
diff --git a/externals/gridflow/src/x11.cxx b/externals/gridflow/src/x11.cxx
new file mode 100644
index 00000000..46778089
--- /dev/null
+++ b/externals/gridflow/src/x11.cxx
@@ -0,0 +1,664 @@
+/*
+ $Id: x11.c 4620 2009-11-01 21:16:58Z matju $
+
+ GridFlow
+ Copyright (c) 2001-2009 by Mathieu Bouchard
+
+ 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.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Note: some of the code was adapted from PDP's (the XVideo stuff).
+*/
+#include "gridflow.hxx.fcs"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <string>
+#include <sys/time.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+//#include <X11/StringDefs.h>
+#ifdef HAVE_X11_SHARED_MEMORY
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+#ifdef HAVE_X11_XVIDEO
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+#endif
+
+/* X11 Error Handler type */
+typedef int (*XEH)(Display *, XErrorEvent *);
+
+struct FormatX11;
+void FormatX11_call(FormatX11 *p);
+
+\class FormatX11 : Format {
+/* at the Display/Screen level */
+ Display *display; /* connection to xserver */
+ Visual *visual; /* screen properties */
+ Window root_window;
+ Colormap colormap;/* for 256-color mode */
+ short depth;
+ bool use_stripes; /* use alternate conversion in 256-color mode */
+ bool shared_memory;
+ bool xvideo;
+/* at the Window level */
+ Window window; /* X11 window number */
+ Window parent; /* X11 window number of the parent */
+ GC imagegc; /* X11 graphics context (like java.awt.Graphics) */
+ XImage *ximage; /* X11 image descriptor */
+ uint8 *image; /* the real data (that XImage binds to) */
+ bool is_owner;
+ int32 pos[2];
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ bool lock_size;
+ bool override_redirect;
+ t_clock *clock;
+ std::string title;
+#ifdef HAVE_X11_SHARED_MEMORY
+ XShmSegmentInfo *shm_info; /* to share memory with X11/Unix */
+#endif
+#ifdef HAVE_X11_XVIDEO
+ int xv_format;
+ int xv_port;
+ XvImage *xvi; /* ils sont fous ces romains */
+ unsigned char *data;
+ int last_encoding;
+#endif
+ ~FormatX11 () {
+ clock_unset(clock);
+ if (is_owner) XDestroyWindow(display,window);
+ XSync(display,0);
+ dealloc_image();
+ if (imagegc) XFreeGC(display,imagegc);
+ XCloseDisplay(display);
+ }
+ template <class T> void frame_by_type (T bogus);
+ void show_section(int x, int y, int sx, int sy);
+ void set_wm_hints ();
+ void dealloc_image ();
+ bool alloc_image (int sx, int sy);
+ void resize_window (int sx, int sy);
+ void open_display(const char *disp_string);
+ void report_pointer(int y, int x, int state);
+ void prepare_colormap();
+ Window search_window_tree (Window xid, Atom key, const char *value, int level=0);
+ \constructor (...) {
+ shared_memory=false; xvideo=false; use_stripes=false; window=0; ximage=0; image=0; is_owner=true;
+ dim=0; lock_size=false; override_redirect=false; clock=0; imagegc=0;
+#ifdef HAVE_X11_SHARED_MEMORY
+ shm_info=0;
+#endif
+ int sy=240, sx=320; // defaults
+ argv++, argc--;
+ t_symbol *domain = argc<1 ? gensym("here") : argv[0];
+ int i;
+ char host[256];
+ if (domain==gensym("here")) {
+ open_display(0);
+ i=1;
+ } else if (domain==gensym("local")) {
+ if (argc<2) RAISE("open x11 local: not enough args");
+ sprintf(host,":%d",int32(argv[1]));
+ open_display(host);
+ i=2;
+ } else if (domain==gensym("remote")) {
+ if (argc<3) RAISE("open x11 remote: not enough args");
+ sprintf(host,"%s:%d",string(argv[1]).data(),int32(argv[2]));
+ open_display(host);
+ i=3;
+ } else if (domain==gensym("display")) {
+ if (argc<2) RAISE("open x11 display: not enough args");
+ strcpy(host,string(argv[1]).data());
+ for (int k=0; host[k]; k++) if (host[k]=='%') host[k]==':';
+ post("mode `display', DISPLAY=`%s'",host);
+ open_display(host);
+ i=2;
+ } else RAISE("x11 destination syntax error");
+ for(;i<argc;i++) {
+ if (argv[i]==gensym("override_redirect")) override_redirect = true;
+ else if (argv[i]==gensym("use_stripes")) use_stripes = true;
+ else break; /*RAISE("argument '%s' not recognized",string(argv[i]).data());*/
+ }
+ pos[1]=pos[0]=0;
+ parent = root_window;
+ if (i>=argc) {
+ } else {
+ const t_atom2 &winspec = argv[i];
+ if (winspec==gensym("root")) {
+ window = root_window;
+ is_owner = false;
+ } else if (winspec==gensym("embed")) {
+ string title = argv[i+1];
+ sy = sx = pos[0] = pos[1] = 0;
+ parent = search_window_tree(root_window,XInternAtom(display,"WM_NAME",0),title.data());
+ if (parent == 0xDeadBeef) RAISE("Window not found.");
+ } else if (winspec==gensym("embed_by_id")) {
+ const char *winspec2 = string(argv[i+1]).data();
+ if (strncmp(winspec2,"0x",2)==0) {
+ parent = strtol(winspec2+2,0,16);
+ } else {
+ parent = atoi(winspec2);
+ }
+ } else {
+ if (winspec.a_type==A_SYMBOL) {
+ const char *winspec2 = string(winspec).data();
+ if (strncmp(winspec2,"0x",2)==0) {
+ window = strtol(winspec2+2,0,16);
+ } else {
+ window = atoi(winspec2); // huh?
+ }
+ } else {
+ window = INT(winspec);
+ }
+ is_owner = false;
+ sy = sx = pos[0] = pos[1] = 0;
+ }
+ }
+ resize_window(sx,sy); // "resize" also takes care of creation
+ if (is_owner) {
+ Atom wmDeleteAtom = XInternAtom(display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(display,window,&wmDeleteAtom,1);
+ }
+ Visual *v = visual;
+ int disp_is_le = !ImageByteOrder(display);
+ int bpp = ximage->bits_per_pixel;
+ switch(visual->c_class) {
+ case TrueColor: case DirectColor: {
+ uint32 masks[3] = { v->red_mask, v->green_mask, v->blue_mask };
+ bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks);
+ } break;
+ case PseudoColor: {
+ uint32 masks[3] = { 0x07, 0x38, 0xC0 }; // BBGGGRRR
+ bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks);
+ } break;
+ default: RAISE("huh?");
+ }
+ clock = clock_new(this,(t_method)FormatX11_call);
+ clock_delay(clock,0);
+ show_section(0,0,sx,sy);
+ if ((mode&4)!=0) {
+ Window root; int x,y; unsigned sx,sy,sb,depth;
+ XGetGeometry(display,window,&root,&x,&y,&sx,&sy,&sb,&depth);
+ post("sx=%d sy=%d",sx,sy);
+ _0_out_size(argc,argv,sy,sx);
+ }
+ }
+
+ \decl 0 bang ();
+ void call ();
+ \decl 0 out_size (int sy, int sx);
+ \decl 0 setcursor (int shape);
+ \decl 0 hidecursor ();
+ \decl 0 set_geometry (int y, int x, int sy, int sx);
+ \decl 0 move (int y, int x);
+ \decl 0 shared_memory (bool toggle);
+ \decl 0 xvideo (bool toggle);
+ \decl 0 title (string title="");
+ \decl 0 warp (int y, int x);
+ \grin 0 int
+};
+
+/* ---------------------------------------------------------------- */
+
+void FormatX11::show_section(int x, int y, int sx, int sy) {
+ if ((mode&2)==0) return;
+ int zy=dim->get(0), zx=dim->get(1);
+ if (y>zy||x>zx) return;
+ if (y+sy>zy) sy=zy-y;
+ if (x+sx>zx) sx=zx-x;
+#ifndef HAVE_X11_XVIDEO
+ if (xvideo) RAISE("xvideo not available (recompile)");
+#endif
+#ifndef HAVE_X11_SHARED_MEMORY
+ if (shared_memory) RAISE("xshm not available (recompile)");
+#endif
+ if (xvideo) {
+#ifdef HAVE_X11_XVIDEO
+ if (shared_memory) {
+#ifdef HAVE_X11_SHARED_MEMORY
+
+#endif // shm
+ } else {
+ XvPutImage(display,port,window,imagegc,ximage,
+ xvi, 0, 0, image_width, image_height,
+ drwX - (vo_panscan_x >> 1), drwY - (vo_panscan_y >> 1),
+ vo_dwidth + vo_panscan_x,
+ vo_dheight + vo_panscan_y);
+
+ }
+#endif // xvideo
+ } else {
+ if (shared_memory) {
+#ifdef HAVE_X11_SHARED_MEMORY
+ XSync(display,False);
+ XShmPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy,False);
+ XFlush(display);
+ //XPutImage( display,window,imagegc,ximage,x,y,x,y,sx,sy);
+ // should completion events be waited for? looks like a bug
+#endif // xshm
+ } else {
+ XPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy);
+ XFlush(display);
+ }
+ }
+}
+
+/* window manager hints, defines the window as non-resizable */
+void FormatX11::set_wm_hints () {
+ if (!is_owner) return;
+ XWMHints wmh;
+ char buf[256],*bufp=buf;
+ if (title=="") {
+ sprintf(buf,"GridFlow (%d,%d,%d)",dim->get(0),dim->get(1),dim->get(2));
+ } else {
+ sprintf(buf,"%.255s",title.data());
+ }
+ XTextProperty wtitle; XStringListToTextProperty((char **)&bufp, 1, &wtitle);
+ XSizeHints sh;
+ sh.flags=PSize|PMaxSize|PMinSize;
+ sh.min_width = sh.max_width = sh.width = dim->get(1);
+ sh.min_height = sh.max_height = sh.height = dim->get(0);
+ wmh.input = True;
+ wmh.flags = InputHint;
+ XSetWMProperties(display,window,&wtitle,&wtitle,0,0,&sh,&wmh,0);
+ XFree(wtitle.value); // do i really have to do that?
+}
+
+void FormatX11::report_pointer(int y, int x, int state) {
+ t_atom a[3];
+ SETFLOAT(a+0,y);
+ SETFLOAT(a+1,x);
+ SETFLOAT(a+2,state);
+ outlet_anything(bself->outlets[0],gensym("position"),COUNT(a),a);
+}
+
+void FormatX11::call() {
+ XEvent e;
+ for (;;) {
+ int xpending = XEventsQueued(display, QueuedAfterFlush);
+ if (!xpending) break;
+ XNextEvent(display,&e);
+ switch (e.type) {
+ case Expose:{
+ XExposeEvent *ex = (XExposeEvent *)&e;
+ if (mode==2) show_section(ex->x,ex->y,ex->width,ex->height);
+ }break;
+ case ButtonPress:{
+ XButtonEvent *eb = (XButtonEvent *)&e;
+ eb->state |= 128<<eb->button;
+ report_pointer(eb->y,eb->x,eb->state);
+ }break;
+ case ButtonRelease:{
+ XButtonEvent *eb = (XButtonEvent *)&e;
+ eb->state &= ~(128<<eb->button);
+ report_pointer(eb->y,eb->x,eb->state);
+ }break;
+ case KeyPress:
+ case KeyRelease:{
+ XKeyEvent *ek = (XKeyEvent *)&e;
+ //XLookupString(ek, buf, 63, 0, 0);
+ char *kss = XKeysymToString(XLookupKeysym(ek, 0));
+ char buf[64];
+ if (!kss) return; /* unknown keys ignored */
+ if (isdigit(*kss)) sprintf(buf,"D%s",kss); else strcpy(buf,kss);
+ t_atom at[4];
+ t_symbol *sel = gensym(const_cast<char *>(e.type==KeyPress ? "keypress" : "keyrelease"));
+ SETFLOAT(at+0,ek->y);
+ SETFLOAT(at+1,ek->x);
+ SETFLOAT(at+2,ek->state);
+ SETSYMBOL(at+3,gensym(buf));
+ outlet_anything(bself->outlets[0],sel,4,at);
+ //XFree(kss);
+ }break;
+ case MotionNotify:{
+ XMotionEvent *em = (XMotionEvent *)&e;
+ report_pointer(em->y,em->x,em->state);
+ }break;
+ case DestroyNotify:{
+ post("This window is being closed, so this handler will close too!");
+ delete this; /* really! what else could i do here anyway? */
+ return;
+ }break;
+ case ConfigureNotify:break; // as if we cared
+ }
+ }
+ clock_delay(clock,20);
+}
+void FormatX11_call(FormatX11 *p) {p->call();}
+
+\def 0 bang () {
+ XGetSubImage(display, window, 0, 0, dim->get(1), dim->get(0), (unsigned)-1, ZPixmap, ximage, 0, 0);
+ GridOutlet out(this,0,dim,cast);
+ int sy=dim->get(0), sx=dim->get(1), bs=dim->prod(1);
+ uint8 b2[bs];
+ for(int y=0; y<sy; y++) {
+ uint8 *b1 = image + ximage->bytes_per_line * y;
+ bit_packing->unpack(sx,b1,b2);
+ out.send(bs,b2);
+ }
+}
+
+/* loathe Xlib's error handlers */
+static FormatX11 *current_x11;
+static int FormatX11_error_handler (Display *d, XErrorEvent *xee) {
+ post("XErrorEvent: type=0x%08x display=0x%08x xid=0x%08x",
+ xee->type, xee->display, xee->resourceid);
+ post("... serial=0x%08x error=0x%08x request=0x%08lx minor=0x%08x",
+ xee->serial, xee->error_code, xee->request_code, xee->minor_code);
+ if (current_x11->shared_memory==1) {
+ post("(note: turning shm off)");
+ current_x11->shared_memory = 0;
+ }
+ return 42; /* it seems that the return value is ignored. */
+}
+
+bool FormatX11::alloc_image (int sx, int sy) {
+ dim = new Dim(sy,sx,3);
+ dealloc_image();
+ if (sx==0 || sy==0) return false;
+ current_x11 = this;
+ if (!shared_memory) {
+ ximage = XCreateImage(display,visual,depth,ZPixmap,0,0,sx,sy,8,0);
+ int size = ximage->bytes_per_line*ximage->height;
+ if (!ximage) RAISE("can't create image");
+ image = new uint8[size];
+ ximage->data = (int8 *)image;
+ } else {
+#ifdef HAVE_X11_SHARED_MEMORY
+ shm_info = new XShmSegmentInfo;
+ ximage = XShmCreateImage(display,visual,depth,ZPixmap,0,shm_info,sx,sy);
+ if (!ximage) {post("x11: will retry without shared memory"); shared_memory=false;}
+ XSync(display,0);
+ if (!shared_memory) return alloc_image(sx,sy);
+ int size = ximage->bytes_per_line*ximage->height;
+ shm_info->shmid = shmget(IPC_PRIVATE,size,IPC_CREAT|0777);
+ if(shm_info->shmid < 0) RAISE("shmget() failed: %s",strerror(errno));
+ ximage->data = shm_info->shmaddr = (char *)shmat(shm_info->shmid,0,0);
+ if ((long)(shm_info->shmaddr) == -1) RAISE("shmat() failed: %s",strerror(errno));
+ image = (uint8 *)ximage->data;
+ shm_info->readOnly = False;
+ if (!XShmAttach(display, shm_info)) RAISE("ERROR: XShmAttach: big problem");
+ XSync(display,0); // make sure the server picks it up
+ // yes, this can be done now. should cause auto-cleanup.
+ shmctl(shm_info->shmid,IPC_RMID,0);
+ if (!shared_memory) return alloc_image(sx,sy);
+#endif
+ }
+#ifdef HAVE_X11_XVIDEO
+ if (xvideo) {
+ unsigned int ver, rel, req, ev, err, i, j, adaptors, formats;
+ XvAdaptorInfo *ai;
+ if (Success != XvQueryExtension(display,&ver,&rel,&req,&ev,&err)) RAISE("XvQueryExtension problem");
+ /* find + lock port */
+ if (Success != XvQueryAdaptors(display,DefaultRootWindow(display),&adaptors,&ai)) RAISE("XvQueryAdaptors problem");
+ 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(display,ai[i].base_id+j,CurrentTime)) RAISE("XvGrabPort problem");
+ xv_port = ai[i].base_id + j;
+ goto breakout;
+ }
+ }
+ }
+ breakout:
+ XFree(ai);
+ if (!xv_port) RAISE("no xv_port");
+/*
+ unsigned int encn;
+ XvEncodingInfo *enc;
+ XvQueryEncodings(display,xv_port,&encn,&enc);
+ for (i=0; i<encn; i++) post("XvEncodingInfo: name='%s' encoding_id=0x%08x",enc[i].name,enc[i].encoding_id);
+ post("pdp_xvideo: grabbed port %d on adaptor %d",xv_port,i);
+ size_t size = sx*sy*4;
+ data = new uint8[size];
+ for (i=0; i<size; i++) data[i]=0;
+ xvi = XvCreateImage(display,xv_port,0x51525762,(char *)data,sx,sy);
+ last_encoding=-1;
+ if (!xvi) RAISE("XvCreateImage problem");
+*/
+ }
+#endif
+ int status = XInitImage(ximage);
+ if (status!=1) post("XInitImage returned: %d", status);
+ return true;
+retry:
+ post("couldn't allocate image buffer for output... retrying...");
+ return alloc_image(sx,sy);
+}
+
+void FormatX11::dealloc_image () {
+ if (!ximage) return;
+ if (!shared_memory) {
+ XFree(ximage); ximage=0; image=0;
+ } else {
+#ifdef HAVE_X11_SHARED_MEMORY
+ shmdt(ximage->data);
+ XShmDetach(display,shm_info);
+ if (shm_info) {delete shm_info; shm_info=0;}
+ XFree(ximage);
+ ximage = 0;
+ image = 0;
+#endif
+ }
+ if (xvideo) {
+#ifdef HAVE_X11_XVIDEO
+ //if (data) delete[] data;
+ if (xvi) XFree(xvi);
+ xvi=0;
+ //data=0;
+#endif
+ }
+}
+
+void FormatX11::resize_window (int sx, int sy) {
+ if (sy<16) sy=16; if (sy>4096) RAISE("height too big");
+ if (sx<16) sx=16; if (sx>4096) RAISE("width too big");
+ alloc_image(sx,sy);
+ if (window) {
+ if (is_owner && !lock_size) {
+ set_wm_hints();
+ XResizeWindow(display,window,sx,sy);
+ }
+ } else {
+ XSetWindowAttributes xswa;
+ xswa.do_not_propagate_mask = 0; //?
+ xswa.override_redirect = override_redirect; //#!@#$
+ window = XCreateWindow(display,
+ parent, pos[1], pos[0], sx, sy, 0,
+ CopyFromParent, InputOutput, CopyFromParent,
+ CWOverrideRedirect|CWDontPropagate, &xswa);
+ if(!window) RAISE("can't create window");
+ set_wm_hints();
+
+ XSelectInput(display, window,
+ ExposureMask|StructureNotifyMask|PointerMotionMask|
+ ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
+ KeyPressMask|KeyReleaseMask);
+
+ if (is_owner) XMapRaised(display, window);
+ imagegc = XCreateGC(display, window, 0, NULL);
+ if (visual->c_class == PseudoColor) prepare_colormap();
+ }
+ XSync(display,0);
+}
+
+GRID_INLET(0) {
+ if (in->dim->n != 3)
+ RAISE("expecting 3 dimensions: rows,columns,channels");
+ if (in->dim->get(2)!=3 && in->dim->get(2)!=4)
+ RAISE("expecting 3 or 4 channels: red,green,blue,ignored (got %d)",in->dim->get(2));
+ int sx = in->dim->get(1), osx = dim->get(1);
+ int sy = in->dim->get(0), osy = dim->get(0);
+ in->set_chunk(1);
+ if (sx!=osx || sy!=osy) resize_window(sx,sy);
+ if (in->dim->get(2)!=bit_packing->size) {
+ bit_packing->mask[3]=0;
+ bit_packing = new BitPacking(bit_packing->endian,
+ bit_packing->bytes, in->dim->get(2), bit_packing->mask);
+ }
+} GRID_FLOW {
+ int bypl = ximage->bytes_per_line;
+ int sxc = in->dim->prod(1);
+ int sx = in->dim->get(1);
+ int y = dex/sxc;
+ for (; n>0; y++, data+=sxc, n-=sxc) {
+ // convert line
+ if (use_stripes) {
+ int o=y*bypl;
+ for (int x=0, i=0, k=y%3; x<sx; x++, i+=3, k=(k+1)%3) {
+ image[o+x] = (k<<6) | data[i+k]>>2;
+ }
+ } else {
+ bit_packing->pack(sx, data, image+y*bypl);
+ }
+ }
+} GRID_FINISH {
+ show_section(0,0,in->dim->get(1),in->dim->get(0));
+} GRID_END
+
+\def 0 out_size (int sy, int sx) { resize_window(sx,sy); }
+
+\def 0 setcursor (int shape) {
+ shape = 2*(shape&63);
+ Cursor c = XCreateFontCursor(display,shape);
+ XDefineCursor(display,window,c);
+ XFlush(display);
+}
+
+\def 0 hidecursor () {
+ Font font = XLoadFont(display,"fixed");
+ XColor color; /* bogus */
+ Cursor c = XCreateGlyphCursor(display,font,font,' ',' ',&color,&color);
+ XDefineCursor(display,window,c);
+ XFlush(display);
+}
+
+void FormatX11::prepare_colormap() {
+ Colormap colormap = XCreateColormap(display,window,visual,AllocAll);
+ XColor colors[256];
+ if (use_stripes) {
+ for (int i=0; i<192; i++) {
+ int k=(i&63)*0xffff/63;
+ colors[i].pixel = i;
+ colors[i].red = (i>>6)==0 ? k : 0;
+ colors[i].green = (i>>6)==1 ? k : 0;
+ colors[i].blue = (i>>6)==2 ? k : 0;
+ colors[i].flags = DoRed | DoGreen | DoBlue;
+ }
+ XStoreColors(display,colormap,colors,192);
+ } else {
+ for (int i=0; i<256; i++) {
+ colors[i].pixel = i;
+ colors[i].red = ((i>>0)&7)*0xffff/7;
+ colors[i].green = ((i>>3)&7)*0xffff/7;
+ colors[i].blue = ((i>>6)&3)*0xffff/3;
+ colors[i].flags = DoRed | DoGreen | DoBlue;
+ }
+ XStoreColors(display,colormap,colors,256);
+ }
+ XSetWindowColormap(display,window,colormap);
+}
+
+void FormatX11::open_display(const char *disp_string) {
+ display = XOpenDisplay(disp_string);
+ if(!display) RAISE("ERROR: opening X11 display: %s",strerror(errno));
+ // btw don't expect too much from Xlib error handling.
+ // Xlib, you are so free of the ravages of intelligence...
+ XSetErrorHandler(FormatX11_error_handler);
+ Screen *screen = DefaultScreenOfDisplay(display);
+ int screen_num = DefaultScreen(display);
+ visual = DefaultVisual(display, screen_num);
+ root_window = DefaultRootWindow(display);
+ depth = DefaultDepthOfScreen(screen);
+ colormap = 0;
+
+ switch(visual->c_class) {
+ // without colormap
+ case TrueColor: case DirectColor: break;
+ // with colormap
+ case PseudoColor: if (depth!=8) RAISE("ERROR: with colormap, only supported depth is 8 (got %d)", depth); break;
+ default: RAISE("ERROR: visual type not supported (got %d)", visual->c_class);
+ }
+
+#if defined(HAVE_X11_XVIDEO)
+ xvideo = true;
+#elif defined(HAVE_X11_SHARED_MEMORY)
+ shared_memory = !! XShmQueryExtension(display);
+#else
+ shared_memory = false;
+#endif
+}
+
+Window FormatX11::search_window_tree (Window xid, Atom key, const char *value, int level) {
+ if (level>2) return 0xDeadBeef;
+ Window root_r, parent_r;
+ Window *children_r;
+ unsigned int nchildren_r;
+ XQueryTree(display,xid,&root_r,&parent_r,&children_r,&nchildren_r);
+ Window target = 0xDeadBeef;
+ for (int i=0; i<(int)nchildren_r; i++) {
+ Atom actual_type_r;
+ int actual_format_r;
+ unsigned long nitems_r, bytes_after_r;
+ unsigned char *prop_r;
+ XGetWindowProperty(display,children_r[i],key,0,666,0,AnyPropertyType,
+ &actual_type_r,&actual_format_r,&nitems_r,&bytes_after_r,&prop_r);
+ uint32 value_l = strlen(value);
+ bool match = prop_r && nitems_r>=value_l &&
+ strncmp((char *)prop_r+nitems_r-value_l,value,value_l)==0;
+ XFree(prop_r);
+ if (match) {target=children_r[i]; break;}
+ target = search_window_tree(children_r[i],key,value,level+1);
+ if (target != 0xDeadBeef) break;
+ }
+ if (children_r) XFree(children_r);
+ return target;
+}
+
+\def 0 move (int y, int x) {
+ pos[0]=y; pos[1]=x;
+ XMoveWindow(display,window,x,y);
+ XFlush(display);
+}
+
+\def 0 set_geometry (int y, int x, int sy, int sx) {
+ pos[0]=y; pos[1]=x;
+ XMoveWindow(display,window,x,y);
+ resize_window(sx,sy);
+ XFlush(display);
+}
+
+\def 0 shared_memory (bool toggle) {shared_memory = toggle;}
+\def 0 xvideo (bool toggle) {xvideo = toggle;}
+
+\def 0 warp (int y, int x) {
+ XWarpPointer(display,None,None,0,0,0,0,x,y);
+ XFlush(display);
+}
+
+\def 0 title (string title="") {this->title = title; set_wm_hints();}
+
+\end class FormatX11 {install_format("#io.x11",6,"");}
+void startup_x11 () {
+ \startall
+}
+