diff options
Diffstat (limited to 'externals/gridflow/format/main.rb')
-rw-r--r-- | externals/gridflow/format/main.rb | 807 |
1 files changed, 0 insertions, 807 deletions
diff --git a/externals/gridflow/format/main.rb b/externals/gridflow/format/main.rb deleted file mode 100644 index 1cac124f..00000000 --- a/externals/gridflow/format/main.rb +++ /dev/null @@ -1,807 +0,0 @@ -=begin - $Id: main.rb,v 1.2 2006-03-15 04:37:46 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(*a); _0_open(*a); _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 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 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 - @stream.close if @stream - 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 - self -}.subclass("#io:tk",1,1) { - install_rgrid 0 - def initialize(mode) - if mode!=:out then raise "only #out" end - super(mode,:file,"/tmp/tk-#{$$}-#{object_id}.ppm") - GridFlow.gui "toplevel .#{object_id}\n" - GridFlow.gui "wm title . GridFlow/Tk\n" - GridFlow.gui "image create photo #{object_id} -width 320 -height 240\n" - GridFlow.gui "pack [label .#{object_id}.im -image #{object_id}]\n" - end - def _0_rgrid_end - super - @stream.seek 0,IO::SEEK_SET - GridFlow.gui "image create photo #{object_id} -file /tmp/tk-#{$$}-#{object_id}.ppm\n" - end - def delete - GridFlow.gui "destroy .#{object_id}\n" - GridFlow.gui "image delete #{object_id}\n" - end - alias close delete -} - -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 |