Update of /cvsroot/pure-data/externals/gridflow/format In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21117/format
Added Files: aalib.c dc1394.c jpeg.c main.rb mpeg3.c png.c quicktimeapple.c quicktimehw.c sdl.c videodev.c x11.c Log Message: 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
--- NEW FILE: main.rb --- =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
--- NEW FILE: quicktimehw.c --- /* $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 }
--- NEW FILE: x11.c --- /* $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 }
--- NEW FILE: png.c --- /* $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 }
--- NEW FILE: videodev.c --- /* $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 }
--- NEW FILE: mpeg3.c --- /* $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 }
--- NEW FILE: dc1394.c --- /* $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 }
--- NEW FILE: jpeg.c --- /* $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 }
--- NEW FILE: quicktimeapple.c --- /* $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 }
--- NEW FILE: sdl.c --- /* $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 }
--- NEW FILE: aalib.c --- /* $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 }