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-(a)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=#{(a)dex.inspect}, prod=#{(a)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
#(a)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: #{(a)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
}