aboutsummaryrefslogtreecommitdiff
path: root/externals/gridflow/format
diff options
context:
space:
mode:
Diffstat (limited to 'externals/gridflow/format')
-rw-r--r--externals/gridflow/format/aalib.c168
-rw-r--r--externals/gridflow/format/dc1394.c346
-rw-r--r--externals/gridflow/format/jpeg.c130
-rw-r--r--externals/gridflow/format/main.rb788
-rw-r--r--externals/gridflow/format/mpeg3.c98
-rw-r--r--externals/gridflow/format/png.c138
-rw-r--r--externals/gridflow/format/quicktimeapple.c500
-rw-r--r--externals/gridflow/format/quicktimehw.c244
-rw-r--r--externals/gridflow/format/sdl.c123
-rw-r--r--externals/gridflow/format/videodev.c544
-rw-r--r--externals/gridflow/format/x11.c655
11 files changed, 3734 insertions, 0 deletions
diff --git a/externals/gridflow/format/aalib.c b/externals/gridflow/format/aalib.c
new file mode 100644
index 00000000..f014a632
--- /dev/null
+++ b/externals/gridflow/format/aalib.c
@@ -0,0 +1,168 @@
+/*
+ $Id: aalib.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 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 "../base/grid.h.fcs"
+#define aa_hardwareparams aa_hardware_params
+#include <aalib.h>
+
+/* MINNOR is a typo in aalib.h, sorry */
+typedef
+#if AA_LIB_MINNOR == 2
+ int
+#else
+ enum aa_attribute
+#endif
+AAAttr;
+
+\class FormatAALib < Format
+struct FormatAALib : Format {
+ aa_context *context;
+ aa_renderparams *rparams;
+ int autodraw; /* as for X11 */
+ bool raw_mode;
+
+ FormatAALib () : context(0), autodraw(1) {}
+
+ \decl void initialize (Symbol mode, Symbol target);
+ \decl void close ();
+ \decl void _0_hidecursor ();
+ \decl void _0_print (int y, int x, int a, Symbol text);
+ \decl void _0_draw ();
+ \decl void _0_autodraw (int autodraw);
+ \decl void _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_factor(in->dim->get(1)*in->dim->get(2));
+} 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 void close () {
+ if (context) {
+ aa_close(context);
+ context=0;
+ }
+}
+
+\def void _0_hidecursor () { aa_hidemouse(context); }
+\def void _0_draw () { aa_flush(context); }
+\def void _0_print (int y, int x, int a, Symbol text) {
+ aa_puts(context,x,y,(AAAttr)a,(char *)rb_sym_name(text));
+ if (autodraw==1) aa_flush(context);
+}
+\def void _0_autodraw (int autodraw) {
+ if (autodraw<0 || autodraw>1)
+ RAISE("autodraw=%d is out of range",autodraw);
+ this->autodraw = autodraw;
+}
+\def void _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++) {
+ STACK_ARRAY(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);
+ }
+ }
+}
+
+/* !@#$ varargs missing here */
+\def void initialize (Symbol mode, Symbol target) {
+ rb_call_super(argc,argv);
+ argc-=2; argv+=2;
+ char *argv2[argc];
+ for (int i=0; i<argc; i++)
+ argv2[i] = strdup(rb_str_ptr(rb_funcall(argv[i],SI(to_s),0)));
+ if (mode!=SYM(out)) RAISE("write-only, sorry");
+ aa_parseoptions(0,0,&argc,argv2);
+ for (int i=0; i<argc; i++) free(argv2[i]);
+ Ruby drivers = rb_ivar_get(rb_obj_class(rself),SI(@drivers));
+ Ruby driver_address = rb_hash_aref(drivers,target);
+ if (driver_address==Qnil)
+ RAISE("unknown aalib driver '%s'",rb_sym_name(target));
+ aa_driver *driver = FIX2PTR(aa_driver,driver_address);
+ 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};
+ gfpost("aalib image size: %s",(new Dim(3,v))->to_s());
+}
+
+\classinfo {
+ Ruby drivers = rb_ivar_set(rself,SI(@drivers),rb_hash_new());
+ const aa_driver *const *p = aa_drivers;
+ for (; *p; p++) {
+ rb_hash_aset(drivers,ID2SYM(rb_intern((*p)->shortname)), PTR2FIX(*p));
+ }
+// IEVAL(rself,"GridFlow.post('aalib supports: %s', @drivers.keys.join(', '))");
+ IEVAL(rself,"install '#in:aalib',1,1;@flags=2;@comment='Ascii Art Library'");
+}
+\end class FormatAALib
+void startup_aalib () {
+ \startall
+}
diff --git a/externals/gridflow/format/dc1394.c b/externals/gridflow/format/dc1394.c
new file mode 100644
index 00000000..1632bef7
--- /dev/null
+++ b/externals/gridflow/format/dc1394.c
@@ -0,0 +1,346 @@
+/*
+ $Id: dc1394.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 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 "../base/grid.h.fcs"
+
+typedef raw1394handle_t RH;
+typedef nodeid_t NID;
+
+static const int ruby_lineno = __LINE__;
+static const char *ruby_code =
+\ruby
+
+def choice(*)end
+class CStruct
+ def initialize(*) end
+end
+
+choice :name,:Speed,:start,0,:values,%w(SPEED_100 SPEED_200 SPEED_400)
+choice :name,:Framerate,:start,32,:values,
+%w(FRAMERATE_1_875 FRAMERATE_3_75 FRAMERATE_7_5 FRAMERATE_15
+ FRAMERATE_30 FRAMERATE_60)
+
+choice :name,:Format0Mode,:start,64,:values,%w(
+ MODE_160x120_YUV444 MODE_320x240_YUV422
+ MODE_640x480_YUV411 MODE_640x480_YUV422
+ MODE_640x480_RGB MODE_640x480_MONO
+ MODE_640x480_MONO16)
+
+choice :name,:Format1Mode,:start,96,:values,%w(
+ MODE_800x600_YUV422 MODE_800x600_RGB
+ MODE_800x600_MONO MODE_1024x768_YUV422
+ MODE_1024x768_RGB MODE_1024x768_MONO
+ MODE_800x600_MONO16 MODE_1024x768_MONO16)
+
+choice :name,:Format2Mode,:start,128,:values,%w(
+ MODE_1280x960_YUV422 MODE_1280x960_RGB
+ MODE_1280x960_MONO MODE_1600x1200_YUV422
+ MODE_1600x1200_RGB MODE_1600x1200_MONO
+ MODE_1280x960_MONO16 MODE_1600x1200_MONO16)
+
+choice :name,:Format6Mode,:start,256,:values,%w(MODE_EXIF)
+
+choice :name,:Format7Mode,:start,288,:values,%w(
+ MODE_FORMAT7_0 MODE_FORMAT7_1 MODE_FORMAT7_2 MODE_FORMAT7_3
+ MODE_FORMAT7_4 MODE_FORMAT7_5 MODE_FORMAT7_6 MODE_FORMAT7_7)
+
+choice :name,:Format7ColorMode,:start,320,:values,%w(
+ COLOR_FORMAT7_MONO8 COLOR_FORMAT7_YUV411
+ COLOR_FORMAT7_YUV422 COLOR_FORMAT7_YUV444
+ COLOR_FORMAT7_RGB8 COLOR_FORMAT7_MONO16
+ COLOR_FORMAT7_RGB16)
+
+choice :name,:TriggerMode,:start,352,:values,%w(
+ TRIGGER_MODE_0 TRIGGER_MODE_1 TRIGGER_MODE_2 TRIGGER_MODE_3)
+
+choice :name,:CameraImageFormat,:start,384,:values,%w(
+ FORMAT_VGA_NONCOMPRESSED FORMAT_SVGA_NONCOMPRESSED_1
+ FORMAT_SVGA_NONCOMPRESSED_2 skip 3
+ FORMAT_STILL_IMAGE FORMAT_SCALABLE_IMAGE_SIZE)
+
+choice :name,:CameraFeatures,:start,416,:values,%w(
+ 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 skip 19
+ FEATURE_ZOOM FEATURE_PAN
+ FEATURE_TILT FEATURE_OPTICAL_FILTER
+ skip 12 FEATURE_CAPTURE_SIZE
+ FEATURE_CAPTURE_QUALITY skip 14)
+
+choice :name,:DCBool,:start,0,:values,%w(False True)
+
+#define MAX_CHARS 32
+#define SUCCESS 1
+#define FAILURE -1
+#define NO_CAMERA 0xffff
+
+# Parameter flags for setup_format7_capture()
+#define QUERY_FROM_CAMERA -1
+#define USE_MAX_AVAIL -2
+#define USE_RECOMMENDED -3
+
+# all dc1394_ prefixes removed
+# raw1394handle_t = RH
+# nodeid_t = NID
+# RH+NID = RN
+
+CameraInfo = CStruct.new %{
+ RH rh;
+ NID id;
+ octlet_t ccr_offset;
+ u_int64_t euid_64;
+ char vendor[MAX_CHARS + 1];
+ char model[MAX_CHARS + 1];
+}
+
+CameraCapture = CStruct.new %{
+ NID node;
+ int channel, frame_rate, frame_width, frame_height;
+ int * capture_buffer;
+ int quadlets_per_frame, quadlets_per_packet;
+ const unsigned char * dma_ring_buffer;
+ int dma_buffer_size, dma_frame_size, num_dma_buffers, dma_last_buffer;
+ const char * dma_device_file;
+ int dma_fd, port;
+ struct timeval filltime;
+ int dma_extra_count;
+ unsigned char * dma_extra_buffer;
+ int drop_frames;
+}
+
+MiscInfo = CStruct.new %{
+ int format, mode, framerate;
+ bool is_iso_on;
+ int iso_channel, iso_speed, mem_channel_number;
+ int save_channel, load_channel;
+}
+
+FeatureInfo = CStruct.new %{
+ uint feature_id;
+ bool available, one_push, readout_capable, on_off_capable;
+ bool auto_capable, manual_capable, polarity_capable, one_push_active;
+ bool is_on, auto_active;
+ char trigger_mode_capable_mask;
+ int trigger_mode;
+ bool trigger_polarity;
+ int min, max, value, BU_value, RV_value, target_value;
+}
+
+FeatureSet = CStruct.new %{
+ FeatureInfo feature[NUM_FEATURES];
+}
+#void print_feature_set(FeatureSet *features);
+#extern const char *feature_desc[NUM_FEATURES];
+#void print_feature(FeatureInfo *feature);
+#RawFire create_handle(int port);
+#destroy_handle(RH rh);
+#void print_camera_info(camerainfo *info);
+
+class RH; %{
+ # those two:
+ # Return -1 in numCameras and NULL from the call if there is a problem
+ # otherwise the number of cameras and the NID array from the call
+ NID* get_camera_nodes(int *numCameras, int showCameras);
+ NID* get_sorted_camera_nodes(int numids, int *ids, int *numCameras, int showCameras);
+ release_camera(CameraCapture *camera);
+ single_capture(CameraCapture *camera);
+# this one returns FAILURE or SUCCESS
+ multi_capture(CameraCapture *cams, int num);
+}end
+
+class RN; %{
+ init_camera();
+ is_camera(bool *value);
+ get_camera_feature_set(FeatureSetFeatureInfo *features);
+ get_camera_feature(FeatureInfo *feature);
+ get_camera_misc_info(miscinfo *info);
+ get_sw_version(quadlet_t *value);
+ get_camera_info(camerainfo *info);
+ query_supported_formats(quadlet_t *value);
+ query_supported_modes(uint format, quadlet_t *value);
+ query_supported_framerates(uint format, uint mode, quadlet_t *value);
+ query_revision(int mode, quadlet_t *value);
+ query_basic_functionality(quadlet_t *value);
+ query_advanced_feature_offset(quadlet_t *value);
+ attr set_video_framerate(uint framerate);
+ attr video_mode(uint mode);
+ attr video_format(uint format);
+ double_attr iso_channel_and_speed(uint channel, uint speed);
+ camera_on();
+ camera_off();
+ start_iso_transmission();
+ stop_iso_transmission();
+ get_iso_status(bool *is_on);
+ set_one_shot();
+ unset_one_shot();
+ set_multi_shot(uint numFrames);
+ unset_multi_shot();
+# attributes :
+# those are get_/set_ methods where the get has an input parameter
+# and the set has an output parameter
+ attr uint brightness
+ attr uint exposure
+ attr uint sharpness
+ double_attr set_white_balance(uint u_b_value, uint v_r_value);
+ attr uint hue
+ attr uint saturation(uint saturation);
+ attr uint gamma(uint gamma);
+ attr shutter(uint shutter);
+ attr uint gain
+ attr uint iris
+ attr uint focus
+ attr uint trigger_mode
+ attr uint zoom
+ attr uint pan
+ attr uint tilt
+ attr uint optical_filter
+ attr uint capture_size
+ attr uint capture_quality
+ int get_temperature(uint *target_temperature, uint *temperature);
+ int set_temperature(uint target_temperature);
+
+ get_memory_load_ch(uint *channel);
+ get_memory_save_ch(uint *channel);
+ is_memory_save_in_operation(bool *value);
+ set_memory_save_ch(uint channel);
+ memory_save();
+ memory_load(uint channel);
+ attr bool trigger_polarity
+ trigger_has_polarity(bool *polarity);
+ attr bool set_trigger_on_off
+
+# this one returns SUCCESS on success, FAILURE otherwise
+ setup_capture(
+ int channel, int format, int mode, int speed, int frame_rate,
+ CameraCapture *camera);
+ dma_setup_capture(
+ int channel, int format, int mode, int speed, int frame_rate,
+ int num_dma_buffers, int drop_frames, const char *dma_device_file,
+ CameraCapture *camera);
+ setup_format7_capture(
+ int channel, int mode, int speed, int bytes_per_packet,
+ uint left, uint top, uint width, uint height, CameraCapture *camera);
+ dma_setup_format7_capture(
+ int channel, int mode, int speed, int bytes_per_packet,
+ uint left, uint top, uint width, uint height,
+ int num_dma_buffers, CameraCapture *camera);
+}end
+
+#RNF = RN+uint feature
+class RNF; %{
+ query_feature_control(uint *availability);
+ query_feature_characteristics(quadlet_t *value);
+ attr uint feature_value
+ is_feature_present(bool *value);
+ has_one_push_auto(bool *value);
+ is_one_push_in_operation(bool *value);
+ start_one_push_operation();
+ can_read_out(bool *value);
+ can_turn_on_off(bool *value);
+ is_feature_on(bool *value);
+ feature_on_off(uint value);
+ has_auto_mode(bool *value);
+ has_manual_mode(bool *value);
+ is_feature_auto(bool *value);
+ auto_on_off(uint value);
+ get_min_value(uint *value);
+ get_max_value(uint *value);
+}end
+
+# DMA Capture Functions
+#dma_release_camera(RH rh, CameraCapture *camera);
+#dma_unlisten(RH rh, CameraCapture *camera);
+#dma_single_capture(CameraCapture *camera);
+#dma_multi_capture(CameraCapture *cams,int num);
+#dma_done_with_buffer(CameraCapture * camera);
+
+# default return type is int, prolly means SUCCESS/FAILURE
+
+#RNM = RN+uint mode
+class RNM; %{
+ query_format7_max_image_size(uint *horizontal_size, uint *vertical_size);
+ query_format7_unit_size(uint *horizontal_unit, uint *vertical_unit);
+ query_format7_color_coding(quadlet_t *value);
+ query_format7_pixel_number(uint *pixnum);
+ query_format7_total_bytes(uint *total_bytes);
+ query_format7_packet_para(uint *min_bytes, uint *max_bytes);
+ query_format7_recommended_byte_per_packet(uint *bpp);
+ query_format7_packet_per_frame(uint *ppf);
+ query_format7_unit_position(uint *horizontal_pos, uint *vertical_pos);
+ # those were query/set pairs.
+ double_qattr format7_image_position(uint left, uint top);
+ double_qattr format7_image_size(uint width, uint height);
+ qattr uint format7_color_coding_id
+ qattr uint format7_byte_per_packet
+ query_format7_value_setting(uint *present, uint *setting1, uint *err_flag1, uint *err_flag2);
+ set_format7_value_setting(); //huh?
+}end
+
+\end ruby
+;
+
+\class FormatDC1394 < Format
+struct FormatDC1394 : Format {
+ \decl void initialize (Symbol mode);
+ \decl void frame ();
+};
+
+\def void initialize(Symbol mode) {
+ gfpost("DC1394: hello world");
+ RH rh = raw1394_new_handle();
+ int numPorts = raw1394_get_port_info(rh,0,0);
+ raw1394_destroy_handle(rh);
+ gfpost("there are %d Feuerweuer ports",numPorts);
+ if (mode!=SYM(in)) RAISE("sorry, read-only");
+ for(int port=0; port<numPorts; port++) {
+ gfpost("trying port #%d...",port);
+ RH rh = dc1394_create_handle(port);
+ int numCameras=0xDEADBEEF;
+ NID *nodes = dc1394_get_camera_nodes(rh,&numCameras,0);
+ gfpost("port #%d has %d cameras",port,numCameras);
+ for (int i=0; i<numCameras; i++) gfpost("camera at node #%d",nodes[i]);
+ // I'm stuck here, can't find that iSight camera. -- matju
+ }
+ dc1394_destroy_handle(rh);
+}
+
+\def void frame () {
+ gfpost("i'd like to get a frame from the cam, but how?");
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:dc1394',1,1;@flags=4;@comment='Video4linux 1.x'");
+ //IEVAL(rself,ruby_code);
+ rb_funcall(rself,SI(instance_eval),3,rb_str_new2(ruby_code),
+ rb_str_new2(__FILE__),INT2NUM(ruby_lineno+3));
+}
+\end class FormatDC1394
+void startup_dc1394 () {
+ \startall
+}
diff --git a/externals/gridflow/format/jpeg.c b/externals/gridflow/format/jpeg.c
new file mode 100644
index 00000000..e59d8e06
--- /dev/null
+++ b/externals/gridflow/format/jpeg.c
@@ -0,0 +1,130 @@
+/*
+ $Id: jpeg.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 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 "../base/grid.h.fcs"
+/* removing macros (removing warnings) */
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef EXTERN
+extern "C" {
+#include <jpeglib.h>
+};
+
+\class FormatJPEG < Format
+struct FormatJPEG : Format {
+ P<BitPacking> bit_packing;
+ struct jpeg_compress_struct cjpeg;
+ struct jpeg_decompress_struct djpeg;
+ struct jpeg_error_mgr jerr;
+ int fd;
+ FILE *f;
+ \decl Ruby frame ();
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \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_factor(in->dim->get(1)*in->dim->get(2));
+ 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,Pt<uint8>(row,rowsize));
+ 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 Ruby frame () {
+ off_t off = NUM2LONG(rb_funcall(rb_ivar_get(rself,SI(@stream)),SI(tell),0));
+ fseek(f,off,SEEK_SET);
+ if (gfeof(f)) return Qfalse;
+ 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),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@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,Pt<uint8>(row,sx*chans));
+ }
+ jpeg_finish_decompress(&djpeg);
+ jpeg_destroy_decompress(&djpeg);
+ return Qnil;
+}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (source!=SYM(file)) RAISE("usage: jpeg file <filename>");
+ rb_funcall(rself,SI(raw_open),3,mode,source,filename);
+ Ruby stream = rb_ivar_get(rself,SI(@stream));
+ fd = NUM2INT(rb_funcall(stream,SI(fileno),0));
+ f = fdopen(fd,mode==SYM(in)?"r":"w");
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,
+ "install '#in:jpeg',1,1;@mode=6;"
+ "include GridFlow::EventIO; suffixes_are'jpeg','jpg'");
+}
+\end class FormatJPEG
+void startup_jpeg () {
+ \startall
+}
diff --git a/externals/gridflow/format/main.rb b/externals/gridflow/format/main.rb
new file mode 100644
index 00000000..6e31be75
--- /dev/null
+++ b/externals/gridflow/format/main.rb
@@ -0,0 +1,788 @@
+=begin
+ $Id: main.rb,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+=end
+
+require "socket"
+require "fcntl"
+
+module GridFlow
+
+class<<self
+ def max_rank; 16; end
+ def max_size; 64*1024**2; end
+ def max_packet; 1024*2; end
+end
+
+ENDIAN_BIG,ENDIAN_LITTLE,ENDIAN_SAME,ENDIAN_DIFF = 0,1,2,3
+
+OurByteOrder = case [1].pack("L")
+ when "\0\0\0\1"; ENDIAN_BIG # Mac, Sun, SiliconGraphics
+ when "\1\0\0\0"; ENDIAN_LITTLE # Intel
+ else raise "Cannot determine byte order" end
+
+class Format < GridObject
+ FF_R,FF_W = 4,2 # flags indicating support of :in and :out respectively.
+ attr_accessor :parent
+=begin API (version 0.8)
+ 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 frame() :
+ 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 seek(Integer i) : select one frame to be read next (by number)
+ def length() : ^Integer returns number of frames (never implemented ?)
+ def close() : close a handler
+ inlet 0 :
+ grid : frame to write
+ other : special options
+ outlet 0 : grid : frame just read
+ outlet 1 : everything else
+=end
+
+ def initialize(mode,*)
+ super
+ @cast = :int32
+ @colorspace = :rgb
+ @mode = mode
+ @frame = 0
+ @parent = nil
+ @stream = nil
+ flags = self.class.instance_eval{if defined?@flags then @flags else 6 end}
+ # FF_W, FF_R, FF_RW
+ 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}'"
+ end
+
+ def close
+ @stream.close if defined? @stream and @stream
+ end
+
+ def self.suffixes_are(*suffixes)
+ suffixes.map{|s|s.split(/[, ]/)}.flatten.each {|suffix|
+ Format.suffixes[suffix] = self
+ }
+ end
+
+ class<<self
+ attr_reader :symbol_name
+ attr_reader :description
+ attr_reader :flags
+ attr_reader :suffixes
+ end
+ @suffixes = {}
+ def seek frame
+ (rewind; return) if frame == 0
+ raise "don't know how to seek for frame other than # 0"
+ end
+
+ # this is what you should use to rewind
+ # different file-sources may redefine this as something else
+ # (eg: gzip)
+ def rewind
+ raise "Nothing to rewind about..." if not @stream
+ @stream.seek 0,IO::SEEK_SET
+ @frame = 0
+ end
+
+ # This is different from IO#eof, which waits until a read has failed
+ # doesn't work in nonblocking mode? (I don't recall why)
+ def eof?
+ thispos = (@stream.seek 0,IO::SEEK_CUR; @stream.tell)
+ lastpos = (@stream.seek 0,IO::SEEK_END; @stream.tell)
+ @stream.seek thispos,IO::SEEK_SET
+ return thispos == lastpos
+ rescue Errno::ESPIPE # just ignore if seek is not possible
+ return false
+ end
+
+ # "ideal" buffer size or something
+ # the buffer may be bigger than this but usually not by much.
+ def self.buffersize; 16384 end
+
+ def _0_headerless(*args) #!@#$ goes in FormatGrid ?
+ args=args[0] if Array===args[0]
+ #raise "expecting dimension list..."
+ args.map! {|a|
+ Numeric===a or raise "expecting dimension list..."
+ a.to_i
+ }
+ @headerless = args
+ end
+ def _0_headerful #!@#$ goes in FormatGrid ?
+ @headerless = nil
+ end
+ def _0_type arg
+ #!@#$ goes in FormatGrid ?
+ #!@#$ bug: should not be able to modify this _during_ a transfer
+ case arg
+ when :uint8; @bpv=8; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xff])
+ when :int16; @bpv=16; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xffff])
+ when :int32; @bpv=32; @bp=nil
+ else raise "unsupported number type: #{arg}"
+ end
+ end
+ def _0_cast arg
+ case arg
+ when :uint8, :int16, :int32, :int64, :float32, :float64
+ @cast = arg
+ else raise "unsupported number type: #{arg}"
+ end
+ end
+ def frame; @frame+=1; @frame-1 end
+end
+
+# common parts between GridIn and GridOut
+module GridIO
+ def check_file_open; if not @format then raise "can't do that: file not open" end end
+ def _0_close; check_file_open; @format.close; @format = nil end
+ def delete; @format.close if @format; @format = nil; super end
+ attr_reader :format
+
+ def _0_open(sym,*a)
+ sym = sym.intern if String===sym
+ if a.length==0 and /\./ =~ sym.to_s then a=[sym]; sym=:file end
+ qlass = GridFlow.fclasses["\#io:#{sym}"]
+ if not qlass then raise "unknown file format identifier: #{sym}" end
+ _0_close if @format
+ @format = qlass.new @mode, *a
+ @format.connect 0,self,1
+ @format.connect 1,self,2
+ @format.parent = self
+ @loop = true
+ end
+
+ def _0_timelog flag; @timelog = Integer(flag)!=0 end
+ def _0_loop flag; @loop = Integer(flag)!=0 end
+ def method_missing(*message)
+ sel = message[0].to_s
+ if sel =~ /^_0_/
+ message[0] = sel.sub(/^_0_/,"").intern
+ @format.send_in 0, *message
+ elsif sel =~ /^_2_/
+ sel = sel.sub(/^_2_/,"").intern
+ message.shift
+ send_out 1, sel, *message
+ else
+ return super
+ end
+ end
+end
+
+GridObject.subclass("#in",1,2) {
+ install_rgrid 0
+ include GridIO
+ def initialize(*a)
+ super
+ @format = nil
+ @timelog = false
+ @framecount = 0
+ @time = Time.new
+ @mode = :in
+ return if a.length==0
+ _0_open(*a)
+ end
+ def _0_bang
+ check_file_open
+ framenum = @format.frame
+ if framenum == false
+ send_out 1
+ return if not @loop
+ @format.seek 0
+ framenum = @format.frame
+ if framenum == false
+ raise "can't read frame: the end is at the beginning???"
+ end
+ end
+ send_out 1, framenum if framenum
+ end
+ def _0_float frame; _0_set frame; _0_bang end
+ def _0_set frame; check_file_open; @format.seek frame end
+ def _0_reset; check_file_open; @format.seek 0; end
+ def _1_grid(*a) send_out 0,:grid,*a end
+ def _0_load name; _0_open name; _0_bang; _0_close end
+}
+
+GridObject.subclass("#out",1,1) {
+ include GridIO
+ def initialize(*a)
+ super
+ @format = nil
+ @timelog = false
+ @framecount = 0
+ @time = Time.new
+ @mode = :out
+ return if a.length==0
+ if Integer===a[0] or Float===a[0]
+ _0_open :x11,:here
+ _0_out_size a[0],a[1]
+ else
+ _0_open(*a)
+ end
+ end
+
+ def _0_list(*a) @format._0_list(*a) end
+
+ # hacks
+ def _1_grid(*a) send_out 0,:grid,*a end # for aalib
+ def _1_position(*a) send_out 0,:position,*a end
+ def _1_keypress(*a) send_out 0,:keypress,*a end
+ def _1_keyrelease(*a) send_out 0,:keyrelease,*a end
+
+ def _0_grid(*a)
+ check_file_open
+ @format._0_grid(*a)
+ send_out 0,:bang
+ log if @timelog
+ @framecount+=1
+ end
+
+ def log
+ time = Time.new
+ post("\#out: frame#%04d time: %10.3f s; diff: %5d ms",
+ @framecount, time, ((time-@time)*1000).to_i)
+ @time = time
+ end
+ install_rgrid 0
+}
+
+class BitPacking
+ alias pack pack2
+ alias unpack unpack2
+end
+
+# adding event-driven IO to a Format class
+module EventIO
+ def read_wait?; !!@action; end
+
+ def initialize(*)
+ @acceptor = nil
+ @buffer = nil
+ @action = nil
+ @chunksize = nil
+ @rewind_redefined = false
+ @clock = Clock.new self
+ @delay = 100 # ms
+ super
+ end
+
+ def call() try_read end
+
+ def on_read(n,&action)
+ @action = action
+ @chunksize = n
+ end
+
+ def try_accept
+ #!@#$ use setsockopt(SO_REUSEADDR) here???
+ TCPSocket.do_not_reverse_lookup = true # hack
+ @acceptor.nonblock = true
+ @stream = @acceptor.accept
+ @stream.nonblock = true
+ @stream.sync = true
+ @clock.unset
+# send_out 0, :accept # does not work
+ rescue Errno::EAGAIN
+ end
+
+ def try_read(dummy=nil)
+ n = @chunksize-(if @buffer then @buffer.length else 0 end)
+ t = @stream.read(n) # or raise EOFError
+ if not t
+ raise "heck" if not @stream.eof?
+ rewind
+ t = @stream.read(n) or raise "can't read any of #{n} bytes?"
+ end
+ if @buffer then @buffer << t else @buffer = t end
+ if @buffer.length == @chunksize
+ action,buffer = @action,@buffer
+ @action,@buffer = nil,""
+ @clock.unset
+ action.call buffer
+ end
+ rescue Errno::EAGAIN
+ post "read would block"
+ end
+
+ def raw_open_gzip_in(filename)
+ r,w = IO.pipe
+ if pid=fork
+ GridFlow.subprocesses[pid]=true
+ w.close
+ @stream = r
+ else
+ r.close
+ STDOUT.reopen w
+ STDIN.reopen @stream
+ @stream = File.open filename, "r"
+ exec "gzip", "-dc"
+ end
+ end
+ def raw_open_gzip_out(filename)
+ r,w = IO.pipe
+ if pid=fork
+ GridFlow.subprocesses[pid]=true
+ r.close
+ @stream = w
+ else
+ w.close
+ STDIN.reopen r
+ STDOUT.reopen @stream
+ @stream = File.open filename, "w"
+ exec "gzip", "-c"
+ end
+ end
+ def raw_open(mode,source,*args)
+ @raw_open_args = mode,source,*args
+ fmode = case mode
+ when :in; "r"
+ when :out; "w"
+ else raise "bad mode" end
+ close
+ case source
+ when :file
+ filename = args[0].to_s
+ filename = GridFlow.find_file filename if mode==:in
+ @stream = File.open filename, fmode
+ when :gzfile
+ filename = args[0].to_s
+ filename = GridFlow.find_file filename if mode==:in
+ if mode==:in then
+ raw_open_gzip_in filename
+ else
+ raw_open_gzip_out filename
+ end
+ def self.rewind
+ raw_open(*@raw_open_args)
+ @frame = 0
+ end unless @rewind_redefined
+ @rewind_redefined = true
+ when :tcp
+ if RUBY_VERSION < "1.6.6"
+ raise "use at least 1.6.6 (reason: bug in socket code)"
+ end
+ post "-----------"
+ time = Time.new
+ TCPSocket.do_not_reverse_lookup = true # hack
+ @stream = TCPSocket.open(args[0].to_s,args[1].to_i)
+ post "----------- #{Time.new-time}"
+ @stream.nonblock = true
+ @stream.sync = true
+ @clock.delay @delay
+ when :tcpserver
+ TCPSocket.do_not_reverse_lookup = true # hack
+ TCPServer.do_not_reverse_lookup = true # hack
+ post "-----------"
+ time = Time.new
+ @acceptor = TCPServer.open(args[0].to_s)
+ post "----------- #{Time.new-time}"
+ @acceptor.nonblock = true
+ #$tasks[self] = proc {self.try_accept} #!!!!!
+ else
+ raise "unknown access method '#{source}'"
+ end
+ end
+ def close
+ @acceptor.close if @acceptor
+ @stream.close if @stream
+ GridFlow.hunt_zombies
+ end
+end
+
+Format.subclass("#io:file",1,1) {
+ def self.new(mode,file)
+ file=file.to_s
+ a = [mode,:file,file]
+ if not /\./=~file then raise "no filename suffix?" end
+ suf=file.split(/\./)[-1]
+ h=Format.suffixes[suf]
+ if not h then raise "unknown suffix '.#{suf}'" end
+ h.new(*a)
+ end
+ @comment="format autodetection proxy"
+}
+
+Format.subclass("#io:grid",1,1) {
+ include EventIO
+ install_rgrid 0
+ @comment = "GridFlow file format"
+ suffixes_are "grid"
+=begin
+ This is the Grid format I defined:
+ 1 uint8: 0x7f
+ 4 uint8: "GRID" big endian | "grid" little endian
+ 1 uint8: type {
+ number of bits in 8,16,32,64, plus one of: 1:unsigned 2:float
+ but float8,float16 are not allowed (!)
+ }
+ 1 uint8: reserved (supported: 0)
+ 1 uint8: number of dimensions N (supported: at least 0..4)
+ N uint32: number of elements per dimension D[0]..D[N-1]
+ raw data goes there.
+=end
+ # bits per value: 32 only
+ attr_accessor :bpv # Fixnum: bits-per-value
+ # endianness
+ # attr_accessor :endian # ENDIAN_LITTLE or ENDIAN_BIG
+ # IO or File or TCPSocket
+ attr_reader :stream
+ # nil=headerful; array=assumed dimensions of received grids
+ #attr_accessor :headerless
+
+ def initialize(mode,source,*args)
+ super
+ @bpv = 32
+ @headerless = nil
+ @endian = OurByteOrder
+ raw_open mode,source,*args
+ end
+
+ def post(*s)
+ # because i'm using miller_0_38 and it can't disable the console
+ # i am using fprintf stderr instead of post.
+ ### STDERR.puts(sprintf(*s))
+ # disabled because i don't need it now
+ end
+
+ # rewinding and starting
+ def frame
+ raise "can't get frame when there is no connection" if not @stream
+ raise "already waiting for input" if read_wait?
+ return false if eof?
+ post "----- 1"
+ if @headerless then
+ @n_dim=@headerless.length
+ @dim = @headerless
+ @dex = 0
+ set_bufsize
+ send_out_grid_begin 0, @dim
+ on_read(bufsize) {|data| frame3 data }
+ else
+ on_read(8) {|data| frame1 data }
+ end
+ post "----- 2"
+ (try_read nil while read_wait?) if not TCPSocket===@stream
+ post "----- 3"
+ super
+ post "----- 4"
+ end
+
+ def set_bufsize
+ @prod = 1
+ @dim.each {|x| @prod *= x }
+ n = @prod/@dim[0]
+ k = GridFlow.max_packet / n
+ k=1 if k<1
+ @bufsize = k*n*@bpv/8
+ @bufsize = @prod if @bufsize > @prod
+ end
+
+ # the header
+ def frame1 data
+ post "----- frame1"
+ head,@bpv,reserved,@n_dim = data.unpack "a5ccc"
+ @endian = case head
+ when "\x7fGRID"; ENDIAN_BIG
+ when "\x7fgrid"; ENDIAN_LITTLE
+ else raise "grid header: invalid (#{data.inspect})" end
+ case bpv
+ when 8, 16, 32; # ok
+ else raise "unsupported bpv (#{@bpv})"
+ end
+ if reserved!=0
+ raise "reserved field is not zero"
+ end
+ if @n_dim > GridFlow.max_rank
+ raise "too many dimensions (#{@n_dim})"
+ end
+ on_read(4*@n_dim) {|data| frame2 data }
+ end
+
+ # the dimension list
+ def frame2 data
+ post "----- frame2"
+ @dim = data.unpack(if @endian==ENDIAN_LITTLE then "V*" else "N*" end)
+ set_bufsize
+ if @prod > GridFlow.max_size
+ raise "dimension list: invalid prod (#{@prod})"
+ end
+ send_out_grid_begin 0, @dim, @cast
+
+ on_read(bufsize) {|data| frame3 data }
+ @dex = 0
+ end
+
+ attr_reader :bufsize
+
+ # for each slice of the body
+ def frame3 data
+ post "----- frame3 with dex=#{@dex.inspect}, prod=#{@prod.inspect}"
+ n = data.length
+ nn = n*8/@bpv
+ # is/was there a problem with the size of the data being read?
+ case @bpv
+ when 8
+ @bp = BitPacking.new(@endian,1,[0xff])
+ send_out_grid_flow(0, @bp.unpack(data))
+ @dex += data.length
+ when 16
+ @bp = BitPacking.new(@endian,2,[0xffff])
+ send_out_grid_flow(0, @bp.unpack(data))
+ @dex += data.length/2
+ when 32
+ data.swap32! if @endian!=OurByteOrder
+ send_out_grid_flow 0, data
+ @dex += data.length/4
+ end
+ if @dex >= @prod
+ @clock.unset
+ else
+ on_read(bufsize) {|data| frame3 data }
+ end
+ end
+
+ def _0_rgrid_begin
+ if not @stream
+ raise "can't send frame when there is no connection"
+ end
+ @dim = inlet_dim 0
+ post "@dim=#{@dim.inspect}"
+ return if @headerless
+ # header
+ @stream.write(
+ [if @endian==ENDIAN_LITTLE then "\x7fgrid" else "\x7fGRID" end,
+ @bpv,0,@dim.length].pack("a5ccc"))
+ # dimension list
+ @stream.write(
+ @dim.to_a.pack(if @endian==ENDIAN_LITTLE then "V*" else "N*" end))
+ end
+
+ def _0_rgrid_flow data
+ case @bpv
+ when 8, 16
+ @stream.write @bp.pack(data)
+ when 32
+ data.swap32! if GridFlow::OurByteOrder != @endian
+ @stream.write data
+ end
+ end
+
+ def _0_rgrid_end; @stream.flush end
+
+ def endian(a)
+ @endian = case a
+ when :little; ENDIAN_LITTLE
+ when :big; ENDIAN_BIG
+ when :same; ENDIAN_SAME
+ else raise "argh"
+ end
+ end
+
+ def headerless(*args)
+ args=args[0] if Array===args[0]
+ args.map! {|a|
+ Numeric===a or raise "expecting dimension list..."
+ a.to_i
+ }
+ @headerless = args
+ end
+
+ def headerful; @headerless = nil end
+
+ #!@#$ method name conflict ?
+ def type(nt)
+ #!@#$ bug: should not be able to modify this _during_ a transfer
+ case nt
+ when :uint8; @bpv= 8; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xff])
+ when :int16; @bpv=16; @bp=BitPacking.new(ENDIAN_LITTLE,1,[0xffff])
+ when :int32; @bpv=32; @bp=nil
+ else raise "unsupported number type"
+ end
+ end
+}
+
+module PPMandTarga
+ # "and false" disables features that may cause crashes and don't
+ # accelerate gridflow that much.
+ def frame_read_body height, width, channels
+ bs = width*channels
+ n = bs*height
+ bs = (self.class.buffersize/bs)*bs+bs # smallest multiple of bs over BufferSize
+ buf = ""
+ if RUBY_VERSION >= "1.8.0" and false
+ data = "x"*bs # must preallocate (bug in 1.8.0.pre1-3)
+ while n>0 do
+ bs=n if bs>n
+ @stream.read(bs,data) or raise EOFError
+ if @bp then
+ send_out_grid_flow 0, @bp.unpack(data,buf)
+ else
+ send_out_grid_flow 0, data, :uint8
+ end
+ n-=bs
+ end
+ else
+ nothing = ""
+ while n>0 do
+ bs=n if bs>n
+ data = @stream.read(bs) or raise EOFError
+ if @bp then
+ send_out_grid_flow 0, @bp.unpack(data,buf)
+ else
+ send_out_grid_flow 0, data, :uint8
+ end
+ data.replace nothing and false # prevent clogging memory
+ n-=bs
+ end
+ end
+ end
+end
+
+Format.subclass("#io:ppm",1,1) {
+ install_rgrid 0
+ @comment = "Portable PixMap (PPM) File Format"
+ suffixes_are "ppm"
+ include EventIO, PPMandTarga
+
+ def initialize(mode,source,*args)
+ @bp = if mode==:out
+ BitPacking.new(ENDIAN_LITTLE,3,[0x0000ff,0x00ff00,0xff0000])
+ else nil end
+ super
+ raw_open mode,source,*args
+ end
+ def frame
+ #@stream.sync = false
+ metrics=[]
+ return false if eof?
+ line = @stream.gets
+ (rewind; line = @stream.gets) if not line # hack
+ line.chomp!
+ if line != "P6" then raise "Wrong format (needing PPM P6)" end
+ while metrics.length<3
+ line = @stream.gets
+ next if line =~ /^#/
+ metrics.push(*(line.split(/\s+/).map{|x| Integer x }))
+ end
+ metrics[2]==255 or
+ raise "Wrong color depth (max_value=#{metrics[2]} instead of 255)"
+
+ send_out_grid_begin 0, [metrics[1], metrics[0], 3], @cast
+ frame_read_body metrics[1], metrics[0], 3
+ super
+ end
+
+ def _0_rgrid_begin
+ dim = inlet_dim 0
+ raise "expecting (rows,columns,channels)" if dim.length!=3
+ raise "expecting channels=3" if dim[2]!=3
+ @stream.write "P6\n"
+ @stream.write "# generated using GridFlow #{GF_VERSION}\n"
+ @stream.write "#{dim[1]} #{dim[0]}\n255\n"
+ @stream.flush
+ inlet_set_factor 0, 3
+ end
+ def _0_rgrid_flow(data) @stream.write @bp.pack(data) end
+ def _0_rgrid_end; @stream.flush end
+}
+
+Format.subclass("#io:targa",1,1) {
+ install_rgrid 0
+ @comment = "TrueVision Targa"
+ suffixes_are "tga"
+ include EventIO, PPMandTarga
+=begin
+targa header is like:
+ [:comment, Uint8, :length],
+ [:colortype, Uint8],
+ [:colors, Uint8], 5,
+ [:origin_x, Int16],
+ [:origin_y, Int16],
+ [:w, Uint16],
+ [:h, Uint16],
+ [:depth, Uint8], 1,
+ [:comment, String8Unpadded, :data],
+=end
+ def initialize(mode,source,*args)
+ super
+ raw_open mode,source,*args
+ end
+
+ def set_bitpacking depth
+ @bp = case depth
+ #!@#$ endian here doesn't seem to be changing much ?
+ when 24; BitPacking.new(ENDIAN_LITTLE,3,[0xff0000,0x00ff00,0x0000ff])
+ when 32; BitPacking.new(ENDIAN_LITTLE,4,
+ [0x00ff0000,0x0000ff00,0x000000ff,0xff000000])
+ else
+ raise "tga: unsupported colour depth: #{depth}\n"
+ end
+ end
+
+ def frame
+ return false if eof?
+ head = @stream.read(18)
+ comment_length,colortype,colors,w,h,depth = head.unpack("cccx9vvcx")
+ comment = @stream.read(comment_length)
+ raise "unsupported color format: #{colors}" if colors != 2
+# post "tga: size y=#{h} x=#{w} depth=#{depth} colortype=#{colortype}"
+# post "tga: comment: \"#{comment}\""
+ set_bitpacking depth
+ send_out_grid_begin 0, [ h, w, depth/8 ], @cast
+ frame_read_body h, w, depth/8
+ super
+ end
+
+ def _0_rgrid_begin
+ dim = inlet_dim 0
+ raise "expecting (rows,columns,channels)" if dim.length!=3
+ raise "expecting channels=3 or 4" if dim[2]!=3 and dim[2]!=4
+ # comment = "created using GridFlow"
+ #!@#$ why did i use that comment again?
+ comment = "generated using GridFlow #{GF_VERSION}"
+ @stream.write [comment.length,colortype=0,colors=2,"\0"*9,
+ dim[1],dim[0],8*dim[2],(8*(dim[2]-3))|32,comment].pack("ccca9vvcca*")
+ set_bitpacking 8*dim[2]
+ inlet_set_factor 0, dim[2]
+ end
+ def _0_rgrid_flow data; @stream.write @bp.pack(data) end
+ def _0_rgrid_end; @stream.flush end
+}
+end # module GridFlow
diff --git a/externals/gridflow/format/mpeg3.c b/externals/gridflow/format/mpeg3.c
new file mode 100644
index 00000000..d6820ccd
--- /dev/null
+++ b/externals/gridflow/format/mpeg3.c
@@ -0,0 +1,98 @@
+/*
+ $Id: mpeg3.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 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 "../base/grid.h.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+\class FormatMPEG3 < Format
+struct FormatMPEG3 : Format {
+ mpeg3_t *mpeg;
+ P<BitPacking> bit_packing;
+ int track;
+ FormatMPEG3 () : track(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void seek (int frame);
+ \decl Ruby frame ();
+ \decl void close ();
+};
+
+\def void seek (int frame) { mpeg3_set_frame(mpeg,frame,track); }
+
+\def Ruby frame () {
+ int nframe = mpeg3_get_frame(mpeg,track);
+ if (nframe >= mpeg3_video_frames(mpeg,track)) return Qfalse;
+
+ int sx = mpeg3_video_width(mpeg,track);
+ int sy = mpeg3_video_height(mpeg,track);
+ int npixels = sx*sy;
+ int channels = 3;
+ Pt<uint8> buf = ARRAY_NEW(uint8,sy*sx*channels+16);
+ uint8 *rows[sy];
+ for (int i=0; i<sy; i++) rows[i]=buf+i*sx*channels;
+ int result = mpeg3_read_frame(mpeg,rows,0,0,sx,sy,sx,sy,MPEG3_RGB888,track);
+
+ GridOutlet out(this,0,new Dim(sy, sx, channels),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ int bs = out.dim->prod(1);
+ STACK_ARRAY(int32,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> row = buf+channels*sx*y;
+ /* bit_packing->unpack(sx,row,b2); out.send(bs,b2); */
+ out.send(bs,row);
+ }
+ delete[] (uint8 *)buf;
+ return INT2NUM(nframe);
+}
+
+\def void close () {
+// fprintf(stderr, "begin mpeg3_close...\n");
+ if (mpeg) { mpeg3_close(mpeg); mpeg=0; }
+ rb_call_super(argc,argv);
+// fprintf(stderr, "end mpeg3_close...\n");
+}
+
+// libmpeg3 may be nice, but it won't take a filehandle, only filename
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (mode!=SYM(in)) RAISE("read-only, sorry");
+ if (source!=SYM(file)) RAISE("usage: mpeg file <filename>");
+ if (TYPE(filename)!=T_STRING) RAISE("PATATE POILUE");
+ filename = rb_funcall(mGridFlow,SI(find_file),1,filename);
+ mpeg = mpeg3_open(rb_str_ptr(filename));
+ if (!mpeg) RAISE("IO Error: can't open file `%s': %s", filename, strerror(errno));
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,"install '#in:mpeg',1,1;@flags=4;"
+ "@comment='Motion Picture Expert Group Format"
+ " (using HeroineWarrior\\'s)';suffixes_are'mpg,mpeg'");
+}
+\end class FormatMPEG3
+void startup_mpeg3 () {
+ \startall
+}
diff --git a/externals/gridflow/format/png.c b/externals/gridflow/format/png.c
new file mode 100644
index 00000000..69a0254e
--- /dev/null
+++ b/externals/gridflow/format/png.c
@@ -0,0 +1,138 @@
+/*
+ $Id: png.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 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 "../base/grid.h.fcs"
+extern "C" {
+#include <libpng12/png.h>
+};
+
+\class FormatPNG < Format
+struct FormatPNG : Format {
+ P<BitPacking> bit_packing;
+ png_structp png;
+ png_infop info;
+ int fd;
+ FILE *f;
+ FormatPNG () : bit_packing(0), png(0), f(0) {}
+ \decl Ruby frame ();
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \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_factor(in->dim->get(1)*in->dim->get(2));
+ 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,Pt<uint8>(row,rowsize));
+ n-=rowsize; data+=rowsize;
+ }
+} GRID_FINISH {
+} GRID_END
+
+\def Ruby frame () {
+ uint8 sig[8];
+ if (!fread(sig, 1, 8, f)) return Qfalse;
+ 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);
+ Pt<uint8> image_data = ARRAY_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),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ out.send(rowbytes*height,image_data);
+ free(image_data);
+ png_destroy_read_struct(&png, &info, NULL);
+ return Qnil;
+}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (source!=SYM(file)) RAISE("usage: png file <filename>");
+ rb_funcall(rself,SI(raw_open),3,mode,source,filename);
+ Ruby stream = rb_ivar_get(rself,SI(@stream));
+ fd = NUM2INT(rb_funcall(stream,SI(fileno),0));
+ f = fdopen(fd,mode==SYM(in)?"r":"w");
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,
+ "install '#io:png',1,1;@mode=4;include GridFlow::EventIO; suffixes_are'png'");
+}
+\end class FormatPNG
+void startup_png () {
+ \startall
+}
diff --git a/externals/gridflow/format/quicktimeapple.c b/externals/gridflow/format/quicktimeapple.c
new file mode 100644
index 00000000..0664322e
--- /dev/null
+++ b/externals/gridflow/format/quicktimeapple.c
@@ -0,0 +1,500 @@
+/*
+ $Id: quicktimeapple.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 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 T_DATA T_COCOA_DATA
+#include <Quicktime/Quicktime.h>
+#include <Quicktime/Movies.h>
+#include <Quicktime/QuickTimeComponents.h>
+#undef T_DATA
+#include "../base/grid.h.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <CoreServices/CoreServices.h>
+
+typedef ComponentInstance VideoDigitizerComponent, VDC;
+typedef ComponentResult VideoDigitizerError, VDE;
+
+//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)
+\end class VDC
+*/
+
+\class FormatQuickTimeCamera < Format
+struct FormatQuickTimeCamera : Format {
+ P<Dim> dim;
+ Pt<uint8> buf;
+ 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;
+ FormatQuickTimeCamera() : vdc(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void frame ();
+ \decl void close ();
+ \grin 0 int
+};
+
+// /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Components.h
+
+static int nn(int c) {return c?c:' ';}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ L
+//vdc = SGGetVideoDigitizerComponent(c);
+ rb_call_super(argc,argv);
+ 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);
+ gfpost("Component #%d",n);
+ char *t = (char *)&cd.componentType;
+ gfpost(" type='%c%c%c%c'",nn(t[3]),nn(t[2]),nn(t[1]),nn(t[0]));
+ t = (char *)&cd.componentSubType;
+ gfpost(" subtype='%c%c%c%c'",nn(t[3]),nn(t[2]),nn(t[1]),nn(t[0]));
+ gfpost(" name=%08x, *name='%*s'",name, *name, name+1);
+ gfpost(" info=%08x, *info='%*s'",info, *name, info+1);
+ n++;
+ }
+ gfpost("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) gfpost("could not make new SG channel");
+ e=SGSetChannelBounds(m_vc, &rect);
+ if(e!=noErr) gfpost("could not set SG ChannelBounds");
+ e=SGSetChannelUsage(m_vc, seqGrabPreview);
+ if(e!=noErr) gfpost("could not set SG ChannelUsage");
+// 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 = ARRAY_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 = ARRAY_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);
+}
+
+/*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 void frame () {
+ GridOutlet out(this,0,dim);
+ out.send(dim->prod(),buf);
+L}
+
+\def void close () {
+ L
+ if (m_vc) {
+ if (::SGDisposeChannel(m_sg, m_vc)) RAISE("SGDisposeChannel");
+ m_vc=0;
+ }
+ if (m_sg) {
+ if (::CloseComponent(m_sg)) RAISE("CloseComponent");
+ m_sg = NULL;
+ if (m_srcGWorld) {
+ ::DisposeGWorld(m_srcGWorld);
+ m_pixMap = NULL;
+ m_srcGWorld = NULL;
+ m_baseAddr = NULL;
+ }
+ }
+}
+
+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_factor(in->dim->prod());
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\classinfo {
+ IEVAL(rself,
+\ruby
+ install '#io:quicktimecamera',1,1
+ @comment="Apple Quicktime (CAMERA MODULE)"
+ @flags=4
+\end ruby
+);}
+\end class FormatQuickTimeCamera
+
+\class FormatQuickTimeApple < Format
+struct FormatQuickTimeApple : Format {
+ Movie movie;
+ TimeValue time;
+ short movie_file;
+ GWorldPtr gw; /* just like an X11 Image or Pixmap, maybe. */
+ Pt<uint8> buffer;
+ P<Dim> dim;
+ int nframe, nframes;
+
+ FormatQuickTimeApple() : movie(0), time(0), movie_file(0), gw(0),
+ buffer(), dim(0), nframe(0), nframes(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void close ();
+ \decl void codec_m (String c);
+ \decl void colorspace_m (Symbol c);
+ \decl Ruby frame ();
+ \decl void seek (int frame);
+ \grin 0
+};
+
+\def void seek (int frame) {
+ nframe=frame;
+}
+
+\def Ruby frame () {
+ CGrafPtr savedPort;
+ GDHandle savedDevice;
+ SetMovieGWorld(movie,gw,GetGWorldDevice(gw));
+ Rect r;
+ GetMovieBox(movie,&r);
+ PixMapHandle pixmap = GetGWorldPixMap(gw);
+ short flags = nextTimeStep;
+ if (nframe>=nframes) return Qfalse;
+ if (nframe==0) flags |= nextTimeEdgeOK;
+ TimeValue duration;
+ OSType mediaType = VisualMediaCharacteristic;
+ GetMovieNextInterestingTime(movie,
+ flags,1,&mediaType,time,0,&time,&duration);
+ if (time<0) {
+ time=0;
+ return Qfalse;
+ }
+// gfpost("quicktime frame #%d; time=%d duration=%d", nframe, (long)time, (long)duration);
+ SetMovieTimeValue(movie,nframe*duration);
+ MoviesTask(movie,0);
+ GridOutlet out(this,0,dim);
+ Pt<uint32> bufu32 = Pt<uint32>((uint32 *)buffer.p,dim->prod()/4);
+ int n = dim->prod()/4;
+ int i;
+ 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_factor(in->dim->prod());
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\def void codec_m (String c) { RAISE("Unimplemented. Sorry."); }
+\def void colorspace_m (Symbol c) { RAISE("Unimplemented. Sorry."); }
+
+\def void close () {
+//!@#$
+ if (movie) {
+ DisposeMovie(movie);
+ DisposeGWorld(gw);
+ CloseMovieFile(movie_file);
+ movie_file=0;
+ }
+ rb_call_super(argc,argv);
+}
+
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ int err;
+ rb_call_super(argc,argv);
+ if (source==SYM(file)) {
+ filename = rb_funcall(mGridFlow,SI(find_file),1,filename);
+ FSSpec fss;
+ FSRef fsr;
+ err = FSPathMakeRef((const UInt8 *)rb_str_ptr(filename), &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;
+ } else {
+ RAISE("usage: quicktime [file <filename> | camera bleh]");
+ }
+ NewMovieFromFile(&movie, movie_file, NULL, NULL, newMovieActive, NULL);
+ Rect r;
+ GetMovieBox(movie, &r);
+ gfpost("handle=%d movie=%d tracks=%d",
+ movie_file, movie, GetMovieTrackCount(movie));
+ gfpost("duration=%d; timescale=%d cHz",
+ (long)GetMovieDuration(movie),
+ (long)GetMovieTimeScale(movie));
+ nframes = GetMovieDuration(movie); /* i don't think so */
+ gfpost("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 = ARRAY_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)", rb_str_ptr(filename),
+ err,
+ rb_str_ptr(rb_funcall(mGridFlow,SI(macerr),1,INT2NUM(err))));
+}
+
+\classinfo {
+ EnterMovies();
+IEVAL(rself,
+\ruby
+ install '#io:quicktime',1,1
+ @comment="Apple Quicktime (using Apple's)"
+ @flags=4
+ suffixes_are'mov'
+ def self.new(mode,source,filename)
+ if source==:camera then FormatQuickTimeCamera.new(mode,source,filename) else super end
+ end
+\end ruby
+);}
+\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..4ae77209
--- /dev/null
+++ b/externals/gridflow/format/quicktimehw.c
@@ -0,0 +1,244 @@
+/*
+ $Id: quicktimehw.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 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 "../base/grid.h.fcs"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <quicktime/quicktime.h>
+#include <quicktime/colormodels.h>
+
+#include <quicktime/lqt_version.h>
+#ifdef LQT_VERSION
+#include <quicktime/lqt.h>
+#include <quicktime/lqt_codecinfo.h>
+#endif
+
+\class FormatQuickTimeHW < Format
+struct 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;
+ FormatQuickTimeHW() : track(0), dim(0), codec(QUICKTIME_RAW),
+ started(false), force(0), framerate(29.97), bit_packing(0) {}
+ \decl void initialize (Symbol mode, Symbol source, String filename);
+ \decl void close ();
+ \decl Ruby frame ();
+ \decl void seek (int frame);
+
+ \decl void _0_force_size (int32 height, int32 width);
+ \decl void _0_codec (String c);
+ \decl void _0_colorspace (Symbol c);
+ \decl void _0_parameter (Symbol name, int32 value);
+ \decl void _0_framerate (float64 f);
+ \decl void _0_size (int32 height, int32 width);
+ \grin 0 int
+};
+
+\def void _0_force_size (int32 height, int32 width) { force = new Dim(height, width); }
+\def void seek (int frame) {quicktime_set_video_position(anim,frame,track);}
+
+\def Ruby frame () {
+ int nframe = quicktime_video_position(anim,track);
+ int length2 = quicktime_video_length(anim,track);
+ if (nframe >= length) {
+// gfpost("nframe=%d length=%d length2=%d",nframe,length,length2);
+ return Qfalse;
+ }
+ /* 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: gfpost("strange quicktime. ask matju."); break;
+ }
+ if (force) {
+ sy = force->get(0);
+ sx = force->get(1);
+ }
+ Pt<uint8> buf = ARRAY_NEW(uint8,sy*sx*channels);
+ uint8 *rows[sy]; for (int i=0; i<sy; i++) rows[i]=buf+i*sx*channels;
+ int result = quicktime_decode_scaled(anim,0,0,sx,sy,sx,sy,colorspace,rows,track);
+ GridOutlet out(this,0,new Dim(sy, sx, channels),
+ NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ int bs = out.dim->prod(1);
+ out.give(sy*sx*channels,buf);
+ started=true;
+ return INT2NUM(nframe);
+}
+
+//!@#$ should also support symbol values (how?)
+\def void _0_parameter (Symbol name, int32 value) {
+ quicktime_set_parameter(anim, (char*)rb_sym_name(name), &value);
+}
+
+\def void _0_framerate (float64 f) {
+ framerate=f;
+ quicktime_set_framerate(anim, f);
+}
+
+\def void _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_factor(in->dim->prod());
+ 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);
+ }
+} 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,Pt<uint8>(data2,n));
+ 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]=data+i*sx*channels;
+ quicktime_encode_video(anim,rows,track);
+ }
+} GRID_FINISH {
+} GRID_END
+
+\def void _0_codec (String c) {
+ //fprintf(stderr,"codec = %s\n",rb_str_ptr(rb_inspect(c)));
+#ifdef LQT_VERSION
+ char buf[5];
+ strncpy(buf,rb_str_ptr(c),4);
+ for (int i=rb_str_len(c); i<4; i++) buf[i]=' ';
+ buf[4]=0;
+ Ruby fourccs = rb_ivar_get(rb_obj_class(rself),SI(@fourccs));
+ if (Qnil==rb_hash_aref(fourccs,rb_str_new2(buf)))
+ RAISE("warning: unknown fourcc '%s' (%s)",
+ buf, rb_str_ptr(rb_inspect(rb_funcall(fourccs,SI(keys),0))));
+#endif
+ codec = strdup(buf);
+}
+
+\def void _0_colorspace (Symbol c) {
+ if (0) {
+ } else if (c==SYM(rgb)) { channels=3; colorspace=BC_RGB888;
+ } else if (c==SYM(rgba)) { channels=4; colorspace=BC_RGBA8888;
+ } else if (c==SYM(bgr)) { channels=3; colorspace=BC_BGR888;
+ } else if (c==SYM(bgrn)) { channels=4; colorspace=BC_BGR8888;
+ } else if (c==SYM(yuv)) { channels=3; colorspace=BC_YUV888;
+ } else if (c==SYM(yuva)) { channels=4; colorspace=BC_YUVA8888;
+ } else if (c==SYM(YUV420P)) { channels=3; colorspace=BC_YUV420P;
+ } else RAISE("unknown colorspace '%s' (supported: rgb, rgba, bgr, bgrn, yuv, yuva)",rb_sym_name(c));
+}
+
+\def void close () {
+ if (anim) { quicktime_close(anim); anim=0; }
+ rb_call_super(argc,argv);
+}
+
+// libquicktime may be nice, but it won't take a filehandle, only filename
+\def void initialize (Symbol mode, Symbol source, String filename) {
+ rb_call_super(argc,argv);
+ if (source!=SYM(file)) RAISE("usage: quicktime file <filename>");
+ filename = rb_funcall(mGridFlow,SI(find_file),1,filename);
+ anim = quicktime_open(rb_str_ptr(filename),mode==SYM(in),mode==SYM(out));
+ if (!anim) RAISE("can't open file `%s': %s", rb_str_ptr(filename), strerror(errno));
+ if (mode==SYM(in)) {
+ length = quicktime_video_length(anim,track);
+ gfpost("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,SYM(rgb));
+ quicktime_set_cpus(anim,1);
+ uint32 mask[3] = {0x0000ff,0x00ff00,0xff0000};
+ bit_packing = new BitPacking(is_le(),3,3,mask);
+}
+
+\classinfo {
+ IEVAL(rself,
+\ruby
+ install '#io:quicktime',1,1
+ @comment=%[Burkhard Plaum's (or HeroineWarrior's) libquicktime]
+ suffixes_are 'mov'
+ @flags=6
+ def self.info; %[codecs: #{@codecs.keys.join' '}] end
+\end ruby
+);
+
+#ifdef LQT_VERSION
+ lqt_registry_init();
+ int n = lqt_get_num_video_codecs();
+ Ruby codecs = rb_hash_new();
+ Ruby fourccs = rb_hash_new();
+ for (int i=0; i<n; i++) {
+ const lqt_codec_info_t *s = lqt_get_video_codec_info(i);
+ Ruby name = rb_str_new2(s->name);
+ Ruby f = rb_ary_new2(s->num_fourccs);
+ for (int j=0; j<s->num_fourccs; j++) {
+ Ruby fn = rb_str_new2(s->fourccs[j]);
+ rb_ary_push(f,fn);
+ rb_hash_aset(fourccs,fn,name);
+ }
+ rb_hash_aset(codecs,name,f);
+ }
+ rb_ivar_set(rself,SI(@codecs),codecs);
+ rb_ivar_set(rself,SI(@fourccs),fourccs);
+#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..628f71ee
--- /dev/null
+++ b/externals/gridflow/format/sdl.c
@@ -0,0 +1,123 @@
+/*
+ $Id: sdl.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 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 "../base/grid.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>
+
+static bool in_use = false;
+
+\class FormatSDL < Format
+struct FormatSDL : Format {
+ SDL_Surface *screen;
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ void resize_window (int sx, int sy);
+ void call ();
+ Pt<uint8> pixels () {
+ return Pt<uint8>((uint8 *)screen->pixels,
+ dim->prod(0,1)*bit_packing->bytes);
+ }
+ \decl void initialize (Symbol mode);
+ \decl void close ();
+ \grin 0 int
+};
+
+void FormatSDL::call() {
+ SDL_Event event;
+ while(SDL_PollEvent(&event)) {}
+ IEVAL(rself,"@clock.delay 20");
+}
+
+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 sxc = in->dim->prod(1);
+ int sx = in->dim->get(1), osx = dim->get(1);
+ int sy = in->dim->get(0), osy = dim->get(0);
+ in->set_factor(sxc);
+ 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;
+ assert((in->dex % sxc) == 0);
+ assert((n % sxc) == 0);
+ 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, 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 void close () {
+ IEVAL(rself,"@clock.unset");
+ in_use=false;
+}
+
+\def void initialize (Symbol mode) {
+ dim=0;screen=0;
+ rb_call_super(argc,argv);
+ 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;
+ }
+ IEVAL(rself,"@clock = Clock.new self");
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:sdl',1,1;@flags=2;@comment='Simple Directmedia Layer'");
+}
+\end class FormatSDL
+void startup_sdl () {
+ \startall
+}
diff --git a/externals/gridflow/format/videodev.c b/externals/gridflow/format/videodev.c
new file mode 100644
index 00000000..e907eaa9
--- /dev/null
+++ b/externals/gridflow/format/videodev.c
@@ -0,0 +1,544 @@
+/*
+ $Id: videodev.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 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 "../base/grid.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>
+
+/* **************************************************************** */
+
+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 ;
+
+static const char *ruby_code =
+\ruby
+flags :name,:VideoTypeFlags,:start,0,:values,%w(
+ CAPTURE TUNER TELETEXT OVERLAY CHROMAKEY CLIPPING FRAMERAM
+ SCALES MONOCHROME SUBCAPTURE
+ MPEG_DECODER MPEG_ENCODER MJPEG_DECODER MJPEG_ENCODER
+)
+flags :name,:TunerFlags,:start,0,:values,%w(
+ PAL NTSC SECAM LOW NORM DUMMY5 DUMMY6 STEREO_ON RDS_ON MBS_ON
+)
+flags :name,:ChannelFlags,:start,0,:values,%w(
+ TUNER AUDIO NORM
+)
+choice :name,:VideoPaletteChoice,:start,0,:values,%w(
+ NIL GREY HI240
+ RGB565 RGB24 RGB32 RGB555
+ YUV422 YUYV UYVY YUV420 YUV411 RAW
+ YUV422P YUV411P YUV420P YUV410P
+)
+choice :name,:VideoModeChoice,:start,0,:values,%w(
+ PAL NTSC SECAM AUTO
+)
+\end ruby
+;
+
+/* **************************************************************** */
+
+/*
+#define WH(_field_,_spec_) \
+ sprintf(buf+strlen(buf), "%s: " _spec_ "; ", #_field_, self->_field_);
+#define WHYX(_name_,_fieldy_,_fieldx_) \
+ sprintf(buf+strlen(buf), "%s: y=%d, x=%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_)); \
+ delete[] foo;}
+#define WHCHOICE(_field_,_table_) { \
+ char *foo; \
+ sprintf(buf+strlen(buf), "%s: %s; ", #_field_, \
+ foo=choice_to_s(self->_field_,COUNT(_table_),_table_));\
+ delete[] foo;}
+static char *flags_to_s(int value, int n, named_int *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].name);
+ }
+ if (!*foo) strcat(foo,"0");
+ return strdup(foo);
+}
+static char *choice_to_s(int value, int n, named_int *table) {
+ if (value < 0 || value >= n) {
+ char foo[64];
+ sprintf(foo,"(Unknown #%d)",value);
+ return strdup(foo);
+ } else {
+ return strdup(table[value].name);
+ }
+}
+*/
+
+class RStream {
+public:
+ Ruby a;
+ RStream(Ruby a) : a(a) {}
+ RStream &operator <<(/*Ruby*/ void *v) { rb_ary_push(a,(Ruby)v); return *this; }
+ RStream &operator <<(int v) { return *this<<(void *)INT2NUM(v); }
+};
+
+static void gfpost(VideoChannel *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoChannel) << self->channel
+ << (void *)rb_str_new(self->name,strnlen(self->name,32))
+ << self->tuners << self->flags << self->type << self->norm;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoTuner *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoTuner) << self->tuner
+ << (void *)rb_str_new(self->name,strnlen(self->name,32))
+ << self->rangelow << self->rangehigh
+ << self->flags << self->mode << self->signal;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoCapability *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoCapability)
+ << (void *)rb_str_new(self->name,strnlen(self->name,32))
+ << self->type
+ << self->channels << self->audios
+ << self->maxheight << self->maxwidth
+ << self->minheight << self->minwidth;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoWindow *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoWindow)
+ << self->y << self->x
+ << self->height << self->width
+ << self->chromakey << self->flags << self->clipcount;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoPicture *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoPicture)
+ << self->brightness << self->contrast << self->colour
+ << self->hue << self->whiteness << self->depth << self->palette;
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoMbuf *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoMBuf) << self->size << self->frames;
+ for (int i=0; i<4; i++) rs << self->offsets[i];
+ rb_p(rs.a);
+}
+
+static void gfpost(VideoMmap *self) {
+ RStream rs(rb_ary_new());
+ rs << (void *)SYM(VideoMMap) << self->frame
+ << self->height << self->width << self->format;
+ rb_p(rs.a);
+};
+
+/* **************************************************************** */
+
+\class FormatVideoDev < Format
+struct FormatVideoDev : Format {
+ VideoCapability vcaps;
+ VideoMbuf vmbuf;
+ VideoMmap vmmap;
+ Pt<uint8> image;
+ int palette;
+ int queue[8], queuesize, queuemax, next_frame;
+ int current_channel, current_tuner;
+ bool use_mmap;
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+
+ FormatVideoDev () : queuesize(0), queuemax(2), next_frame(0), use_mmap(true), bit_packing(0), dim(0) {}
+ void frame_finished (Pt<uint8> buf);
+
+ \decl void initialize (Symbol mode, String filename, Symbol option=Qnil);
+ \decl void initialize2 ();
+ \decl void close ();
+ \decl void alloc_image ();
+ \decl void dealloc_image ();
+ \decl void frame ();
+ \decl void frame_ask ();
+ \grin 0 int
+
+ \decl void _0_size (int sy, int sx);
+ \decl void _0_norm (int value);
+ \decl void _0_tuner (int value);
+ \decl void _0_channel (int value);
+ \decl void _0_frequency (int value);
+ \decl void _0_transfer (Symbol sym, int queuemax=2);
+ \decl void _0_colorspace (Symbol c);
+ \decl void _0_get (Symbol attr=0);
+ \decl void _0_brightness (uint16 value);
+ \decl void _0_hue (uint16 value);
+ \decl void _0_colour (uint16 value);
+ \decl void _0_contrast (uint16 value);
+ \decl void _0_whiteness (uint16 value);
+};
+
+#define DEBUG(args...) 42
+//#define DEBUG(args...) gfpost
+
+#define IOCTL(_f_,_name_,_arg_) \
+ (DEBUG("fd%d.ioctl(0x%08x(:%s),0x%08x)\n",_f_,_name_,#_name_,_arg_), \
+ ioctl(_f_,_name_,_arg_))
+
+#define WIOCTL(_f_,_name_,_arg_) \
+ (DEBUG("fd%d.ioctl(0x%08x(:%s),0x%08x)\n",_f_,_name_,#_name_,_arg_), \
+ ioctl(_f_,_name_,_arg_) < 0) && \
+ (gfpost("ioctl %s: %s",#_name_,strerror(errno)),1)
+
+#define WIOCTL2(_f_,_name_,_arg_) \
+ ((DEBUG("fd%d.ioctl(0x%08x(:%s),0x%08x)\n",_f_,_name_,#_name_,_arg_), \
+ ioctl(_f_,_name_,_arg_) < 0) && \
+ (gfpost("ioctl %s: %s",#_name_,strerror(errno)), \
+ RAISE("ioctl error"), 0))
+
+#define GETFD NUM2INT(rb_funcall(rb_ivar_get(rself,SI(@stream)),SI(fileno),0))
+
+\def void _0_size (int sy, int sx) {
+ int fd = GETFD;
+ VideoWindow grab_win;
+ // !@#$ bug here: won't flush the frame queue
+ dim = new Dim(sy,sx,3);
+ WIOCTL(fd, VIDIOCGWIN, &grab_win);
+ gfpost(&grab_win);
+ grab_win.clipcount = 0;
+ grab_win.flags = 0;
+ if (sy && sx) {
+ grab_win.height = sy;
+ grab_win.width = sx;
+ }
+ gfpost(&grab_win);
+ WIOCTL(fd, VIDIOCSWIN, &grab_win);
+ WIOCTL(fd, VIDIOCGWIN, &grab_win);
+ gfpost(&grab_win);
+}
+
+\def void dealloc_image () {
+ if (!image) return;
+ if (!use_mmap) {
+ delete[] (uint8 *)image;
+ } else {
+ munmap(image, vmbuf.size);
+ image = Pt<uint8>();
+ }
+}
+
+\def void alloc_image () {
+ if (!use_mmap) {
+ image = ARRAY_NEW(uint8,dim->prod(0,1)*bit_packing->bytes);
+ return;
+ }
+ int fd = GETFD;
+ WIOCTL2(fd, VIDIOCGMBUF, &vmbuf);
+ image = Pt<uint8>((uint8 *)
+ mmap(0,vmbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0),
+ vmbuf.size);
+ if (((int)image)<=0) {
+ image=Pt<uint8>();
+ RAISE("mmap: %s", strerror(errno));
+ }
+}
+
+\def void frame_ask () {
+ int fd = GETFD;
+ 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 = palette;
+ vmmap.width = dim->get(1);
+ vmmap.height = dim->get(0);
+ WIOCTL2(fd, VIDIOCMCAPTURE, &vmmap);
+ next_frame = (next_frame+1) % vmbuf.frames;
+}
+
+void FormatVideoDev::frame_finished (Pt<uint8> buf) {
+ GridOutlet out(this,0,dim,NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ /* picture is converted here. */
+ int sy = dim->get(0);
+ int sx = dim->get(1);
+ int bs = dim->prod(1);
+ if (palette==VIDEO_PALETTE_YUV420P) {
+ STACK_ARRAY(uint8,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> bufy = buf+sx*y;
+ Pt<uint8> bufu = buf+sx*sy +(sx/2)*(y/2);
+ Pt<uint8> bufv = buf+sx*sy*5/4+(sx/2)*(y/2);
+ for (int x=0; x<sx; x++) {
+ b2[x*3+0]=bufy[x];
+ b2[x*3+1]=bufu[x/2];
+ b2[x*3+2]=bufv[x/2];
+ }
+ out.send(bs,b2);
+ }
+ } else if (bit_packing) {
+ STACK_ARRAY(uint8,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> buf2 = buf+bit_packing->bytes*sx*y;
+ bit_packing->unpack(sx,buf2,b2);
+ out.send(bs,b2);
+ }
+ } else {
+ out.send(sy*bs,buf);
+ }
+}
+
+static int read2(int fd, uint8 *image, int n) {
+ int r=0;
+ for (; n>0; ) {
+ int rr=read(fd,image,n);
+ if (rr<0) return rr;
+ 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 void frame () {
+ if (!image) rb_funcall(rself,SI(alloc_image),0);
+ int fd = GETFD;
+ 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) rb_funcall(rself,SI(frame_ask),0);
+ 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];
+ rb_funcall(rself,SI(frame_ask),0);
+}
+
+GRID_INLET(FormatVideoDev,0) {
+ RAISE("can't write.");
+} GRID_FLOW {
+} GRID_FINISH {
+} GRID_END
+
+\def void _0_norm (int value) {
+ int fd = GETFD;
+ 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)) {
+ gfpost("no tuner #%d", value);
+ } else {
+ vtuner.mode = value;
+ gfpost(&vtuner);
+ WIOCTL(fd, VIDIOCSTUNER, &vtuner);
+ }
+}
+
+\def void _0_tuner (int value) {
+ int fd = GETFD;
+ 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);
+}
+
+\def void _0_channel (int value) {
+ int fd = GETFD;
+ VideoChannel vchan;
+ vchan.channel = value;
+ current_channel = value;
+ if (0> IOCTL(fd, VIDIOCGCHAN, &vchan)) RAISE("no channel #%d", value);
+ gfpost(&vchan);
+ WIOCTL(fd, VIDIOCSCHAN, &vchan);
+ if (vcaps.type & VID_TYPE_TUNER) rb_funcall(rself,SI(_0_tuner),1,INT2NUM(0));
+}
+
+\def void _0_frequency (int value) {
+ int fd = GETFD;
+ if (0> IOCTL(fd, VIDIOCSFREQ, &value)) RAISE("can't set frequency to %d",value);
+}
+
+\def void _0_transfer (Symbol sym, int queuemax=2) {
+ if (sym == SYM(read)) {
+ rb_funcall(rself,SI(dealloc_image),0);
+ use_mmap = false;
+ gfpost("transfer read");
+ } else if (sym == SYM(mmap)) {
+ rb_funcall(rself,SI(dealloc_image),0);
+ use_mmap = true;
+ rb_funcall(rself,SI(alloc_image),0);
+ queuemax=min(queuemax,vmbuf.frames);
+ gfpost("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_) {\
+ int fd = GETFD; \
+ VideoPicture vp; \
+ WIOCTL(fd, VIDIOCGPICT, &vp); \
+ vp._name_ = value; \
+ WIOCTL(fd, VIDIOCSPICT, &vp);}
+
+\def void _0_brightness (uint16 value) {PICTURE_ATTR(brightness)}
+\def void _0_hue (uint16 value) {PICTURE_ATTR(hue)}
+\def void _0_colour (uint16 value) {PICTURE_ATTR(colour)}
+\def void _0_contrast (uint16 value) {PICTURE_ATTR(contrast)}
+\def void _0_whiteness (uint16 value) {PICTURE_ATTR(whiteness)}
+
+#define PICTURE_ATTR_GET(_name_) { \
+ int fd = GETFD; \
+ VideoPicture vp; \
+ WIOCTL(fd, VIDIOCGPICT, &vp); \
+ Ruby argv[3] = {INT2NUM(1), SYM(_name_), INT2NUM(vp._name_)}; \
+ send_out(COUNT(argv),argv);}
+
+\def void _0_get (Symbol attr) {
+ if (!attr) {
+ _0_get(0,0,SYM(brightness));
+ _0_get(0,0,SYM(hue ));
+ _0_get(0,0,SYM(colour ));
+ _0_get(0,0,SYM(contrast ));
+ _0_get(0,0,SYM(whiteness ));
+ _0_get(0,0,SYM(frequency ));
+ } else if (attr==SYM(brightness)) { PICTURE_ATTR_GET(brightness);
+ } else if (attr==SYM(hue )) { PICTURE_ATTR_GET(hue );
+ } else if (attr==SYM(colour )) { PICTURE_ATTR_GET(colour );
+ } else if (attr==SYM(contrast )) { PICTURE_ATTR_GET(contrast );
+ } else if (attr==SYM(whiteness )) { PICTURE_ATTR_GET(whiteness );
+ } else if (attr==SYM(frequency )) {
+ int fd = GETFD;
+ int value;
+ WIOCTL(fd, VIDIOCGFREQ, &value);
+ {Ruby argv[3] ={INT2NUM(1), SYM(frequency), INT2NUM(value)}; send_out(COUNT(argv),argv);}
+ } else { RAISE("What you say?"); }
+}
+
+\def void close () {
+ if (image) rb_funcall(rself,SI(dealloc_image),0);
+ rb_call_super(argc,argv);
+}
+
+\def void _0_colorspace (Symbol c) {
+ if (c==SYM(RGB24)) palette=VIDEO_PALETTE_RGB24;
+ else if (c==SYM(YUV420P)) palette=VIDEO_PALETTE_YUV420P;
+ else RAISE("supported: RGB24, YUV420P");
+
+ int fd = GETFD;
+ VideoPicture *gp = new VideoPicture;
+ WIOCTL(fd, VIDIOCGPICT, gp);
+ gp->palette = palette;
+ WIOCTL(fd, VIDIOCSPICT, gp);
+ WIOCTL(fd, VIDIOCGPICT, gp);
+ //if (bit_packing) { delete bit_packing; bit_packing=0; }
+ switch(palette) {
+ case VIDEO_PALETTE_RGB24:{
+ uint32 masks[3] = { 0xff0000,0x00ff00,0x0000ff };
+ bit_packing = new BitPacking(is_le(),3,3,masks);
+ }break;
+ case VIDEO_PALETTE_YUV420P:{
+ // woops, special case already, can't do that with bit_packing
+ }
+ default:
+ RAISE("can't handle palette %d", gp->palette);
+ }
+ delete gp;
+}
+
+\def void initialize2 () {
+ int fd = GETFD;
+ VideoPicture *gp = new VideoPicture;
+/* long flags;
+ fcntl(fd,F_GETFL,&flags);
+ flags |= O_NONBLOCK;
+ fcntl(fd,F_SETFL,&flags); */
+
+ WIOCTL(fd, VIDIOCGCAP, &vcaps);
+ gfpost(&vcaps);
+ rb_funcall(rself,SI(_0_size),2,INT2NUM(vcaps.maxheight),INT2NUM(vcaps.maxwidth));
+ WIOCTL(fd, VIDIOCGPICT, gp);
+ gfpost(gp);
+ char buf[1024] = "";
+ int n = 17 /*COUNT(video_palette_choice)*/;
+ for (int i=0; i<n; i++) {
+ gp->palette = i;
+ ioctl(fd, VIDIOCSPICT, gp);
+ ioctl(fd, VIDIOCGPICT, gp);
+ if (gp->palette == i) {
+ if (*buf) strcpy(buf+strlen(buf),", ");
+ //strcpy(buf+strlen(buf),video_palette_choice[i].name);
+ sprintf(buf+strlen(buf),"%d",i);
+ }
+ }
+ gfpost("This card supports palettes: %s", buf);
+ _0_colorspace(0,0,SYM(RGB24));
+ rb_funcall(rself,SI(_0_channel),1,INT2NUM(0));
+ delete gp;
+}
+
+\def void initialize (Symbol mode, String filename, Symbol option=Qnil) {
+ rb_call_super(argc,argv);
+ image = Pt<uint8>();
+ rb_ivar_set(rself,SI(@stream),
+ rb_funcall(rb_cFile,SI(open),2,filename,rb_str_new2("r+")));
+ rb_funcall(rself,SI(initialize2),0);
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:videodev',1,1;@flags=4;@comment='Video4linux 1.x'");
+}
+\end class FormatVideoDev
+void startup_videodev () {
+ \startall
+}
diff --git a/externals/gridflow/format/x11.c b/externals/gridflow/format/x11.c
new file mode 100644
index 00000000..62041f06
--- /dev/null
+++ b/externals/gridflow/format/x11.c
@@ -0,0 +1,655 @@
+/*
+ $Id: x11.c,v 1.1 2005-10-04 02:02:15 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 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 "../base/grid.h.fcs"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/StringDefs.h>
+
+/* X11 Error Handler type */
+typedef int (*XEH)(Display *, XErrorEvent *);
+
+#ifdef HAVE_X11_SHARED_MEMORY
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+
+\class FormatX11 < Format
+struct FormatX11 : Format {
+ template <class T> void frame_by_type (T bogus);
+/* at the Display/Screen level */
+ Display *display; /* connection to xserver */
+ Visual *visual; /* screen properties */
+ Window root_window;
+ Colormap colormap; /* for 256-color mode */
+ long black,white; /* color numbers in default colormap */
+ short depth;
+ bool use_shm; /* should use shared memory? */
+ bool use_stripes; /* use alternate conversion in 256-color mode */
+
+/* at the Window level */
+ int autodraw; /* how much to send to the display at once */
+ 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 */
+ char *name; /* window name (for use by window manager) */
+ Pt<uint8> image; /* the real data (that XImage binds to) */
+ bool is_owner;
+ int pos_x, pos_y;
+
+ P<BitPacking> bit_packing;
+ P<Dim> dim;
+ bool lock_size;
+ bool override_redirect;
+
+#ifdef HAVE_X11_SHARED_MEMORY
+ XShmSegmentInfo *shm_info; /* to share memory with X11/Unix */
+#endif
+
+ Atom wmProtocolsAtom;
+ Atom wmDeleteAtom;
+
+ FormatX11 () : use_stripes(false),
+ autodraw(1), window(0), ximage(0), name(0), image(Pt<uint8>()), is_owner(true),
+ dim(0), lock_size(false), override_redirect(false)
+#ifdef HAVE_X11_SHARED_MEMORY
+ , shm_info(0)
+#endif
+ {}
+
+ void show_section(int x, int y, int sx, int sy);
+ void set_wm_hints (int sx, int sy);
+ 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 FormatX11::search_window_tree (Window xid, Atom key, const char *value, int level=0);
+
+ \decl void initialize (...);
+ \decl void frame ();
+ \decl void close ();
+ \decl void call ();
+ \decl void _0_out_size (int sy, int sx);
+ \decl void _0_draw ();
+ \decl void _0_autodraw (int autodraw);
+ \decl void _0_setcursor (int shape);
+ \decl void _0_hidecursor ();
+ \decl void _0_set_geometry (int y, int x, int sy, int sx);
+ \decl void _0_fall_thru (int flag);
+ \grin 0 int
+};
+
+/* ---------------------------------------------------------------- */
+
+void FormatX11::show_section(int x, int y, int sx, int sy) {
+ if (y>dim->get(0)||x>dim->get(1)) return;
+ if (y+sy>dim->get(0)) sy=dim->get(0)-y;
+ if (x+sx>dim->get(1)) sx=dim->get(1)-x;
+#ifdef HAVE_X11_SHARED_MEMORY
+ if (use_shm) {
+ XSync(display,False);
+ XShmPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy,False);
+ /* should completion events be waited for? looks like a bug */
+ } else
+#endif
+ 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 (int sx, int sy) {
+ if (!is_owner) return;
+ XWMHints wmhints;
+ XTextProperty window_name, icon_name;
+ XSizeHints hints;
+ // enable recommended, maximum and minimum size
+ hints.flags=PSize|PMaxSize|PMinSize;
+ // set those
+ hints.min_width = hints.max_width = hints.width = sx;
+ hints.min_height = hints.max_height = hints.height = sy;
+
+ wmhints.input = True;
+ wmhints.flags = InputHint;
+ XStringListToTextProperty((char **)&name, 1, &window_name);
+ XStringListToTextProperty((char **)&name, 1, &icon_name);
+ XSetWMProperties(display, window,
+ &window_name, &icon_name, NULL, 0, &hints, &wmhints, NULL);
+}
+
+void FormatX11::report_pointer(int y, int x, int state) {
+ Ruby argv[5] = {
+ INT2NUM(0), SYM(position),
+ INT2NUM(y), INT2NUM(x), INT2NUM(state) };
+ send_out(COUNT(argv),argv);
+}
+
+\def void 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 (rb_ivar_get(rself,SI(@mode)) == SYM(out)) {
+ 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);
+ Ruby argv[6] = {
+ INT2NUM(0), e.type==KeyPress ? SYM(keypress) : SYM(keyrelease),
+ INT2NUM(ek->y), INT2NUM(ek->x), INT2NUM(ek->state),
+ rb_funcall(rb_str_new2(buf),SI(intern),0) };
+ send_out(COUNT(argv),argv);
+ //XFree(kss);
+ }break;
+ case MotionNotify:{
+ XMotionEvent *em = (XMotionEvent *)&e;
+ report_pointer(em->y,em->x,em->state);
+ }break;
+ case DestroyNotify:{
+ gfpost("This window is being closed, so this handler will close too!");
+ rb_funcall(rself,SI(close),0);
+ return;
+ }break;
+ case ConfigureNotify:break; // as if we cared
+ case ClientMessage:{
+ // tnx to vektor&walken
+ /*
+ if (e.xclient.message_type==wmProtocolsAtom
+ && e.xclient.format==32
+ && (Atom)(e.xclient.data.l[0])==wmDeleteAtom) {
+ gfpost("This window is being closed, so this handler will close too!");
+ rb_funcall(rself,SI(close),0);
+ return;
+ }
+ */
+ }break;
+ }
+ }
+ IEVAL(rself,"@clock.delay 20");
+}
+
+\def void frame () {
+ XGetSubImage(display, window,
+ 0, 0, dim->get(1), dim->get(0),
+ (unsigned)-1, ZPixmap, ximage, 0, 0);
+ GridOutlet out(this,0,dim,NumberTypeE_find(rb_ivar_get(rself,SI(@cast))));
+ int sy=dim->get(0), sx=dim->get(1);
+ int bs=dim->prod(1);
+ STACK_ARRAY(uint8,b2,bs);
+ for(int y=0; y<sy; y++) {
+ Pt<uint8> b1 = Pt<uint8>(image,ximage->bytes_per_line*dim->get(0))
+ + 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) {
+ gfpost("X11 reports Error: display=0x%08x",(int)d);
+ gfpost("serial=0x%08x error=0x%08x request=0x%08lx minor=0x%08x",
+ xee->serial, xee->error_code, xee->request_code, xee->minor_code);
+ current_x11->use_shm = false;
+ return 42; /* it seems that the return value is ignored. */
+}
+
+void FormatX11::dealloc_image () {
+ if (!ximage) return;
+ if (use_shm) {
+ #ifdef HAVE_X11_SHARED_MEMORY
+ shmdt(ximage->data);
+ XShmDetach(display,shm_info);
+ if (shm_info) {delete shm_info; shm_info=0;}
+ //ximage->data = new char[1]; // bogus
+ //ximage->data = 0;
+ //XDestroyImage(ximage);
+ XFree(ximage);
+ ximage = 0;
+ image = Pt<uint8>();
+ #endif
+ } else {
+ //XDestroyImage(ximage);
+ XFree(ximage);
+ ximage = 0;
+ image = Pt<uint8>();
+ }
+}
+
+bool FormatX11::alloc_image (int sx, int sy) {
+ dim = new Dim(sy, sx, 3);
+ dealloc_image();
+ if (sx==0 || sy==0) return false;
+#ifdef HAVE_X11_SHARED_MEMORY
+ if (use_shm) {
+ shm_info = new XShmSegmentInfo;
+ ximage = XShmCreateImage(display,visual,depth,ZPixmap,0,shm_info,sx,sy);
+ if (!ximage) {
+ gfpost("shm got disabled (1), retrying...");
+ return alloc_image(sx,sy);}
+ shm_info->shmid = shmget(IPC_PRIVATE,
+ ximage->bytes_per_line*ximage->height, IPC_CREAT|0777);
+ if(shm_info->shmid < 0)
+ RAISE("ERROR: shmget failed: %s",strerror(errno));
+ ximage->data = shm_info->shmaddr =
+ (char *)shmat(shm_info->shmid,0,0);
+ image = Pt<uint8>((uint8 *)ximage->data,
+ ximage->bytes_per_line*sy);
+ shm_info->readOnly = False;
+ current_x11 = this;
+ //XSetErrorHandler(FormatX11_error_handler);
+ if (!XShmAttach(display, shm_info)) RAISE("ERROR: XShmAttach: big problem");
+ XSync(display,0); // make sure the server picks it up
+ //XSetErrorHandler(0);
+ /* yes, this can be done now. should cause auto-cleanup. */
+ shmctl(shm_info->shmid,IPC_RMID,0);
+ if (!use_shm) {
+ gfpost("shm got disabled (2), retrying...");
+ return alloc_image(sx,sy);}
+ } else
+#endif
+ ximage = XCreateImage(display,visual,depth,ZPixmap,0,0,sx,sy,8,0);
+ if (!ximage) RAISE("can't create image");
+ image = ARRAY_NEW(uint8,ximage->bytes_per_line*sy);
+ ximage->data = (int8 *)image;
+ int status = XInitImage(ximage);
+ if (status!=1) gfpost("XInitImage returned: %d", status);
+ return true;
+}
+
+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 (name) delete name;
+ name = new char[64];
+ sprintf(name,"GridFlow (%d,%d,3)",sy,sx);
+ if (window) {
+ if (is_owner && !lock_size) {
+ set_wm_hints(sx,sy);
+ XResizeWindow(display,window,sx,sy);
+ }
+ } else {
+ XSetWindowAttributes xswa;
+ xswa.do_not_propagate_mask = 0; //?
+ xswa.override_redirect = override_redirect; //#!@#$
+ window = XCreateWindow(display,
+ parent, pos_x, pos_y, sx, sy, 0,
+ CopyFromParent, InputOutput, CopyFromParent,
+ CWOverrideRedirect|CWDontPropagate, &xswa);
+ if(!window) RAISE("can't create window");
+ set_wm_hints(sx,sy);
+ if (is_owner) {
+ // fall_thru 0
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask | PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
+ KeyPressMask | KeyReleaseMask);
+ XMapRaised(display, window);
+ } else {
+ // fall_thru 1
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask);
+ }
+ 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 sxc = in->dim->prod(1);
+ int sx = in->dim->get(1), osx = dim->get(1);
+ int sy = in->dim->get(0), osy = dim->get(0);
+ in->set_factor(sxc);
+ 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;
+ int oy = y;
+ 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);
+ }
+ }
+ if (autodraw==2) show_section(0,oy,sx,y-oy);
+} GRID_FINISH {
+ if (autodraw==1) show_section(0,0,in->dim->get(1),in->dim->get(0));
+} GRID_END
+
+\def void close () {
+ if (!this) RAISE("stupid error: trying to close display NULL. =)");
+ bit_packing=0;
+ IEVAL(rself,"@clock.unset");
+ if (is_owner) XDestroyWindow(display,window);
+ XSync(display,0);
+ dealloc_image();
+ XCloseDisplay(display);
+ display=0;
+ rb_call_super(argc,argv);
+}
+
+\def void _0_out_size (int sy, int sx) { resize_window(sx,sy); }
+\def void _0_draw () { show_section(0,0,dim->get(1),dim->get(0)); }
+
+\def void _0_autodraw (int autodraw) {
+ if (autodraw<0 || autodraw>2)
+ RAISE("autodraw=%d is out of range",autodraw);
+ this->autodraw = autodraw;
+}
+
+\def void _0_setcursor (int shape) {
+ shape = 2*(shape&63);
+ Cursor c = XCreateFontCursor(display,shape);
+ XDefineCursor(display,window,c);
+ XFlush(display);
+}
+
+\def void _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) {
+ int screen_num;
+ Screen *screen;
+
+ // Open an X11 connection
+ 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 = DefaultScreenOfDisplay(display);
+ screen_num = DefaultScreen(display);
+ visual = DefaultVisual(display, screen_num);
+ white = XWhitePixel(display,screen_num);
+ black = XBlackPixel(display,screen_num);
+ root_window = DefaultRootWindow(display);
+ depth = DefaultDepthOfScreen(screen);
+ colormap = 0;
+
+ switch(visual->c_class) {
+ case TrueColor: case DirectColor: /* without colormap */
+ break;
+ case PseudoColor: /* with colormap */
+ 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);
+ }
+
+#ifdef HAVE_X11_SHARED_MEMORY
+ use_shm = !! XShmQueryExtension(display);
+#else
+ use_shm = 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 void _0_set_geometry (int y, int x, int sy, int sx) {
+ pos_x = x;
+ pos_y = y;
+ XMoveWindow(display,window,x,y);
+ resize_window(sx,sy);
+ XFlush(display);
+}
+
+\def void _0_fall_thru (int flag) {
+ if (flag) {
+ gfpost("falling through!");
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask);
+ } else {
+ gfpost("NOT falling through!");
+ XSelectInput(display, window,
+ ExposureMask | StructureNotifyMask |
+ PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
+ }
+ XFlush(display);
+}
+
+\def void initialize (...) {
+ int sy=240, sx=320; // defaults
+ rb_call_super(argc,argv);
+ argv++, argc--;
+ VALUE domain = argc<1 ? SYM(here) : argv[0];
+ int i;
+ if (domain==SYM(here)) {
+ open_display(0);
+ i=1;
+ } else if (domain==SYM(local)) {
+ if (argc<2) RAISE("open x11 local: not enough args");
+ char host[256];
+ int dispnum = NUM2INT(argv[1]);
+ sprintf(host,":%d",dispnum);
+ open_display(host);
+ i=2;
+ } else if (domain==SYM(remote)) {
+ if (argc<3) RAISE("open x11 remote: not enough args");
+ char host[256];
+ strcpy(host,rb_sym_name(argv[1]));
+ int dispnum = NUM2INT(argv[2]);
+ sprintf(host+strlen(host),":%d",dispnum);
+ open_display(host);
+ i=3;
+ } else if (domain==SYM(display)) {
+ if (argc<2) RAISE("open x11 display: not enough args");
+ char host[256];
+ strcpy(host,rb_sym_name(argv[1]));
+ for (int k=0; host[k]; k++) if (host[k]=='%') host[k]==':';
+ gfpost("mode `display', DISPLAY=`%s'",host);
+ open_display(host);
+ i=2;
+ } else {
+ RAISE("x11 destination syntax error");
+ }
+
+ for(;i<argc;i++) {
+ Ruby a=argv[i];
+ if (a==SYM(override_redirect)) {
+ override_redirect = true;
+ } else if (a==SYM(use_stripes)){
+ use_stripes = true;
+ } else {
+ break;
+ }
+ }
+
+ pos_x=pos_y=0;
+ parent = root_window;
+ if (i>=argc) {
+ } else {
+ VALUE winspec = argv[i];
+ if (winspec==SYM(root)) {
+ window = root_window;
+ is_owner = false;
+ } else if (winspec==SYM(embed)) {
+ Ruby title_s = rb_funcall(argv[i+1],SI(to_s),0);
+ char *title = strdup(rb_str_ptr(title_s));
+ sy = sx = pos_y = pos_x = 0;
+ parent = search_window_tree(root_window,XInternAtom(display,"WM_NAME",0),title);
+ free(title);
+ if (parent == 0xDeadBeef) RAISE("Window not found.");
+ } else if (winspec==SYM(embed_by_id)) {
+ const char *winspec2 = rb_sym_name(argv[i+1]);
+ if (strncmp(winspec2,"0x",2)==0) {
+ parent = strtol(winspec2+2,0,16);
+ } else {
+ parent = atoi(winspec2);
+ }
+ } else {
+ if (TYPE(winspec)==T_SYMBOL) {
+ const char *winspec2 = rb_sym_name(winspec);
+ 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_y = pos_x = 0;
+ }
+ }
+
+ // "resize" also takes care of creation
+ resize_window(sx,sy);
+
+ if (is_owner) {
+ wmProtocolsAtom = XInternAtom(display, "WM_PROTOCOLS", False);
+ 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;
+ }
+ IEVAL(rself,"@clock = Clock.new self; @clock.delay 0");
+}
+
+\classinfo {
+ IEVAL(rself,"install '#io:x11',1,1;@mode=6;@comment='X Window System Version 11.5'");
+}
+\end class FormatX11
+void startup_x11 () {
+ \startall
+}