diff options
Diffstat (limited to 'externals/gridflow/src')
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[ ¬ _ 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 +} + |