chunky_png 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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