zpng 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile CHANGED
@@ -8,8 +8,7 @@ gem 'hexdump'
8
8
  # Add dependencies to develop your gem here.
9
9
  # Include everything needed to run rake, tests, features, etc.
10
10
  group :development do
11
- gem "rspec", "~> 2.8.0"
12
- gem "bundler", "~> 1.0.0"
13
- gem "jeweler", "~> 1.6.4"
14
- gem "rcov", ">= 0"
11
+ gem "rspec", ">= 2.8.0"
12
+ gem "bundler", ">= 1.0.0"
13
+ gem "jeweler", "~> 1.8.4"
15
14
  end
@@ -4,29 +4,31 @@ GEM
4
4
  colorize (0.5.8)
5
5
  diff-lcs (1.1.3)
6
6
  git (1.2.5)
7
- hexdump (0.2.1)
8
- jeweler (1.6.4)
7
+ hexdump (0.2.3)
8
+ jeweler (1.8.4)
9
9
  bundler (~> 1.0)
10
10
  git (>= 1.2.5)
11
11
  rake
12
- rake (0.9.2.2)
13
- rcov (0.9.11)
14
- rspec (2.8.0)
15
- rspec-core (~> 2.8.0)
16
- rspec-expectations (~> 2.8.0)
17
- rspec-mocks (~> 2.8.0)
18
- rspec-core (2.8.0)
19
- rspec-expectations (2.8.0)
20
- diff-lcs (~> 1.1.2)
21
- rspec-mocks (2.8.0)
12
+ rdoc
13
+ json (1.7.5)
14
+ rake (10.0.2)
15
+ rdoc (3.12)
16
+ json (~> 1.4)
17
+ rspec (2.12.0)
18
+ rspec-core (~> 2.12.0)
19
+ rspec-expectations (~> 2.12.0)
20
+ rspec-mocks (~> 2.12.0)
21
+ rspec-core (2.12.1)
22
+ rspec-expectations (2.12.0)
23
+ diff-lcs (~> 1.1.3)
24
+ rspec-mocks (2.12.0)
22
25
 
23
26
  PLATFORMS
24
27
  ruby
25
28
 
26
29
  DEPENDENCIES
27
- bundler (~> 1.0.0)
30
+ bundler (>= 1.0.0)
28
31
  colorize
29
32
  hexdump
30
- jeweler (~> 1.6.4)
31
- rcov
32
- rspec (~> 2.8.0)
33
+ jeweler (~> 1.8.4)
34
+ rspec (>= 2.8.0)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- zpng
1
+ zpng [![Build Status](https://secure.travis-ci.org/zed-0xff/zpng.png)](http://secure.travis-ci.org/zed-0xff/zpng) [![Dependency Status](https://gemnasium.com/zed-0xff/zpng.png)](https://gemnasium.com/zed-0xff/zpng)
2
2
  ======
3
3
 
4
4
 
@@ -15,14 +15,18 @@ Usage
15
15
 
16
16
  # zpng -h
17
17
 
18
- Usage: zpng [options]
18
+ Usage: zpng [options] filename.png
19
19
  -v, --verbose Run verbosely (can be used multiple times)
20
20
  -q, --quiet Silent any warnings (can be used multiple times)
21
- -I, --info General image info
22
21
  -C, --chunks Show file chunks (default)
22
+ -i, --info General image info (default)
23
23
  -A, --ascii Try to display image as ASCII (works best with monochrome images)
24
24
  -S, --scanlines Show scanlines info
25
25
  -P, --palette Show palette
26
+ -E, --extract-chunk ID extract a single chunk
27
+ -U, --unpack-imagedata unpack Image Data (IDAT) chunk(s), output to stdout
28
+ -c, --crop GEOMETRY crop image, {WIDTH}x{HEIGHT}+{X}+{Y},
29
+ puts results on stdout unless --ascii given
26
30
 
27
31
  ### Info
28
32
 
@@ -35,15 +39,15 @@ Usage
35
39
 
36
40
  # zpng --chunks qr_aux_chunks.png
37
41
 
38
- [.] #<ZPNG::Chunk IHDR size= 13, crc=36a28ef4, width=35, height=35, depth=1, color=0, compression=0, filter=0, interlace=0> CRC OK
39
- [.] #<ZPNG::Chunk gAMA size= 4, crc=0bfc6105 > CRC OK
40
- [.] #<ZPNG::Chunk sRGB size= 1, crc=aece1ce9 > CRC OK
41
- [.] #<ZPNG::Chunk cHRM size= 32, crc=9cba513c > CRC OK
42
- [.] #<ZPNG::Chunk pHYs size= 9, crc=46c96b3e > CRC OK
43
- [.] #<ZPNG::Chunk IDAT size= 213, crc=5f3f1ff9 > CRC OK
44
- [.] #<ZPNG::Chunk tEXt size= 37, crc=8d62fd1a > CRC OK
45
- [.] #<ZPNG::Chunk tEXt size= 37, crc=fc3f45a6 > CRC OK
46
- [.] #<ZPNG::Chunk IEND size= 0, crc=ae426082 > CRC OK
42
+ [.] <Chunk #00 IHDR size= 13, crc=36a28ef4, width=35, height=35, depth=1, color=0, compression=0, filter=0, interlace=0, idx=0> CRC OK
43
+ [.] <Chunk #01 gAMA size= 4, crc=0bfc6105 > CRC OK
44
+ [.] <Chunk #02 sRGB size= 1, crc=aece1ce9 > CRC OK
45
+ [.] <Chunk #03 cHRM size= 32, crc=9cba513c > CRC OK
46
+ [.] <Chunk #04 pHYs size= 9, crc=46c96b3e > CRC OK
47
+ [.] <Chunk #05 IDAT size= 213, crc=5f3f1ff9 > CRC OK
48
+ [.] <Chunk #06 tEXt size= 37, crc=8d62fd1a > CRC OK
49
+ [.] <Chunk #07 tEXt size= 37, crc=fc3f45a6 > CRC OK
50
+ [.] <Chunk #08 IEND size= 0, crc=ae426082 > CRC OK
47
51
 
48
52
  ### ASCII
49
53
 
@@ -90,47 +94,47 @@ source image: ![qr_rgb.png](https://github.com/zed-0xff/zpng/raw/master/samples/
90
94
 
91
95
  # zpng --scanlines qr_rgb.png
92
96
 
93
- [#<ZPNG::ScanLine idx=0, bpp=24, BPP=3, offset=1, filter=1>,
94
- #<ZPNG::ScanLine idx=1, bpp=24, BPP=3, offset=107, filter=4>,
95
- #<ZPNG::ScanLine idx=2, bpp=24, BPP=3, offset=213, filter=4>,
96
- #<ZPNG::ScanLine idx=3, bpp=24, BPP=3, offset=319, filter=4>,
97
- #<ZPNG::ScanLine idx=4, bpp=24, BPP=3, offset=425, filter=2>,
98
- #<ZPNG::ScanLine idx=5, bpp=24, BPP=3, offset=531, filter=2>,
99
- #<ZPNG::ScanLine idx=6, bpp=24, BPP=3, offset=637, filter=4>,
100
- #<ZPNG::ScanLine idx=7, bpp=24, BPP=3, offset=743, filter=0>,
101
- #<ZPNG::ScanLine idx=8, bpp=24, BPP=3, offset=849, filter=1>,
102
- #<ZPNG::ScanLine idx=9, bpp=24, BPP=3, offset=955, filter=0>,
103
- #<ZPNG::ScanLine idx=10, bpp=24, BPP=3, offset=1061, filter=0>,
104
- #<ZPNG::ScanLine idx=11, bpp=24, BPP=3, offset=1167, filter=0>,
105
- #<ZPNG::ScanLine idx=12, bpp=24, BPP=3, offset=1273, filter=1>,
106
- #<ZPNG::ScanLine idx=13, bpp=24, BPP=3, offset=1379, filter=2>,
107
- #<ZPNG::ScanLine idx=14, bpp=24, BPP=3, offset=1485, filter=4>,
108
- #<ZPNG::ScanLine idx=15, bpp=24, BPP=3, offset=1591, filter=0>,
109
- #<ZPNG::ScanLine idx=16, bpp=24, BPP=3, offset=1697, filter=4>,
110
- #<ZPNG::ScanLine idx=17, bpp=24, BPP=3, offset=1803, filter=0>,
111
- #<ZPNG::ScanLine idx=18, bpp=24, BPP=3, offset=1909, filter=4>,
112
- #<ZPNG::ScanLine idx=19, bpp=24, BPP=3, offset=2015, filter=4>,
113
- #<ZPNG::ScanLine idx=20, bpp=24, BPP=3, offset=2121, filter=0>,
114
- #<ZPNG::ScanLine idx=21, bpp=24, BPP=3, offset=2227, filter=1>,
115
- #<ZPNG::ScanLine idx=22, bpp=24, BPP=3, offset=2333, filter=2>,
116
- #<ZPNG::ScanLine idx=23, bpp=24, BPP=3, offset=2439, filter=0>,
117
- #<ZPNG::ScanLine idx=24, bpp=24, BPP=3, offset=2545, filter=2>,
118
- #<ZPNG::ScanLine idx=25, bpp=24, BPP=3, offset=2651, filter=1>,
119
- #<ZPNG::ScanLine idx=26, bpp=24, BPP=3, offset=2757, filter=1>,
120
- #<ZPNG::ScanLine idx=27, bpp=24, BPP=3, offset=2863, filter=4>,
121
- #<ZPNG::ScanLine idx=28, bpp=24, BPP=3, offset=2969, filter=4>,
122
- #<ZPNG::ScanLine idx=29, bpp=24, BPP=3, offset=3075, filter=4>,
123
- #<ZPNG::ScanLine idx=30, bpp=24, BPP=3, offset=3181, filter=4>,
124
- #<ZPNG::ScanLine idx=31, bpp=24, BPP=3, offset=3287, filter=2>,
125
- #<ZPNG::ScanLine idx=32, bpp=24, BPP=3, offset=3393, filter=4>,
126
- #<ZPNG::ScanLine idx=33, bpp=24, BPP=3, offset=3499, filter=4>,
127
- #<ZPNG::ScanLine idx=34, bpp=24, BPP=3, offset=3605, filter=1>]
97
+ [#<ZPNG::ScanLine idx=0, bpp=24, offset=1, filter=1>,
98
+ #<ZPNG::ScanLine idx=1, bpp=24, offset=107, filter=4>,
99
+ #<ZPNG::ScanLine idx=2, bpp=24, offset=213, filter=4>,
100
+ #<ZPNG::ScanLine idx=3, bpp=24, offset=319, filter=4>,
101
+ #<ZPNG::ScanLine idx=4, bpp=24, offset=425, filter=2>,
102
+ #<ZPNG::ScanLine idx=5, bpp=24, offset=531, filter=2>,
103
+ #<ZPNG::ScanLine idx=6, bpp=24, offset=637, filter=4>,
104
+ #<ZPNG::ScanLine idx=7, bpp=24, offset=743, filter=0>,
105
+ #<ZPNG::ScanLine idx=8, bpp=24, offset=849, filter=1>,
106
+ #<ZPNG::ScanLine idx=9, bpp=24, offset=955, filter=0>,
107
+ #<ZPNG::ScanLine idx=10, bpp=24, offset=1061, filter=0>,
108
+ #<ZPNG::ScanLine idx=11, bpp=24, offset=1167, filter=0>,
109
+ #<ZPNG::ScanLine idx=12, bpp=24, offset=1273, filter=1>,
110
+ #<ZPNG::ScanLine idx=13, bpp=24, offset=1379, filter=2>,
111
+ #<ZPNG::ScanLine idx=14, bpp=24, offset=1485, filter=4>,
112
+ #<ZPNG::ScanLine idx=15, bpp=24, offset=1591, filter=0>,
113
+ #<ZPNG::ScanLine idx=16, bpp=24, offset=1697, filter=4>,
114
+ #<ZPNG::ScanLine idx=17, bpp=24, offset=1803, filter=0>,
115
+ #<ZPNG::ScanLine idx=18, bpp=24, offset=1909, filter=4>,
116
+ #<ZPNG::ScanLine idx=19, bpp=24, offset=2015, filter=4>,
117
+ #<ZPNG::ScanLine idx=20, bpp=24, offset=2121, filter=0>,
118
+ #<ZPNG::ScanLine idx=21, bpp=24, offset=2227, filter=1>,
119
+ #<ZPNG::ScanLine idx=22, bpp=24, offset=2333, filter=2>,
120
+ #<ZPNG::ScanLine idx=23, bpp=24, offset=2439, filter=0>,
121
+ #<ZPNG::ScanLine idx=24, bpp=24, offset=2545, filter=2>,
122
+ #<ZPNG::ScanLine idx=25, bpp=24, offset=2651, filter=1>,
123
+ #<ZPNG::ScanLine idx=26, bpp=24, offset=2757, filter=1>,
124
+ #<ZPNG::ScanLine idx=27, bpp=24, offset=2863, filter=4>,
125
+ #<ZPNG::ScanLine idx=28, bpp=24, offset=2969, filter=4>,
126
+ #<ZPNG::ScanLine idx=29, bpp=24, offset=3075, filter=4>,
127
+ #<ZPNG::ScanLine idx=30, bpp=24, offset=3181, filter=4>,
128
+ #<ZPNG::ScanLine idx=31, bpp=24, offset=3287, filter=2>,
129
+ #<ZPNG::ScanLine idx=32, bpp=24, offset=3393, filter=4>,
130
+ #<ZPNG::ScanLine idx=33, bpp=24, offset=3499, filter=4>,
131
+ #<ZPNG::ScanLine idx=34, bpp=24, offset=3605, filter=1>]
128
132
 
129
133
  ### Palette
130
134
 
131
135
  # zpng --palette qr_plte_bw.png
132
136
 
133
- #<ZPNG::Chunk PLTE size= 6, crc=55c2d37e >
137
+ <Chunk #02 PLTE size= 6, crc=55c2d37e >
134
138
  00000000 ff ff ff 00 00 00 |......|
135
139
 
136
140
 
@@ -157,6 +161,16 @@ source image: ![qr_rgb.png](https://github.com/zed-0xff/zpng/raw/master/samples/
157
161
  f << img.export
158
162
  end
159
163
 
164
+ ## Create 16x16 transparent PNG
165
+
166
+ #!/usr/bin/env ruby
167
+ require 'zpng'
168
+ include ZPNG
169
+
170
+ img = Image.new :width => 16, :height => 16
171
+ File.open("16x16.png","wb") do |f|
172
+ f << img.export
173
+ end
160
174
 
161
175
  License
162
176
  -------
@@ -1,4 +1,4 @@
1
- zpng
1
+ zpng [![Build Status](https://secure.travis-ci.org/zed-0xff/zpng.png)](http://secure.travis-ci.org/zed-0xff/zpng) [![Dependency Status](https://gemnasium.com/zed-0xff/zpng.png)](https://gemnasium.com/zed-0xff/zpng)
2
2
  ======
3
3
 
4
4
 
@@ -61,6 +61,16 @@ source image: ![qr_rgb.png](https://github.com/zed-0xff/zpng/raw/master/samples/
61
61
  f << img.export
62
62
  end
63
63
 
64
+ ## Create 16x16 transparent PNG
65
+
66
+ #!/usr/bin/env ruby
67
+ require 'zpng'
68
+ include ZPNG
69
+
70
+ img = Image.new :width => 16, :height => 16
71
+ File.open("16x16.png","wb") do |f|
72
+ f << img.export
73
+ end
64
74
 
65
75
  License
66
76
  -------
data/Rakefile CHANGED
@@ -72,3 +72,46 @@ task :readme do
72
72
  Dir.chdir '..'
73
73
  File.open('README.md','w'){ |f| f << result }
74
74
  end
75
+
76
+ desc "generate"
77
+ task :gen do
78
+ $:.unshift("./lib")
79
+ require 'zpng'
80
+ img = ZPNG::Image.new :width => 16, :height => 16, :bpp => 4
81
+ img.save "out.png"
82
+ end
83
+
84
+ Rake::Task[:console].clear
85
+
86
+ # from /usr/local/lib64/ruby/gems/1.9.1/gems/jeweler-1.8.4/lib/jeweler/tasks.rb
87
+ desc "Start IRB with all runtime dependencies loaded"
88
+ task :console, [:script] do |t,args|
89
+ # TODO move to a command
90
+ dirs = ['ext', 'lib'].select { |dir| File.directory?(dir) }
91
+
92
+ original_load_path = $LOAD_PATH
93
+
94
+ cmd = if File.exist?('Gemfile')
95
+ require 'bundler'
96
+ Bundler.setup(:default)
97
+ end
98
+
99
+ # add the project code directories
100
+ $LOAD_PATH.unshift(*dirs)
101
+
102
+ # clear ARGV so IRB is not confused
103
+ ARGV.clear
104
+
105
+ require 'irb'
106
+
107
+ # ZZZ actually added only these 2 lines
108
+ require 'zpng'
109
+ include ZPNG
110
+
111
+ # set the optional script to run
112
+ IRB.conf[:SCRIPT] = args.script
113
+ IRB.start
114
+
115
+ # return the $LOAD_PATH to it's original state
116
+ $LOAD_PATH.reject! { |path| !(original_load_path.include?(path)) }
117
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.0
@@ -18,25 +18,42 @@ module ZPNG
18
18
  end
19
19
  end
20
20
 
21
- def initialize io
22
- @size, @type = io.read(8).unpack('Na4')
23
- @data = io.read(size)
24
- @crc = io.read(4).to_s.unpack('N').first
21
+ def initialize x = {}
22
+ if x.respond_to?(:read)
23
+ # IO
24
+ @size, @type = x.read(8).unpack('Na4')
25
+ @data = x.read(size)
26
+ @crc = x.read(4).to_s.unpack('N').first
27
+ elsif x.respond_to?(:[])
28
+ # Hash
29
+ %w'size type data crc'.each do |k|
30
+ instance_variable_set "@#{k}", x[k.to_sym]
31
+ end
32
+ if !@type && self.class.superclass == ZPNG::Chunk
33
+ # guess @type from self class name, e.g. ZPNG::Chunk::IHDR => "IHDR"
34
+ @type = self.class.to_s.split("::").last
35
+ end
36
+ if !@size && @data
37
+ # guess @size from @data
38
+ @size = @data.size
39
+ end
40
+ end
25
41
  end
26
42
 
27
43
  def export
28
44
  @data = self.export_data # virtual
45
+ @size = @data.size # XXX hmm.. is it always is?
29
46
  @crc = Zlib.crc32(data, Zlib.crc32(type))
30
47
  [@size,@type].pack('Na4') + @data + [@crc].pack('N')
31
48
  end
32
49
 
33
50
  def export_data
34
51
  #STDERR.puts "[!] Chunk::#{type} must realize 'export_data' virtual method".yellow if @size != 0
35
- @data
52
+ @data || ''
36
53
  end
37
54
 
38
55
  def inspect
39
- size = @size ? sprintf("%5d",@size) : sprintf("%5s","???")
56
+ size = @size ? sprintf("%6d",@size) : sprintf("%6s","???")
40
57
  crc = @crc ? sprintf("%08x",@crc) : sprintf("%8s","???")
41
58
  type = @type.to_s.gsub(/[^0-9a-z]/i){ |x| sprintf("\\x%02X",x.ord) }
42
59
  sprintf "<Chunk #%02d %4s size=%s, crc=%s >", idx.to_i, type, size, crc
@@ -54,11 +71,16 @@ module ZPNG
54
71
  COLOR_USED = 2
55
72
  ALPHA_USED = 4
56
73
 
57
- COLOR_GRAYSCALE = 0 # Each pixel is a grayscale sample
58
- COLOR_RGB = 2 # Each pixel is an R,G,B triple.
59
- COLOR_INDEXED = 3 # Each pixel is a palette index; a PLTE chunk must appear.
60
- COLOR_GRAY_ALPHA = 4 # Each pixel is a grayscale sample, followed by an alpha sample.
61
- COLOR_RGBA = 6 # Each pixel is an R,G,B triple, followed by an alpha sample.
74
+ # put constants in the scope of ZPNG module
75
+ # to be able to create new images easily with
76
+ # include ZPNG
77
+ # img = Image.new :width => 16, :height => 16, :color => COLOR_RGB
78
+
79
+ ZPNG::COLOR_GRAYSCALE = 0 # Each pixel is a grayscale sample
80
+ ZPNG::COLOR_RGB = 2 # Each pixel is an R,G,B triple.
81
+ ZPNG::COLOR_INDEXED = 3 # Each pixel is a palette index; a PLTE chunk must appear.
82
+ ZPNG::COLOR_GRAY_ALPHA = 4 # Each pixel is a grayscale sample, followed by an alpha sample.
83
+ ZPNG::COLOR_RGBA = 6 # Each pixel is an R,G,B triple, followed by an alpha sample.
62
84
 
63
85
  SAMPLES_PER_COLOR = {
64
86
  COLOR_GRAYSCALE => 1,
@@ -68,11 +90,67 @@ module ZPNG
68
90
  COLOR_RGBA => 4
69
91
  }
70
92
 
93
+ # http://www.w3.org/TR/PNG/#table111
94
+ ALLOWED_DEPTHS = {
95
+ COLOR_GRAYSCALE => [ 1, 2, 4, 8, 16 ],
96
+ COLOR_RGB => [ 8, 16 ],
97
+ COLOR_INDEXED => [ 1, 2, 4, 8 ],
98
+ COLOR_GRAY_ALPHA => [ 8, 16 ],
99
+ COLOR_RGBA => [ 8, 16 ],
100
+ }
101
+
71
102
  FORMAT = 'NNC5'
72
103
 
73
- def initialize io
104
+ def initialize x
74
105
  super
75
- @width, @height, @depth, @color, @compression, @filter, @interlace = data.unpack(FORMAT)
106
+ vars = %w'width height depth color compression filter interlace' # order is important
107
+ if x.respond_to?(:read)
108
+ # IO
109
+ elsif x.respond_to?(:[])
110
+ # Hash
111
+ vars.each{ |k| instance_variable_set "@#{k}", x[k.to_sym] }
112
+
113
+ raise "[!] width not set" unless @width
114
+ raise "[!] height not set" unless @height
115
+
116
+ # allow easier image creation like
117
+ # img = Image.new :width => 16, :height => 16, :bpp => 4, :color => false
118
+ # img = Image.new :width => 16, :height => 16, :bpp => 1, :color => true
119
+ # img = Image.new :width => 16, :height => 16, :bpp => 32
120
+ if x[:bpp]
121
+ unless [true,false,nil].include?(@color)
122
+ raise "[!] :color must be either 'true' or 'false' when :bpp is set"
123
+ end
124
+ if @depth
125
+ raise "[!] don't use :depth when :bpp is set"
126
+ end
127
+ @color, @depth = case x[:bpp]
128
+ when 1,2,4,8; [ @color ? COLOR_INDEXED : COLOR_GRAYSCALE, x[:bpp] ]
129
+ when 16;
130
+ raise "[!] I don't know how to make COLOR 16 bpp PNG. do you?" if @color
131
+ [ COLOR_GRAY_ALPHA, 8 ]
132
+ when 24; [ COLOR_RGB, 8 ]
133
+ when 32; [ COLOR_RGBA, 8 ]
134
+ else
135
+ raise "[!] unsupported bpp=#{x[:bpp].inspect}"
136
+ end
137
+ end
138
+
139
+ @color ||= COLOR_RGBA
140
+ @depth ||= 8
141
+ @compression ||= 0
142
+ @filter ||= 0
143
+ @interlace ||= 0
144
+
145
+ unless ALLOWED_DEPTHS[@color].include?(@depth)
146
+ raise "[!] invalid color mode (#{@color.inspect}) / bit depth (#{@depth.inspect}) combination"
147
+ end
148
+ end
149
+ if data
150
+ data.unpack(FORMAT).each_with_index do |value,idx|
151
+ instance_variable_set "@#{vars[idx]}", value
152
+ end
153
+ end
76
154
  end
77
155
 
78
156
  def export_data
@@ -109,13 +187,20 @@ module ZPNG
109
187
  end
110
188
 
111
189
  class PLTE < Chunk
190
+ attr_accessor :max_colors
191
+
112
192
  def [] idx
113
193
  rgb = @data[idx*3,3]
114
- rgb && ZPNG::Color.new(*rgb.split('').map(&:ord))
194
+ rgb && ZPNG::Color.new(*rgb.unpack('C3'))
195
+ end
196
+
197
+ def []= idx, color
198
+ @data ||= ''
199
+ @data[idx*3,3] = [color.r, color.g, color.b].pack('C3')
115
200
  end
116
201
 
117
202
  def ncolors
118
- @size/3
203
+ @data.to_s.size/3
119
204
  end
120
205
 
121
206
  def index color
@@ -125,11 +210,23 @@ module ZPNG
125
210
  end
126
211
  nil
127
212
  end
128
- end
129
213
 
130
- class IEND < Chunk
214
+ def add color
215
+ raise "palette full, cannot add color #{ncolors}" if ncolors >= max_colors
216
+ idx = ncolors
217
+ self[idx] = color
218
+ idx
219
+ end
220
+
221
+ def find_or_add color
222
+ index(color) || add(color)
223
+ end
224
+ alias :<< :find_or_add
131
225
  end
132
226
 
227
+ class IDAT < Chunk; end
228
+ class IEND < Chunk; end
229
+
133
230
  class ZTXT < Chunk
134
231
  attr_accessor :keyword, :comp_method, :text
135
232
  def initialize *args