diff options
author | N.N. <matju@users.sourceforge.net> | 2005-10-04 02:02:15 +0000 |
---|---|---|
committer | N.N. <matju@users.sourceforge.net> | 2005-10-04 02:02:15 +0000 |
commit | 5e2a1bc9e56003349e533f7e5841041ba5c04e28 (patch) | |
tree | ad040f6894d9383b732423a74420e732f62a66a5 /externals/gridflow/format | |
parent | 520a243c297175386ab31c78c84693a664934a69 (diff) |
starting to commit gridflow 0.8.0 ...
if you know how to use "cvs import" please mail me and i'll use it for 0.8.1
svn path=/trunk/; revision=3646
Diffstat (limited to 'externals/gridflow/format')
-rw-r--r-- | externals/gridflow/format/aalib.c | 168 | ||||
-rw-r--r-- | externals/gridflow/format/dc1394.c | 346 | ||||
-rw-r--r-- | externals/gridflow/format/jpeg.c | 130 | ||||
-rw-r--r-- | externals/gridflow/format/main.rb | 788 | ||||
-rw-r--r-- | externals/gridflow/format/mpeg3.c | 98 | ||||
-rw-r--r-- | externals/gridflow/format/png.c | 138 | ||||
-rw-r--r-- | externals/gridflow/format/quicktimeapple.c | 500 | ||||
-rw-r--r-- | externals/gridflow/format/quicktimehw.c | 244 | ||||
-rw-r--r-- | externals/gridflow/format/sdl.c | 123 | ||||
-rw-r--r-- | externals/gridflow/format/videodev.c | 544 | ||||
-rw-r--r-- | externals/gridflow/format/x11.c | 655 |
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 +} |