chunky_png 0.9.2 → 0.10.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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chunky_png (0.9.2)
4
+ chunky_png (0.10.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -16,9 +16,11 @@ def image_data(name)
16
16
  data
17
17
  end
18
18
 
19
- png_stream = image_data('pixelstream_reference.png')
20
- rgba_pixelstream = image_data('pixelstream.rgba')
21
- rgb_pixelstream = image_data('pixelstream.rgb')
19
+ no_filtering_stream = image_data('pixelstream_fast_rgba.png')
20
+ up_filtering_stream = image_data('pixelstream_reference.png')
21
+ paeth_filtering_stream = image_data('pixelstream_best_compression.png')
22
+ rgba_pixelstream = image_data('pixelstream.rgba')
23
+ rgb_pixelstream = image_data('pixelstream.rgb')
22
24
 
23
25
  n = (ENV['N'] || '5').to_i
24
26
 
@@ -28,7 +30,9 @@ puts "---------------------------------------------"
28
30
  puts
29
31
 
30
32
  Benchmark.bmbm do |x|
31
- x.report('From PNG stream') { n.times { ChunkyPNG::Image.from_blob(png_stream) } }
33
+ x.report('PNG - no filtering') { n.times { ChunkyPNG::Image.from_blob(no_filtering_stream) } }
34
+ x.report('PNG - UP filtering') { n.times { ChunkyPNG::Image.from_blob(up_filtering_stream) } }
35
+ x.report('PNG - PAETH filtering') { n.times { ChunkyPNG::Image.from_blob(paeth_filtering_stream) } }
32
36
  x.report('From RGBA pixelstream') { n.times { ChunkyPNG::Image.from_rgba_stream(240, 180, rgba_pixelstream) } }
33
37
  x.report('From RGB pixelstream') { n.times { ChunkyPNG::Image.from_rgb_stream(240, 180, rgb_pixelstream) } }
34
38
  end
@@ -21,11 +21,13 @@ puts "---------------------------------------------"
21
21
  puts
22
22
 
23
23
  Benchmark.bmbm do |x|
24
- x.report('Default (indexed)') { n.times { image.to_blob } }
24
+ x.report('Autodetect (indexed)') { n.times { image.to_blob } }
25
25
 
26
26
  # Presets
27
+ x.report(':no_compression') { n.times { image.to_blob(:no_compression) } }
27
28
  x.report(':fast_rgba') { n.times { image.to_blob(:fast_rgba) } }
28
29
  x.report(':fast_rgb') { n.times { image.to_blob(:fast_rgb) } }
30
+ x.report(':good_compression') { n.times { image.to_blob(:good_compression) } }
29
31
  x.report(':best_compression') { n.times { image.to_blob(:best_compression) } }
30
32
 
31
33
  # Some options
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.setup
5
+
6
+ require 'benchmark'
7
+ require 'chunky_png'
8
+
9
+ files = ['pixelstream_reference.png', 'operations.png', 'gray_10x10.png', 'clock_stubbed.png']
10
+
11
+ def encode_png(image, constraints = {})
12
+ filesize = nil
13
+ time = Benchmark.realtime { filesize = image.to_blob(constraints).bytesize }
14
+ [filesize, time]
15
+ end
16
+
17
+ files.each do |file|
18
+ filename = File.join(File.dirname(__FILE__), '..', 'spec', 'resources', file)
19
+ image = ChunkyPNG::Canvas.from_file(filename)
20
+
21
+ puts "#{file}: #{image.width}x#{image.height} - #{image.palette.size} colors"
22
+ puts "------------------------------------------------"
23
+ puts "<default> : %8d bytes in %0.4fs" % encode_png(image)
24
+ puts ":no_compression : %8d bytes in %0.4fs" % encode_png(image, :no_compression)
25
+ puts ":fast_rgba : %8d bytes in %0.4fs" % encode_png(image, :fast_rgba)
26
+ puts ":fast_rgb : %8d bytes in %0.4fs" % encode_png(image, :fast_rgb)
27
+ puts ":good_compression : %8d bytes in %0.4fs" % encode_png(image, :good_compression)
28
+ puts ":best_compression : %8d bytes in %0.4fs" % encode_png(image, :best_compression)
29
+ puts
30
+ end
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
 
4
4
  # Do not change the version and date fields by hand. This will be done
5
5
  # automatically by the gem release script.
6
- s.version = "0.9.2"
7
- s.date = "2010-09-17"
6
+ s.version = "0.10.0"
7
+ s.date = "2010-10-04"
8
8
 
9
9
  s.summary = "Pure ruby library for read/write, chunk-level access to PNG files"
10
10
  s.description = <<-EOT
@@ -34,6 +34,6 @@ Gem::Specification.new do |s|
34
34
 
35
35
  # Do not change the files and test_files fields by hand. This will be done
36
36
  # automatically by the gem release script.
37
- s.files = %w(tasks/github-gem.rake lib/chunky_png/canvas.rb spec/resources/composited.png lib/chunky_png/canvas/drawing.rb spec/resources/gray_10x10_grayscale_alpha.png spec/resources/cropped.png LICENSE spec/resources/clock_mask_updated.png spec/resources/pixelstream.rgba Gemfile spec/resources/clock_rotate_right.png spec/resources/ztxt_chunk.png spec/resources/transparent_gray_10x10.png lib/chunky_png/canvas/png_decoding.rb lib/chunky_png/image.rb spec/resources/lines.png spec/resources/clock_mask.png spec/resources/clock_stubbed.png spec/resources/pixelstream.rgb tasks/benchmarks.rake spec/resources/indexed_4bit.png spec/resources/text_chunk.png chunky_png.gemspec benchmarks/decoding_benchmark.rb spec/resources/gray_10x10_truecolor_alpha.png lib/chunky_png/color.rb spec/resources/damaged_chunk.png spec/resources/rect.png README.rdoc lib/chunky_png/canvas/stream_exporting.rb spec/resources/clock_flip_horizontally.png lib/chunky_png/canvas/stream_importing.rb spec/chunky_png/canvas/adam7_interlacing_spec.rb spec/spec_helper.rb lib/chunky_png/rmagick.rb Gemfile.lock Rakefile spec/chunky_png/canvas/png_decoding_spec.rb lib/chunky_png.rb lib/chunky_png/canvas/png_encoding.rb .gitignore spec/resources/16x16_interlaced.png spec/chunky_png/canvas/drawing_spec.rb spec/chunky_png/canvas/png_encoding_spec.rb spec/resources/adam7.png lib/chunky_png/palette.rb spec/resources/gray_10x10_indexed.png spec/chunky_png/canvas_spec.rb spec/resources/clock.png benchmarks/encoding_benchmark.rb spec/chunky_png/image_spec.rb spec/resources/clock_rotate_180.png spec/resources/clock_rotate_left.png BENCHMARKS.rdoc spec/resources/16x16_non_interlaced.png lib/chunky_png/chunk.rb spec/chunky_png/canvas/operations_spec.rb spec/resources/gray_10x10_truecolor.png spec/resources/gray_10x10_grayscale.png spec/resources/pixelstream_reference.png spec/chunky_png_spec.rb spec/resources/operations.png spec/resources/clock_flip_vertically.png spec/chunky_png/rmagick_spec.rb lib/chunky_png/datastream.rb spec/resources/clock_base.png lib/chunky_png/canvas/operations.rb spec/resources/replaced.png spec/resources/clock_updated.png spec/resources/damaged_signature.png spec/chunky_png/datastream_spec.rb lib/chunky_png/canvas/adam7_interlacing.rb spec/chunky_png/color_spec.rb spec/resources/gray_10x10.png)
38
- s.test_files = %w(spec/chunky_png/canvas/adam7_interlacing_spec.rb spec/chunky_png/canvas/png_decoding_spec.rb spec/chunky_png/canvas/drawing_spec.rb spec/chunky_png/canvas/png_encoding_spec.rb spec/chunky_png/canvas_spec.rb spec/chunky_png/image_spec.rb spec/chunky_png/canvas/operations_spec.rb spec/chunky_png_spec.rb spec/chunky_png/rmagick_spec.rb spec/chunky_png/datastream_spec.rb spec/chunky_png/color_spec.rb)
37
+ s.files = %w(.gitignore BENCHMARKS.rdoc Gemfile Gemfile.lock LICENSE README.rdoc Rakefile benchmarks/decoding_benchmark.rb benchmarks/encoding_benchmark.rb benchmarks/filesize_benchmark.rb chunky_png.gemspec lib/chunky_png.rb lib/chunky_png/canvas.rb lib/chunky_png/canvas/adam7_interlacing.rb lib/chunky_png/canvas/drawing.rb lib/chunky_png/canvas/operations.rb lib/chunky_png/canvas/png_decoding.rb lib/chunky_png/canvas/png_encoding.rb lib/chunky_png/canvas/stream_exporting.rb lib/chunky_png/canvas/stream_importing.rb lib/chunky_png/chunk.rb lib/chunky_png/color.rb lib/chunky_png/datastream.rb lib/chunky_png/image.rb lib/chunky_png/palette.rb lib/chunky_png/rmagick.rb spec/chunky_png/canvas/adam7_interlacing_spec.rb spec/chunky_png/canvas/drawing_spec.rb spec/chunky_png/canvas/operations_spec.rb spec/chunky_png/canvas/png_decoding_spec.rb spec/chunky_png/canvas/png_encoding_spec.rb spec/chunky_png/canvas_spec.rb spec/chunky_png/color_spec.rb spec/chunky_png/datastream_spec.rb spec/chunky_png/image_spec.rb spec/chunky_png/rmagick_spec.rb spec/chunky_png_spec.rb spec/resources/16x16_interlaced.png spec/resources/16x16_non_interlaced.png spec/resources/adam7.png spec/resources/clock.png spec/resources/clock_base.png spec/resources/clock_flip_horizontally.png spec/resources/clock_flip_vertically.png spec/resources/clock_mask.png spec/resources/clock_mask_updated.png spec/resources/clock_rotate_180.png spec/resources/clock_rotate_left.png spec/resources/clock_rotate_right.png spec/resources/clock_stubbed.png spec/resources/clock_updated.png spec/resources/composited.png spec/resources/cropped.png spec/resources/damaged_chunk.png spec/resources/damaged_signature.png spec/resources/gray_10x10.png spec/resources/gray_10x10_grayscale.png spec/resources/gray_10x10_grayscale_alpha.png spec/resources/gray_10x10_indexed.png spec/resources/gray_10x10_truecolor.png spec/resources/gray_10x10_truecolor_alpha.png spec/resources/indexed_4bit.png spec/resources/lines.png spec/resources/operations.png spec/resources/pixelstream.rgb spec/resources/pixelstream.rgba spec/resources/pixelstream_best_compression.png spec/resources/pixelstream_fast_rgba.png spec/resources/pixelstream_reference.png spec/resources/rect.png spec/resources/replaced.png spec/resources/text_chunk.png spec/resources/transparent_gray_10x10.png spec/resources/ztxt_chunk.png spec/spec_helper.rb tasks/benchmarks.rake tasks/github-gem.rake)
38
+ s.test_files = %w(spec/chunky_png/canvas/adam7_interlacing_spec.rb spec/chunky_png/canvas/drawing_spec.rb spec/chunky_png/canvas/operations_spec.rb spec/chunky_png/canvas/png_decoding_spec.rb spec/chunky_png/canvas/png_encoding_spec.rb spec/chunky_png/canvas_spec.rb spec/chunky_png/color_spec.rb spec/chunky_png/datastream_spec.rb spec/chunky_png/image_spec.rb spec/chunky_png/rmagick_spec.rb spec/chunky_png_spec.rb)
39
39
  end
@@ -27,7 +27,7 @@ module ChunkyPNG
27
27
 
28
28
  # The current version of ChunkyPNG. This value will be updated automatically
29
29
  # by them gem:release rake task.
30
- VERSION = "0.9.2"
30
+ VERSION = "0.10.0"
31
31
 
32
32
  ###################################################
33
33
  # PNG international standard defined constants
@@ -80,4 +80,14 @@ module ChunkyPNG
80
80
  # Exception that is raised if an expectation fails.
81
81
  class OutOfBounds < ChunkyPNG::ExpectationFailed
82
82
  end
83
+
84
+ EXTRA_BYTE = (RUBY_VERSION.to_f < 1.9) ? "\0" : "\0".force_encoding('ASCII-8BIT')
85
+ end
86
+
87
+ if RUBY_VERSION.to_f < 1.9
88
+ class String
89
+ alias_method :getbyte, :[]
90
+ alias_method :setbyte, :[]=
91
+ alias_method :bytesize, :size
92
+ end
83
93
  end
@@ -97,20 +97,37 @@ module ChunkyPNG
97
97
  # @param [Integer] x The x-coordinate of the pixel (column)
98
98
  # @param [Integer] y The y-coordinate of the pixel (row)
99
99
  # @param [ChunkyPNG::Color] pixel The new pixel for the provided coordinates.
100
+ # @return [Integer] the new pixel value, i.e. <tt>color</tt>.
101
+ # @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the image's dimensions.
100
102
  def []=(x, y, color)
101
103
  assert_xy!(x, y)
102
104
  @pixels[y * width + x] = color
103
105
  end
104
106
 
107
+ # Replaces a single pixel in this canvas, without bounds checking.
108
+ # @param (see #[]=)
109
+ # @return [Integer] the new pixel value, i.e. <tt>color</tt>.
110
+ def set_pixel(x, y, color)
111
+ @pixels[y * width + x] = color
112
+ end
113
+
105
114
  # Returns a single pixel from this canvas.
106
115
  # @param [Integer] x The x-coordinate of the pixel (column)
107
116
  # @param [Integer] y The y-coordinate of the pixel (row)
108
117
  # @return [ChunkyPNG::Color] The current pixel at the provided coordinates.
118
+ # @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the image's dimensions.
109
119
  def [](x, y)
110
120
  assert_xy!(x, y)
111
121
  @pixels[y * width + x]
112
122
  end
113
123
 
124
+ # Returns a single pixel from this canvas, without checking bounds.
125
+ # @param (see #[])
126
+ # @return [ChunkyPNG::Color] The current pixel at the provided coordinates.
127
+ def get_pixel(x, y)
128
+ @pixels[y * width + x]
129
+ end
130
+
114
131
  # Returns an extracted row as vector of pixels
115
132
  # @param [Integer] y The 0-based row index
116
133
  # @return [Array<Integer>] The vector of pixels in the requested row
@@ -124,7 +141,7 @@ module ChunkyPNG
124
141
  # @return [Array<Integer>] The vector of pixels in the requested column.
125
142
  def column(x)
126
143
  assert_x!(x)
127
- (0...height).inject([]) { |pixels, y| pixels << self[x, y] }
144
+ (0...height).inject([]) { |pixels, y| pixels << get_pixel(x, y) }
128
145
  end
129
146
 
130
147
  # Replaces a row of pixels on this canvas.
@@ -141,7 +158,7 @@ module ChunkyPNG
141
158
  def replace_column!(x, vector)
142
159
  assert_x!(x) && assert_height!(vector.length)
143
160
  for y in 0...height do
144
- self[x, y] = vector[y]
161
+ set_pixel(x, y, vector[y])
145
162
  end
146
163
  end
147
164
 
@@ -5,7 +5,7 @@ module ChunkyPNG
5
5
 
6
6
  # Sets a point on the canvas by composing a pixel with its background color.
7
7
  def point(x, y, color)
8
- self[x, y] = ChunkyPNG::Color.compose(color, self[x, y])
8
+ set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y)))
9
9
  end
10
10
 
11
11
  # Draws an anti-aliased line using Xiaolin Wu's algorithm.
@@ -63,7 +63,7 @@ module ChunkyPNG
63
63
  return self
64
64
  end
65
65
 
66
- alias :line :line_xiaolin_wu
66
+ alias_method :line, :line_xiaolin_wu
67
67
 
68
68
  def rect(x0, y0, x1, y1, line_color, fill_color = ChunkyPNG::COLOR::TRANSPARENT)
69
69
 
@@ -29,7 +29,7 @@ module ChunkyPNG
29
29
 
30
30
  for y in 0...other.height do
31
31
  for x in 0...other.width do
32
- self[x + offset_x, y + offset_y] = ChunkyPNG::Color.compose(other[x, y], self[x + offset_x, y + offset_y])
32
+ set_pixel(x + offset_x, y + offset_y, ChunkyPNG::Color.compose(other.get_pixel(x, y), get_pixel(x + offset_x, y + offset_y)))
33
33
  end
34
34
  end
35
35
  self
@@ -14,12 +14,12 @@ module ChunkyPNG
14
14
  # * Based on the color mode, width and height of the original image, which
15
15
  # is read from the PNG header (IHDR chunk), the amount of bytes
16
16
  # per line is determined.
17
- # * For every line of pixels in the original image, the determined amount
18
- # of bytes is read from the pixel stream.
17
+ # * For every line of pixels in the encoded image, the original byte values
18
+ # are restored by unapplying the milter method for that line.
19
19
  # * The read bytes are unfiltered given by the filter function specified by
20
20
  # the first byte of the line.
21
- # * The unfiltered bytes are converted into colored pixels, using the color mode.
22
- # * All lines combined form the original image.
21
+ # * The unfiltered pixelstream are is into colored pixels, using the color mode.
22
+ # * All lines combined to form the original image.
23
23
  #
24
24
  # For interlaced images, the original image was split into 7 subimages.
25
25
  # These images get decoded just like the process above (from step 3), and get
@@ -41,7 +41,7 @@ module ChunkyPNG
41
41
  from_datastream(ChunkyPNG::Datastream.from_blob(str))
42
42
  end
43
43
 
44
- alias :from_string :from_blob
44
+ alias_method :from_string, :from_blob
45
45
 
46
46
  # Decodes a Canvas from a PNG encoded file.
47
47
  # @param [String] filename The file to read from.
@@ -57,6 +57,8 @@ module ChunkyPNG
57
57
  from_datastream(ChunkyPNG::Datastream.from_io(io))
58
58
  end
59
59
 
60
+ alias_method :from_stream, :from_io
61
+
60
62
  # Decodes the Canvas from a PNG datastream instance.
61
63
  # @param [ChunkyPNG::Datastream] ds The datastream to decode.
62
64
  # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG datastream.
@@ -86,8 +88,8 @@ module ChunkyPNG
86
88
  raise ChunkyPNG::ExpectationFailed, "This palette is not suitable for decoding!" if decoding_palette && !decoding_palette.can_decode?
87
89
 
88
90
  return case interlace
89
- when ChunkyPNG::INTERLACING_NONE then decode_png_without_interlacing(stream, width, height, color_mode)
90
- when ChunkyPNG::INTERLACING_ADAM7 then decode_png_with_adam7_interlacing(stream, width, height, color_mode)
91
+ when ChunkyPNG::INTERLACING_NONE; decode_png_without_interlacing(stream, width, height, color_mode)
92
+ when ChunkyPNG::INTERLACING_ADAM7; decode_png_with_adam7_interlacing(stream, width, height, color_mode)
91
93
  else raise ChunkyPNG::NotSupported, "Don't know how the handle interlacing method #{interlace}!"
92
94
  end
93
95
  end
@@ -138,14 +140,14 @@ module ChunkyPNG
138
140
  # @param [Integer] start_pos The position in the pixel stream to start reading.
139
141
  # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
140
142
  def decode_png_image_pass(stream, width, height, color_mode, start_pos = 0)
141
-
143
+ stream << ChunkyPNG::EXTRA_BYTE if color_mode == ChunkyPNG::COLOR_TRUECOLOR
142
144
  pixel_size = Color.bytesize(color_mode)
143
145
  pixel_decoder = case color_mode
144
- when ChunkyPNG::COLOR_TRUECOLOR then lambda { |bytes| ChunkyPNG::Color.rgb(*bytes) }
145
- when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then lambda { |bytes| ChunkyPNG::Color.rgba(*bytes) }
146
- when ChunkyPNG::COLOR_INDEXED then lambda { |bytes| decoding_palette[bytes.first] }
147
- when ChunkyPNG::COLOR_GRAYSCALE then lambda { |bytes| ChunkyPNG::Color.grayscale(*bytes) }
148
- when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then lambda { |bytes| ChunkyPNG::Color.grayscale_alpha(*bytes) }
146
+ when ChunkyPNG::COLOR_TRUECOLOR; lambda { |s, pos| s.unpack("@#{pos + 1}" << ('NX' * width)).map { |c| c | 0x000000ff } }
147
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; lambda { |s, pos| s.unpack("@#{pos + 1}N#{width}") }
148
+ when ChunkyPNG::COLOR_INDEXED; lambda { |s, pos| (1..width).map { |i| decoding_palette[s.getbyte(pos + i)] } }
149
+ when ChunkyPNG::COLOR_GRAYSCALE; lambda { |s, pos| (1..width).map { |i| ChunkyPNG::Color.grayscale(s.getbyte(pos + i)) } }
150
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; lambda { |s, pos| (0...width).map { |i| ChunkyPNG::Color.grayscale_alpha(s.getbyte(pos + (i * 2) + 1), s.getbyte(pos + (i * 2) + 2)) } }
149
151
  else raise ChunkyPNG::NotSupported, "No suitable pixel decoder found for color mode #{color_mode}!"
150
152
  end
151
153
 
@@ -155,114 +157,103 @@ module ChunkyPNG
155
157
  raise ChunkyPNG::ExpectationFailed, "Invalid stream length!" unless stream.length - start_pos >= width * height * pixel_size + height
156
158
 
157
159
  decoded_bytes = Array.new(width * pixel_size, 0)
158
- for line_no in 0...height do
160
+ line_length = width * pixel_size
161
+ pos, prev_pos = start_pos, nil
159
162
 
160
- # get bytes of scanline
161
- position = start_pos + line_no * (width * pixel_size + 1)
162
- line_length = width * pixel_size
163
- bytes = stream.unpack("@#{position}CC#{line_length}")
164
- filter = bytes.shift
165
- decoded_bytes = decode_png_scanline(filter, bytes, decoded_bytes, pixel_size)
163
+ for line_no in 0...height do
164
+ decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size)
165
+ pixels += pixel_decoder.call(stream, pos)
166
166
 
167
- # decode bytes into colors
168
- decoded_bytes.each_slice(pixel_size) { |bytes| pixels << pixel_decoder.call(bytes) }
167
+ prev_pos = pos
168
+ pos += line_length + 1
169
169
  end
170
170
  end
171
171
 
172
172
  new(width, height, pixels)
173
173
  end
174
174
 
175
- # Decodes filtered bytes from a scanline from a PNG pixelstream,
176
- # to return the original bytes of the image.
175
+ # Decodes a scanline if it was encoded using filtering.
177
176
  #
178
- # The decoded bytes should be used to get the original pixels of the
179
- # scanline, combining them using a color mode dependent color decoder.
177
+ # It will extract the filtering method from the first byte of the scanline, and uses the
178
+ # method to change the subsequent bytes to unfiltered values. This will modify the pixelstream.
180
179
  #
181
- # @param [Integer] filter The filter used to encode the bytes.
182
- # @param [Array<Integer>] bytes The filtered bytes to decode.
183
- # @param [Array<Integer>] previous_bytes The decoded bytes of the
184
- # previous scanline.
185
- # @param [Integer] pixelsize The amount of bytes used for every pixel.
186
- # This depends on the used color mode and color depth.
187
- # @return [Array<Integer>] The array of original bytes for the scanline,
188
- # before they were encoded.
189
- def decode_png_scanline(filter, bytes, previous_bytes, pixelsize = 3)
190
- case filter
191
- when ChunkyPNG::FILTER_NONE then decode_png_scanline_none( bytes, previous_bytes, pixelsize)
192
- when ChunkyPNG::FILTER_SUB then decode_png_scanline_sub( bytes, previous_bytes, pixelsize)
193
- when ChunkyPNG::FILTER_UP then decode_png_scanline_up( bytes, previous_bytes, pixelsize)
194
- when ChunkyPNG::FILTER_AVERAGE then decode_png_scanline_average( bytes, previous_bytes, pixelsize)
195
- when ChunkyPNG::FILTER_PAETH then decode_png_scanline_paeth( bytes, previous_bytes, pixelsize)
196
- else raise ChunkyPNG::NotSupported, "Unknown filter type: #{filter}!"
180
+ # The bytes of the scanline can then be used to construct pixels, based on the color mode..
181
+ #
182
+ # @param [String] stream The pixelstream to undo the filtering in.
183
+ # @param [Integer] pos The starting position of the scanline to decode.
184
+ # @param [Integer, nil] prev_pos The starting position of the previously decoded scanline, or <tt>nil</tt>
185
+ # if this is the first scanline of the image.
186
+ # @param [Integer] line_length The number of bytes in the scanline, discounting the filter method byte.
187
+ # @param [Integer] pixel_size The number of bytes used per pixel, based on the color mode.
188
+ # @return [nil]
189
+ def decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size)
190
+ case stream.getbyte(pos)
191
+ when ChunkyPNG::FILTER_NONE; # noop
192
+ when ChunkyPNG::FILTER_SUB; decode_png_str_scanline_sub( stream, pos, prev_pos, line_length, pixel_size)
193
+ when ChunkyPNG::FILTER_UP; decode_png_str_scanline_up( stream, pos, prev_pos, line_length, pixel_size)
194
+ when ChunkyPNG::FILTER_AVERAGE; decode_png_str_scanline_average( stream, pos, prev_pos, line_length, pixel_size)
195
+ when ChunkyPNG::FILTER_PAETH; decode_png_str_scanline_paeth( stream, pos, prev_pos, line_length, pixel_size)
196
+ else raise ChunkyPNG::NotSupported, "Unknown filter type: #{stream.getbyte(pos)}!"
197
197
  end
198
198
  end
199
199
 
200
- # Decoded filtered scanline bytes that were not filtered.
201
- # @param bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
202
- # @param previous_bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
203
- # @param pixelsize (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
204
- # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
205
- # @see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline
206
- def decode_png_scanline_none(bytes, previous_bytes, pixelsize = 3)
207
- bytes
200
+ # Decodes a scanline that wasn't encoded using filtering. This is a no-op.
201
+ # @params (see #decode_png_str_scanline)
202
+ # @return [nil]
203
+ def decode_png_str_scanline_sub_none(stream, pos, prev_pos, line_length, pixel_size)
204
+ # noop - this method shouldn't get called.
208
205
  end
209
206
 
210
- # Decoded filtered scanline bytes that were filtered using SUB filtering.
211
- # @param bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
212
- # @param previous_bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
213
- # @param pixelsize (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
214
- # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
215
- # @see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline
216
- def decode_png_scanline_sub(bytes, previous_bytes, pixelsize = 3)
217
- bytes.each_with_index { |b, i| bytes[i] = (b + (i >= pixelsize ? bytes[i-pixelsize] : 0)) % 256 }
218
- bytes
207
+ # Decodes a scanline in a pxielstream that was encoded using SUB filtering.
208
+ # This will chnage the pixelstream to have unfiltered values.
209
+ # @params (see #decode_png_str_scanline)
210
+ # @return [nil]
211
+ def decode_png_str_scanline_sub(stream, pos, prev_pos, line_length, pixel_size)
212
+ for i in 1..line_length do
213
+ stream.setbyte(pos + i, (stream.getbyte(pos + i) + (i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0)) & 0xff)
214
+ end
219
215
  end
220
216
 
221
- # Decoded filtered scanline bytes that were filtered using UP filtering.
222
- # @param bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
223
- # @param previous_bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
224
- # @param pixelsize (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
225
- # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
226
- # @see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline
227
- def decode_png_scanline_up(bytes, previous_bytes, pixelsize = 3)
228
- bytes.each_with_index { |b, i| bytes[i] = (b + previous_bytes[i]) % 256 }
229
- bytes
217
+ # Decodes a scanline in a pxielstream that was encoded using UP filtering.
218
+ # This will chnage the pixelstream to have unfiltered values.
219
+ # @params (see #decode_png_str_scanline)
220
+ # @return [nil]
221
+ def decode_png_str_scanline_up(stream, pos, prev_pos, line_length, pixel_size)
222
+ for i in 1..line_length do
223
+ up = prev_pos ? stream.getbyte(prev_pos + i) : 0
224
+ stream.setbyte(pos + i, (stream.getbyte(pos + i) + up) & 0xff)
225
+ end
230
226
  end
231
227
 
232
- # Decoded filtered scanline bytes that were filtered using AVERAGE filtering.
233
- # @param bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
234
- # @param previous_bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
235
- # @param pixelsize (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
236
- # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
237
- # @see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline
238
- def decode_png_scanline_average(bytes, previous_bytes, pixelsize = 3)
239
- bytes.each_with_index do |byte, i|
240
- a = (i >= pixelsize) ? bytes[i - pixelsize] : 0
241
- b = previous_bytes[i]
242
- bytes[i] = (byte + ((a + b) >> 1)) % 256
228
+ # Decodes a scanline in a pxielstream that was encoded using AVERAGE filtering.
229
+ # This will chnage the pixelstream to have unfiltered values.
230
+ # @params (see #decode_png_str_scanline)
231
+ # @return [nil]
232
+ def decode_png_str_scanline_average(stream, pos, prev_pos, line_length, pixel_size)
233
+ for i in 1..line_length do
234
+ a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0
235
+ b = prev_pos ? stream.getbyte(prev_pos + i) : 0
236
+ stream.setbyte(pos + i, (stream.getbyte(pos + i) + ((a + b) >> 1)) & 0xff)
243
237
  end
244
- bytes
245
238
  end
246
239
 
247
- # Decoded filtered scanline bytes that were filtered using PAETH filtering.
248
- # @param bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
249
- # @param previous_bytes (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
250
- # @param pixelsize (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
251
- # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline)
252
- # @see ChunkyPNG::Canvas::PNGDecoding#decode_png_scanline
253
- def decode_png_scanline_paeth(bytes, previous_bytes, pixelsize = 3)
254
- bytes.each_with_index do |byte, i|
255
- a = (i >= pixelsize) ? bytes[i - pixelsize] : 0
256
- b = previous_bytes[i]
257
- c = (i >= pixelsize) ? previous_bytes[i - pixelsize] : 0
240
+ # Decodes a scanline in a pxielstream that was encoded using PAETH filtering.
241
+ # This will chnage the pixelstream to have unfiltered values.
242
+ # @params (see #decode_png_str_scanline)
243
+ # @return [nil]
244
+ def decode_png_str_scanline_paeth(stream, pos, prev_pos, line_length, pixel_size)
245
+ for i in 1..line_length do
246
+ cur_pos = pos + i
247
+ a = (i > pixel_size) ? stream.getbyte(cur_pos - pixel_size) : 0
248
+ b = prev_pos ? stream.getbyte(prev_pos + i) : 0
249
+ c = (prev_pos && i > pixel_size) ? stream.getbyte(prev_pos + i - pixel_size) : 0
258
250
  p = a + b - c
259
251
  pa = (p - a).abs
260
252
  pb = (p - b).abs
261
253
  pc = (p - c).abs
262
- pr = (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c)
263
- bytes[i] = (byte + pr) % 256
254
+ pr = (pa <= pb) ? (pa <= pc ? a : c) : (pb <= pc ? b : c)
255
+ stream.setbyte(cur_pos, (stream.getbyte(cur_pos) + pr) & 0xff)
264
256
  end
265
- bytes
266
257
  end
267
258
  end
268
259
  end