rlm@11
|
1 class Graster
|
rlm@11
|
2 class Image
|
rlm@11
|
3 PROPS = [:filename,:size,:pixels]
|
rlm@11
|
4
|
rlm@11
|
5 def initialize(props)
|
rlm@11
|
6 PROPS.each do |p|
|
rlm@11
|
7 raise "required image property :#{p} missing" unless props[p]
|
rlm@11
|
8 instance_variable_set "@#{p}", props[p]
|
rlm@11
|
9 end
|
rlm@11
|
10 end
|
rlm@11
|
11
|
rlm@11
|
12 PROPS.each{|p| attr_reader p }
|
rlm@11
|
13
|
rlm@11
|
14 def self.from_file pathname
|
rlm@11
|
15 raise "file not found #{pathname}" unless File.exist? pathname
|
rlm@11
|
16 img = Magick::Image.read(pathname)
|
rlm@11
|
17 raise "bad image data in #{pathname}" unless img = img[0]
|
rlm@11
|
18 new :filename => File.basename(pathname),
|
rlm@11
|
19 :size => [img.columns,img.rows],
|
rlm@11
|
20 :pixels => img.export_pixels(0,0,img.columns,img.rows,"I")
|
rlm@11
|
21 end
|
rlm@11
|
22
|
rlm@11
|
23 # get pixel(s) from x,y coords
|
rlm@11
|
24 # 0,0 is bottom,left
|
rlm@11
|
25 # image[x,y] => pixel at x,y
|
rlm@11
|
26 # image[y] => row at y
|
rlm@11
|
27 def [] y, x=nil
|
rlm@11
|
28 if x
|
rlm@11
|
29 @pixels[(@size[1]-y)*@size[0]+x]
|
rlm@11
|
30 else
|
rlm@11
|
31 @pixels[(@size[1]-y)*@size[0],@size[0]]
|
rlm@11
|
32 end
|
rlm@11
|
33 end
|
rlm@11
|
34
|
rlm@11
|
35 def each_row &block
|
rlm@11
|
36 @pixels.chars.each_slice(@size[0]).each_with_index &block
|
rlm@11
|
37 end
|
rlm@11
|
38
|
rlm@11
|
39 # "encode" a float 0..1 to a pixel
|
rlm@11
|
40 def self.f_to_pix f
|
rlm@11
|
41 (f*65535).round
|
rlm@11
|
42 end
|
rlm@11
|
43
|
rlm@11
|
44 # "decode" an encoded pixel to a float 0..1
|
rlm@11
|
45 def self.pix_to_f pix
|
rlm@11
|
46 pix/65535.0
|
rlm@11
|
47 end
|
rlm@11
|
48
|
rlm@11
|
49
|
rlm@11
|
50 # convert bitmap data to spans (or runs) of contiguous pixels
|
rlm@11
|
51 # also invert the Y axis
|
rlm@11
|
52 def build_spans on_range
|
rlm@11
|
53 # TODO: rewrite in terms of each_row
|
rlm@11
|
54 @spans = Array.new @size[1]
|
rlm@11
|
55
|
rlm@11
|
56 @size[1].times do |y|
|
rlm@11
|
57 spans = []
|
rlm@11
|
58 left = (@size[1]-y-1)*@size[0]
|
rlm@11
|
59 start = nil
|
rlm@11
|
60
|
rlm@11
|
61 @size[0].times do |x|
|
rlm@11
|
62 d = on_range.include?(@pixels[left+x])
|
rlm@11
|
63
|
rlm@11
|
64 if !start && d
|
rlm@11
|
65 start = x
|
rlm@11
|
66 elsif start && !d
|
rlm@11
|
67 spans << [start, x]
|
rlm@11
|
68 start = nil
|
rlm@11
|
69 end
|
rlm@11
|
70 end
|
rlm@11
|
71
|
rlm@11
|
72 spans << [start, @size[0]] if start
|
rlm@11
|
73 @spans[y] = spans
|
rlm@11
|
74 end
|
rlm@11
|
75 end
|
rlm@11
|
76
|
rlm@11
|
77 attr_reader :spans
|
rlm@11
|
78
|
rlm@11
|
79 def hash
|
rlm@11
|
80 [@pixels,@width,@height].hash
|
rlm@11
|
81 end
|
rlm@11
|
82 end
|
rlm@11
|
83 end |