diff options
Diffstat (limited to 'externals/gridflow/format')
-rw-r--r-- | externals/gridflow/format/aalib.c | 144 | ||||
-rw-r--r-- | externals/gridflow/format/dc1394.c | 287 | ||||
-rw-r--r-- | externals/gridflow/format/ieee1394-ioctl.h | 111 | ||||
-rw-r--r-- | externals/gridflow/format/jpeg.c | 122 | ||||
-rw-r--r-- | externals/gridflow/format/main.c | 249 | ||||
-rw-r--r-- | externals/gridflow/format/mpeg3.c | 83 | ||||
-rw-r--r-- | externals/gridflow/format/netpbm.c | 119 | ||||
-rw-r--r-- | externals/gridflow/format/opengl.c | 174 | ||||
-rw-r--r-- | externals/gridflow/format/png.c | 116 | ||||
-rw-r--r-- | externals/gridflow/format/pwc-ioctl.h | 292 | ||||
-rw-r--r-- | externals/gridflow/format/quartz.m | 224 | ||||
-rw-r--r-- | externals/gridflow/format/quicktimeapple.c | 456 | ||||
-rw-r--r-- | externals/gridflow/format/quicktimehw.c | 243 | ||||
-rw-r--r-- | externals/gridflow/format/sdl.c | 209 | ||||
-rw-r--r-- | externals/gridflow/format/videodev.c | 792 | ||||
-rw-r--r-- | externals/gridflow/format/x11.c | 656 |
16 files changed, 4277 insertions, 0 deletions
diff --git a/externals/gridflow/format/aalib.c b/externals/gridflow/format/aalib.c new file mode 100644 index 00000000..f33d8d83 --- /dev/null +++ b/externals/gridflow/format/aalib.c @@ -0,0 +1,144 @@ +/* + $Id: aalib.c 3624 2008-04-19 02:07:36Z 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. +*/ + +#include "../gridflow.h.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(FormatAALib,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->factor(); + if (raw_mode) { + int sx = min(f,aa_scrwidth(context)); + int y = in->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 = in->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/format/dc1394.c b/externals/gridflow/format/dc1394.c new file mode 100644 index 00000000..7f107b49 --- /dev/null +++ b/externals/gridflow/format/dc1394.c @@ -0,0 +1,287 @@ +/* + $Id: dc1394.c 3978 2008-07-04 20:18:01Z 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. +*/ + +#include <libraw1394/raw1394.h> +#include <libdc1394/dc1394_control.h> +#include "../gridflow.h.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/format/ieee1394-ioctl.h b/externals/gridflow/format/ieee1394-ioctl.h new file mode 100644 index 00000000..f92b5663 --- /dev/null +++ b/externals/gridflow/format/ieee1394-ioctl.h @@ -0,0 +1,111 @@ +/* Base file for all ieee1394 ioctl's. Linux-1394 has allocated base '#' + * with a range of 0x00-0x3f. */ + +#ifndef __IEEE1394_IOCTL_H +#define __IEEE1394_IOCTL_H + +#include <linux/ioctl.h> +#include <linux/types.h> + + +/* AMDTP Gets 6 */ +#define AMDTP_IOC_CHANNEL _IOW('#', 0x00, struct amdtp_ioctl) +#define AMDTP_IOC_PLUG _IOW('#', 0x01, struct amdtp_ioctl) +#define AMDTP_IOC_PING _IOW('#', 0x02, struct amdtp_ioctl) +#define AMDTP_IOC_ZAP _IO ('#', 0x03) + + +/* DV1394 Gets 10 */ + +/* Get the driver ready to transmit video. pass a struct dv1394_init* as + * the parameter (see below), or NULL to get default parameters */ +#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init) + +/* Stop transmitting video and free the ringbuffer */ +#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07) + +/* Submit N new frames to be transmitted, where the index of the first new + * frame is first_clear_buffer, and the index of the last new frame is + * (first_clear_buffer + N) % n_frames */ +#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08) + +/* Block until N buffers are clear (pass N as the parameter) Because we + * re-transmit the last frame on underrun, there will at most be n_frames + * - 1 clear frames at any time */ +#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09) + +/* Capture new frames that have been received, where the index of the + * first new frame is first_clear_buffer, and the index of the last new + * frame is (first_clear_buffer + N) % n_frames */ +#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a) + +/* Tell card to start receiving DMA */ +#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b) + +/* Pass a struct dv1394_status* as the parameter */ +#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status) + + +/* Video1394 Gets 10 */ + +#define VIDEO1394_IOC_LISTEN_CHANNEL \ + _IOWR('#', 0x10, struct video1394_mmap) +#define VIDEO1394_IOC_UNLISTEN_CHANNEL \ + _IOW ('#', 0x11, int) +#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \ + _IOW ('#', 0x12, struct video1394_wait) +#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \ + _IOWR('#', 0x13, struct video1394_wait) +#define VIDEO1394_IOC_TALK_CHANNEL \ + _IOWR('#', 0x14, struct video1394_mmap) +#define VIDEO1394_IOC_UNTALK_CHANNEL \ + _IOW ('#', 0x15, int) +/* + * This one is broken: it really wanted + * "sizeof (struct video1394_wait) + sizeof (struct video1394_queue_variable)" + * but got just a "size_t" + */ +#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \ + _IOW ('#', 0x16, size_t) +#define VIDEO1394_IOC_TALK_WAIT_BUFFER \ + _IOW ('#', 0x17, struct video1394_wait) +#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \ + _IOWR('#', 0x18, struct video1394_wait) + + +/* Raw1394's ISO interface */ +#define RAW1394_IOC_ISO_XMIT_INIT \ + _IOW ('#', 0x1a, struct raw1394_iso_status) +#define RAW1394_IOC_ISO_RECV_INIT \ + _IOWR('#', 0x1b, struct raw1394_iso_status) +#define RAW1394_IOC_ISO_RECV_START \ + _IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3) +#define RAW1394_IOC_ISO_XMIT_START \ + _IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2) +#define RAW1394_IOC_ISO_XMIT_RECV_STOP \ + _IO ('#', 0x1e) +#define RAW1394_IOC_ISO_GET_STATUS \ + _IOR ('#', 0x1f, struct raw1394_iso_status) +#define RAW1394_IOC_ISO_SHUTDOWN \ + _IO ('#', 0x20) +#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \ + _IO ('#', 0x21) +#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \ + _IOW ('#', 0x22, unsigned char) +#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \ + _IOW ('#', 0x23, unsigned char) +#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \ + _IOW ('#', 0x24, __u64) +#define RAW1394_IOC_ISO_RECV_PACKETS \ + _IOW ('#', 0x25, struct raw1394_iso_packets) +#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \ + _IOW ('#', 0x26, unsigned int) +#define RAW1394_IOC_ISO_XMIT_PACKETS \ + _IOW ('#', 0x27, struct raw1394_iso_packets) +#define RAW1394_IOC_ISO_XMIT_SYNC \ + _IO ('#', 0x28) +#define RAW1394_IOC_ISO_RECV_FLUSH \ + _IO ('#', 0x29) + + +#endif /* __IEEE1394_IOCTL_H */ diff --git a/externals/gridflow/format/jpeg.c b/externals/gridflow/format/jpeg.c new file mode 100644 index 00000000..80e5e717 --- /dev/null +++ b/externals/gridflow/format/jpeg.c @@ -0,0 +1,122 @@ +/* + $Id: jpeg.c 3624 2008-04-19 02:07:36Z 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. +*/ + +//!@#$ not handling abort on compress +//!@#$ not handling abort on decompress + +#include "../gridflow.h.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; + \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 (); + \decl 0 quality (short quality); + \grin 0 int +}; + +GRID_INLET(FormatJPEG,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_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) { + quality = min(max((int)quality,0),100); + // should the last arg ("baseline") be set to true ? + // and what is it for? is it for accuracy of the DC component? + jpeg_set_quality(&cjpeg,quality,false); +} + +\classinfo {install_format("#io.jpeg",6,"jpeg jpg");} +\end class FormatJPEG +void startup_jpeg () { + \startall +} diff --git a/externals/gridflow/format/main.c b/externals/gridflow/format/main.c new file mode 100644 index 00000000..caf90672 --- /dev/null +++ b/externals/gridflow/format/main.c @@ -0,0 +1,249 @@ +/* + $Id$ + + 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. +*/ + +#include "../gridflow.h.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]; // = "\7fgrid" on little endian, "\x7fGRID" on big endian + uint8 type; // number of bits. + // the original doc said: "plus one of: 1:unsigned 2:float" but i don't recall what this means. + 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) { + strncpy(head.magic,is_le()?"\7fgrid":"\7fGRID",5); + head.type = 32; + _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 { + fread(&head,1,8,f); + uint8 *m = (uint8 *)head.magic; + if (strncmp((char *)m,"\7fgrid",5)==0) endian=1; else + if (strncmp((char *)m,"\7fGRID",5)==0) endian=1; 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]); + if (head.type!=32) 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]; + fread(dimv,head.dimn,4,f); + 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]; fread(data,nn,sizeof(T),f); out.send(nn,(T *)data);} +TYPESWITCH(nt,FOO,) +#undef FOO + SUPER; +} + +GRID_INLET(FormatGrid,0) { + if (!headerless_dim) { + fwrite(&head,1,8,f); + fwrite(in->dim->v,in->dim->n,4,f); // forgot the endian here + } +} 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(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/format/mpeg3.c b/externals/gridflow/format/mpeg3.c new file mode 100644 index 00000000..c034f4dc --- /dev/null +++ b/externals/gridflow/format/mpeg3.c @@ -0,0 +1,83 @@ +/* + $Id: mpeg3.c 3815 2008-06-06 03:50:40Z 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. +*/ + +#define LIBMPEG_INCLUDE_HERE +#include "../gridflow.h.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 (long frame); + \decl 0 rewind (); + \decl 0 bang (); +}; + +\def 0 seek (long frame) { + mpeg3_set_frame(mpeg,clip(frame,0L,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/format/netpbm.c b/externals/gridflow/format/netpbm.c new file mode 100644 index 00000000..159903bd --- /dev/null +++ b/externals/gridflow/format/netpbm.c @@ -0,0 +1,119 @@ +/* + $Id$ + + 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. +*/ + +#include "../gridflow.h.fcs" +extern "C" { +#include <pam.h> +}; + +\class FormatNetPBM : Format { + struct pam inpam, outpam; + \grin 0 + \constructor (t_symbol *mode, string filename) { + Format::_0_open(0,0,mode,filename); + memset(& inpam,sizeof(pam),0); + memset(&outpam,sizeof(pam),0); + } + \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(FormatNetPBM,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/format/opengl.c b/externals/gridflow/format/opengl.c new file mode 100644 index 00000000..e4606d4e --- /dev/null +++ b/externals/gridflow/format/opengl.c @@ -0,0 +1,174 @@ +/* + $Id: opengl.c 3650 2008-04-25 15:55:52Z 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. +*/ + +#include "../gridflow.h.fcs" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/time.h> +#include <signal.h> +#ifdef __APPLE__ +#include <GLUT/glut.h> +#include <OpenGL/gl.h> +#include <OpenGL/glu.h> +#else +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glut.h> +#endif +#include <setjmp.h> + +static bool in_use = false; + +\class FormatOpenGL : Format { + int window; + GLuint gltex; + P<BitPacking> bit_packing; + P<Dim> dim; + uint8 *buf; + t_clock *clock; + void call (); + \decl 0 resize_window (int sx, int sy); + \grin 0 + \constructor (t_symbol *mode) { + if (in_use) RAISE("only one #io:opengl object at a time; sorry"); + in_use=true; + if (mode!=gensym("out")) RAISE("write-only, sorry"); + int dummy = 0; + glutInit(&dummy,0); + glutInitDisplayMode(GLUT_RGBA); + resize_window(0,0,320,240); + gltex = 0; + glEnable(GL_TEXTURE_2D); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_ALPHA_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_DITHER); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + window = 0xDeadBeef; + uint32 mask[3] = {0xff000000,0x00ff0000,0x0000ff00}; + bit_packing = new BitPacking(4,4,3,mask); + clock = clock_new(this,(t_method)FormatOpenGL_call); + clock_delay(clock,0); + } + + ~FormatOpenGL () { + clock_unset(clock); + if (gltex) glDeleteTextures(1, (GLuint*)&gltex); + if (buf) delete buf; + in_use=false; + if ((unsigned)window!=0xDeadBeef) { + glutDestroyWindow(window); + window=0xDeadBeef; + } + } +}; + +static jmp_buf hack; +static void my_idle () { longjmp(hack,1); } + +\def void call () { + int32 sy = dim->get(0); + int32 sx = dim->get(1); + glEnable(GL_TEXTURE_2D); + if (!gltex) { + } + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,sx,sy,GL_RGBA,GL_UNSIGNED_BYTE,buf); + glBindTexture(GL_TEXTURE_2D, gltex); + glBegin(GL_QUADS); + glColor3f(1.f,1.f,1.f); + glTexCoord2f(0.f,0.f); glVertex2f(0.f,0.f); + glTexCoord2f(1.f,0.f); glVertex2f( sx,0.f); + glTexCoord2f(1.f,1.f); glVertex2f( sx, sy); + glTexCoord2f(0.f,1.f); glVertex2f(0.f, sy); + glEnd(); + + //Here comes some (un)fair amount of arm-twisting + //This is for processing queued events and then "returning". + glutIdleFunc(my_idle); + if(!setjmp(hack)) glutMainLoop(); + //done + + clock_delay(clock,100); +} +void FormatOpenGL_call (FormatOpenGL *self) {self->call();} + +\def 0 resize_window (int sx, int sy) { + dim = new Dim(sy,sx,3); + char foo[666]; + sprintf(foo,"GridFlow/GL (%d,%d,3)",sy,sx); + if ((unsigned)window==0xDeadBeef) { + glutInitWindowSize(sx,sy); + window = glutCreateWindow(foo); + } else { + glutReshapeWindow(sx,sy); + } + if (buf) delete buf; + buf = new uint8[sy*sx*4]; + glViewport(0,0,sx,sy); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0,sx,sy,0,-99999,99999); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (gltex) glDeleteTextures(1, (GLuint*)&gltex); + glGenTextures(1, (GLuint*)&gltex); + glBindTexture(GL_TEXTURE_2D,gltex); + glPixelStorei(GL_UNPACK_ALIGNMENT,1); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,sx,sy,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); + +} + +GRID_INLET(FormatOpenGL,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(0,0,sx,sy); +} GRID_FLOW { + int sxc = in->dim->prod(1); + int sx = in->dim->get(1); + int bypl = 4*sx; + int y = in->dex / sxc; + for (; n>0; y++, data+=sxc, n-=sxc) bit_packing->pack(sx, data, buf+y*bypl); + } GRID_FINISH { +} GRID_END + +\classinfo {install_format("#io.opengl",2,"");} +\end class FormatOpenGL +void startup_opengl () { + \startall +} diff --git a/externals/gridflow/format/png.c b/externals/gridflow/format/png.c new file mode 100644 index 00000000..a55ca648 --- /dev/null +++ b/externals/gridflow/format/png.c @@ -0,0 +1,116 @@ +/* + $Id: png.c 3624 2008-04-19 02:07:36Z 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. +*/ + +/* !@#$ not handling abort on compress */ +/* !@#$ not handling abort on decompress */ + +#include "../gridflow.h.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(FormatPNG,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); + RAISE("bother, said pooh, as the PNG encoding was found unimplemented"); +} GRID_FLOW { + int rowsize = in->dim->get(1)*in->dim->get(2); + int rowsize2 = in->dim->get(1)*3; + uint8 row[rowsize2]; + while (n) { + bit_packing->pack(in->dim->get(1),data,row); + n-=rowsize; data+=rowsize; + } +} 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/format/pwc-ioctl.h b/externals/gridflow/format/pwc-ioctl.h new file mode 100644 index 00000000..65805eaa --- /dev/null +++ b/externals/gridflow/format/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/format/quartz.m b/externals/gridflow/format/quartz.m new file mode 100644 index 00000000..8ff389a8 --- /dev/null +++ b/externals/gridflow/format/quartz.m @@ -0,0 +1,224 @@ +/* + $Id: quartz.m 3709 2008-05-21 18:53:54Z 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.h.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(FormatQuartz,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_factor(in->dim->prod(1)); +} GRID_FLOW { + int off = in->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/format/quicktimeapple.c b/externals/gridflow/format/quicktimeapple.c new file mode 100644 index 00000000..2882168e --- /dev/null +++ b/externals/gridflow/format/quicktimeapple.c @@ -0,0 +1,456 @@ +/* + $Id: quicktimeapple.c 3904 2008-06-18 20:32:46Z 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. +*/ + +#include <QuickTime/QuickTime.h> +#include <QuickTime/Movies.h> +#include <QuickTime/QuickTimeComponents.h> +#include "../gridflow.h.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(FormatQuickTimeCamera,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(FormatQuickTimeApple,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/format/quicktimehw.c b/externals/gridflow/format/quicktimehw.c new file mode 100644 index 00000000..363c4641 --- /dev/null +++ b/externals/gridflow/format/quicktimehw.c @@ -0,0 +1,243 @@ +/* + $Id: quicktimehw.c 3878 2008-06-15 04:37:33Z 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. +*/ + +#define QUICKTIMEHW_INCLUDE_HERE +#include "../gridflow.h.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; + int length; // in frames + 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=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")) { + length = quicktime_video_length(anim,track); + post("quicktime: codec=%s height=%d width=%d depth=%d framerate=%f", + quicktime_video_compressor(anim,track), + quicktime_video_height(anim,track), + quicktime_video_width(anim,track), + quicktime_video_depth(anim,track), + quicktime_frame_rate(anim,track)); + /* 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 (long 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 (long frame) { + quicktime_set_video_position(anim,clip(frame,0L,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 = new uint8[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.give(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(FormatQuickTimeHW,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,length); + outlet_anything(bself->outlets[0],gensym("frames"),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/format/sdl.c b/externals/gridflow/format/sdl.c new file mode 100644 index 00000000..e87d65c9 --- /dev/null +++ b/externals/gridflow/format/sdl.c @@ -0,0 +1,209 @@ +/* + $Id: sdl.c 3809 2008-06-05 17:17:54Z 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. +*/ + +#include "../gridflow.h.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(FormatSDL,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 = in->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/format/videodev.c b/externals/gridflow/format/videodev.c new file mode 100644 index 00000000..eb508fbd --- /dev/null +++ b/externals/gridflow/format/videodev.c @@ -0,0 +1,792 @@ +/* + $Id: videodev.c 3871 2008-06-12 14:41:53Z 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. +*/ + +/* 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.h.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 long 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(FormatVideoDev,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(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 long frequency () { + long value; + //if (ioctl(fd, VIDIOCGFREQ, &value)<0) {has_frequency=false; return 0;} + WIOCTL(fd, VIDIOCGFREQ, &value); + return value; +} +\def 0 frequency (long 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/format/x11.c b/externals/gridflow/format/x11.c new file mode 100644 index 00000000..91beb0bc --- /dev/null +++ b/externals/gridflow/format/x11.c @@ -0,0 +1,656 @@ +/* + $Id: x11.c 3982 2008-07-04 20:49:12Z 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. + + Note: some of the code was adapted from PDP's (the XVideo stuff). +*/ +#include "../gridflow.h.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,":%ld",long(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:%ld",string(argv[1]).data(),long(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 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 }; + 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); + } + + \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) { + 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); +} + +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(FormatX11,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 = in->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 +} + |