chunky_png 0.5.4 → 0.5.5

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.
@@ -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.5.4"
7
- s.date = "2010-01-18"
6
+ s.version = "0.5.5"
7
+ s.date = "2010-02-16"
8
8
 
9
9
  s.summary = "Pure ruby library for read/write, chunk-level access to PNG files"
10
10
  s.description = <<-EOT
@@ -26,7 +26,7 @@ module ChunkyPNG
26
26
 
27
27
  # The current version of ChunkyPNG. This value will be updated automatically
28
28
  # by them gem:release rake task.
29
- VERSION = "0.5.4"
29
+ VERSION = "0.5.5"
30
30
 
31
31
  ###################################################
32
32
  # PNG international standard defined constants
@@ -1,26 +1,65 @@
1
1
  module ChunkyPNG
2
2
  class Canvas
3
-
4
- # The PNGDecoding contains methods for decoding PNG datastreams to create a Canvas object.
5
- # The datastream can be provided as filename, string or IO object.
3
+
4
+ # The PNGDecoding contains methods for decoding PNG datastreams to create a
5
+ # Canvas object. The datastream can be provided as filename, string or IO
6
+ # stream.
7
+ #
8
+ # Overview of the decoding process:
9
+ #
10
+ # * The optional PLTE and tRNS chunk are decoded for the color palette of
11
+ # the original image.
12
+ # * The contents of the IDAT chunks is combined, and uncompressed using
13
+ # Inflate decompression to the image pixelstream.
14
+ # * Based on the color mode, width and height of the original image, which
15
+ # is read from the PNG header (IHDR chunk), the amount of bytes
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.
19
+ # * The read bytes are unfiltered given by the filter function specified by
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.
23
+ #
24
+ # For interlaced images, the original image was split into 7 subimages.
25
+ # These images get decoded just like the process above (from step 3), and get
26
+ # combined to form the original images.
27
+ #
28
+ # @see ChunkyPNG::Canvas::PNGEncoding
29
+ # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification
6
30
  module PNGDecoding
7
31
 
32
+ # The palette that is used to decode the image, loading from the PLTE and
33
+ # tRNS chunk from the PNG stream. For RGB(A) images, no palette is required.
34
+ # @return [ChunkyPNG::Palette]
8
35
  attr_accessor :decoding_palette
9
36
 
37
+ # Decodes a Canvas from a PNG encoded string.
38
+ # @param [String] str The string to read from.
39
+ # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG encoded string.
10
40
  def from_blob(str)
11
41
  from_datastream(ChunkyPNG::Datastream.from_blob(str))
12
42
  end
13
-
43
+
14
44
  alias :from_string :from_blob
15
45
 
46
+ # Decodes a Canvas from a PNG encoded file.
47
+ # @param [String] filename The file to read from.
48
+ # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG file.
16
49
  def from_file(filename)
17
50
  from_datastream(ChunkyPNG::Datastream.from_file(filename))
18
51
  end
19
-
52
+
53
+ # Decodes a Canvas from a PNG encoded stream.
54
+ # @param [IO, #read] io The stream to read from.
55
+ # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG stream.
20
56
  def from_io(io)
21
57
  from_datastream(ChunkyPNG::Datastream.from_io(io))
22
58
  end
23
59
 
60
+ # Decodes the Canvas from a PNG datastream instance.
61
+ # @param [ChunkyPNG::Datastream] ds The datastream to decode.
62
+ # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG datastream.
24
63
  def from_datastream(ds)
25
64
  raise "Only 8-bit color depth is currently supported by ChunkyPNG!" unless ds.header_chunk.depth == 8
26
65
 
@@ -28,13 +67,21 @@ module ChunkyPNG
28
67
  height = ds.header_chunk.height
29
68
  color_mode = ds.header_chunk.color
30
69
  interlace = ds.header_chunk.interlace
31
-
70
+
32
71
  self.decoding_palette = ChunkyPNG::Palette.from_chunks(ds.palette_chunk, ds.transparency_chunk)
33
72
  pixelstream = ChunkyPNG::Chunk::ImageData.combine_chunks(ds.data_chunks)
34
-
73
+
35
74
  decode_png_pixelstream(pixelstream, width, height, color_mode, interlace)
36
75
  end
37
76
 
77
+ # Decodes a canvas from a PNG encoded pixelstream, using a given width, height,
78
+ # color mode and interlacing mode.
79
+ # @param [String] stream The pixelstream to read from.
80
+ # @param [Integer] width The width of the image.
81
+ # @param [Integer] width The height of the image.
82
+ # @param [Integer] color_mode The color mode of the encoded pixelstream.
83
+ # @param [Integer] interlace The interlace method of the encoded pixelstream.
84
+ # @return [ChunkyPNG::Canvas] The decoded Canvas instance.
38
85
  def decode_png_pixelstream(stream, width, height, color_mode = ChunkyPNG::COLOR_TRUECOLOR, interlace = ChunkyPNG::INTERLACING_NONE)
39
86
  raise "This palette is not suitable for decoding!" if decoding_palette && !decoding_palette.can_decode?
40
87
 
@@ -47,10 +94,24 @@ module ChunkyPNG
47
94
 
48
95
  protected
49
96
 
97
+ # Decodes a canvas from a non-interlaced PNG encoded pixelstream, using a
98
+ # given width, height and color mode.
99
+ # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
100
+ # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
101
+ # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
102
+ # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
103
+ # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
50
104
  def decode_png_without_interlacing(stream, width, height, color_mode)
51
105
  decode_png_image_pass(stream, width, height, color_mode)
52
106
  end
53
107
 
108
+ # Decodes a canvas from a Adam 7 interlaced PNG encoded pixelstream, using a
109
+ # given width, height and color mode.
110
+ # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
111
+ # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
112
+ # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
113
+ # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
114
+ # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
54
115
  def decode_png_with_adam7_interlacing(stream, width, height, color_mode)
55
116
  canvas = ChunkyPNG::Canvas.new(width, height)
56
117
  pixel_size = Color.bytesize(color_mode)
@@ -64,6 +125,18 @@ module ChunkyPNG
64
125
  canvas
65
126
  end
66
127
 
128
+ # Decodes a single PNG image pass width a given width, height and color
129
+ # mode, to a Canvas, starting at the given position in the stream.
130
+ #
131
+ # A non-interlaced image only consists of one pass, while an Adam7
132
+ # image consists of 7 passes that must be combined after decoding.
133
+ #
134
+ # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
135
+ # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
136
+ # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
137
+ # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
138
+ # @param [Integer] start_pos The position in the pixel stream to start reading.
139
+ # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
67
140
  def decode_png_image_pass(stream, width, height, color_mode, start_pos = 0)
68
141
 
69
142
  pixel_size = Color.bytesize(color_mode)
@@ -99,8 +172,20 @@ module ChunkyPNG
99
172
  new(width, height, pixels)
100
173
  end
101
174
 
102
-
103
-
175
+ # Decodes filtered bytes from a scanline from a PNG pixelstream,
176
+ # to return the original bytes of the image.
177
+ #
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.
180
+ #
181
+ # @param [Integer] filter The filter used to encode the bytes.
182
+ # @param [Array<Fixnum>] bytes The filtered bytes to decode.
183
+ # @param [Array<Fixnum>] 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<Fixnum>] The array of original bytes for the scanline,
188
+ # before they were encoded.
104
189
  def decode_png_scanline(filter, bytes, previous_bytes, pixelsize = 3)
105
190
  case filter
106
191
  when ChunkyPNG::FILTER_NONE then decode_png_scanline_none( bytes, previous_bytes, pixelsize)
@@ -112,20 +197,44 @@ module ChunkyPNG
112
197
  end
113
198
  end
114
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
115
206
  def decode_png_scanline_none(bytes, previous_bytes, pixelsize = 3)
116
207
  bytes
117
208
  end
118
209
 
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
119
216
  def decode_png_scanline_sub(bytes, previous_bytes, pixelsize = 3)
120
217
  bytes.each_with_index { |b, i| bytes[i] = (b + (i >= pixelsize ? bytes[i-pixelsize] : 0)) % 256 }
121
218
  bytes
122
219
  end
123
220
 
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
124
227
  def decode_png_scanline_up(bytes, previous_bytes, pixelsize = 3)
125
228
  bytes.each_with_index { |b, i| bytes[i] = (b + previous_bytes[i]) % 256 }
126
229
  bytes
127
230
  end
128
231
 
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
129
238
  def decode_png_scanline_average(bytes, previous_bytes, pixelsize = 3)
130
239
  bytes.each_with_index do |byte, i|
131
240
  a = (i >= pixelsize) ? bytes[i - pixelsize] : 0
@@ -135,6 +244,12 @@ module ChunkyPNG
135
244
  bytes
136
245
  end
137
246
 
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
138
253
  def decode_png_scanline_paeth(bytes, previous_bytes, pixelsize = 3)
139
254
  bytes.each_with_index do |byte, i|
140
255
  a = (i >= pixelsize) ? bytes[i - pixelsize] : 0
@@ -1,20 +1,49 @@
1
1
  module ChunkyPNG
2
2
  class Canvas
3
3
 
4
- # Methods for encoding a Canvas into a PNG datastream
4
+ # Methods for encoding a Canvas instance into a PNG datastream.
5
5
  #
6
+ # Overview of the encoding process:
7
+ #
8
+ # * The image is split up in scanlines (i.e. rows of pixels);
9
+ # * Every pixel in this row is converted into bytes, based on the color mode;
10
+ # * Filter every byte in the row according to the filter method.
11
+ # * Concatenate all the filtered bytes of every line to a single stream
12
+ # * Compress the resulting string using deflate compression.
13
+ # * Split compressed data over one or more PNG chunks.
14
+ # * These chunks should be embedded in a datastream with at least a IHDR and
15
+ # IEND chunk and possibly a PLTE chunk.
16
+ #
17
+ # For interlaced images, the initial image is first split into 7 subimages.
18
+ # These images get encoded exectly as above, and the result gets combined
19
+ # before the compression step.
20
+ #
21
+ # @see ChunkyPNG::Canvas::PNGDecoding
22
+ # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification
6
23
  module PNGEncoding
7
24
 
25
+ # The palette used for encoding the image.This is only in used for images
26
+ # that get encoded using indexed colors.
27
+ # @return [ChunkyPNG::Palette]
8
28
  attr_accessor :encoding_palette
9
29
 
30
+ # Writes the canvas to an IO stream, encoded as a PNG image.
31
+ # @param [IO] io The output stream to write to.
32
+ # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream)
10
33
  def write(io, constraints = {})
11
34
  to_datastream(constraints).write(io)
12
35
  end
13
36
 
37
+ # Writes the canvas to a file, encoded as a PNG image.
38
+ # @param [String] filname The file to save the PNG image to.
39
+ # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream)
14
40
  def save(filename, constraints = {})
15
41
  File.open(filename, 'wb') { |io| write(io, constraints) }
16
42
  end
17
43
 
44
+ # Encoded the canvas to a PNG formatted string.
45
+ # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream)
46
+ # @return [String] The PNG encoded canvas as string.
18
47
  def to_blob(constraints = {})
19
48
  to_datastream(constraints).to_blob
20
49
  end
@@ -24,33 +53,36 @@ module ChunkyPNG
24
53
 
25
54
  # Converts this Canvas to a datastream, so that it can be saved as a PNG image.
26
55
  # @param [Hash] constraints The constraints to use when encoding the canvas.
56
+ # @return [ChunkyPNG::Datastream] The PNG datastream containing the encoded canvas.
57
+ # @see ChunkyPNG::Canvas::PNGEncoding#determine_png_encoding
27
58
  def to_datastream(constraints = {})
28
- data = encode_png(constraints)
59
+ encoding = determine_png_encoding(constraints)
60
+
29
61
  ds = Datastream.new
30
- ds.header_chunk = Chunk::Header.new(data[:header])
31
- ds.palette_chunk = data[:palette_chunk] if data[:palette_chunk]
32
- ds.transparency_chunk = data[:transparency_chunk] if data[:transparency_chunk]
33
- ds.data_chunks = Chunk::ImageData.split_in_chunks(data[:pixelstream])
34
- ds.end_chunk = Chunk::End.new
62
+ ds.header_chunk = Chunk::Header.new(:width => width, :height => height,
63
+ :color => encoding[:color_mode], :interlace => encoding[:interlace])
64
+
65
+ if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED
66
+ ds.palette_chunk = encoding_palette.to_plte_chunk
67
+ ds.transparency_chunk = encoding_palette.to_trns_chunk unless encoding_palette.opaque?
68
+ end
69
+
70
+ data = encode_png_pixelstream(encoding[:color_mode], encoding[:interlace])
71
+ ds.data_chunks = Chunk::ImageData.split_in_chunks(data)
72
+ ds.end_chunk = Chunk::End.new
35
73
  return ds
36
74
  end
37
75
 
38
76
  protected
39
-
40
- def encode_png(constraints = {})
41
- encoding = determine_png_encoding(constraints)
42
- result = {}
43
- result[:header] = { :width => width, :height => height, :color => encoding[:color_mode], :interlace => encoding[:interlace] }
44
-
45
- if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED
46
- result[:palette_chunk] = encoding_palette.to_plte_chunk
47
- result[:transparency_chunk] = encoding_palette.to_trns_chunk unless encoding_palette.opaque?
48
- end
49
-
50
- result[:pixelstream] = encode_png_pixelstream(encoding[:color_mode], encoding[:interlace])
51
- return result
52
- end
53
77
 
78
+ # Determines the best possible PNG encoding variables for this image, by analyzing
79
+ # the colors used for the image.
80
+ #
81
+ # You can provide constraints for the encoding variables by passing a hash with
82
+ # encoding variables to this method.
83
+ #
84
+ # @param [Hash] constraints The constraints for the encoding.
85
+ # @return [Hash] A hash with encoding options for {ChunkyPNG::Canvas::PNGEncoding#to_datastream}
54
86
  def determine_png_encoding(constraints = {})
55
87
 
56
88
  if constraints == :fast_rgb
@@ -80,6 +112,11 @@ module ChunkyPNG
80
112
  return encoding
81
113
  end
82
114
 
115
+ # Encodes the canvas according to the PNG format specification with a given color
116
+ # mode, possibly with interlacing.
117
+ # @param [Integer] color_mode The color mode to use for encoding.
118
+ # @param [Integer] interlace The interlacing method to use.
119
+ # @param [String] The PNG encoded canvas as string.
83
120
  def encode_png_pixelstream(color_mode = ChunkyPNG::COLOR_TRUECOLOR, interlace = ChunkyPNG::INTERLACING_NONE)
84
121
 
85
122
  if color_mode == ChunkyPNG::COLOR_INDEXED && (encoding_palette.nil? || !encoding_palette.can_encode?)
@@ -93,12 +130,23 @@ module ChunkyPNG
93
130
  end
94
131
  end
95
132
 
133
+ # Encodes the canvas according to the PNG format specification with a given color mode.
134
+ # @param [Integer] color_mode The color mode to use for encoding.
135
+ # @param [String] Th PNG encoded canvas as string.
96
136
  def encode_png_image_without_interlacing(color_mode)
97
137
  stream = ""
98
138
  encode_png_image_pass_to_stream(stream, color_mode)
99
139
  stream
100
140
  end
101
141
 
142
+ # Encodes the canvas according to the PNG format specification with a given color
143
+ # mode and Adam7 interlacing.
144
+ #
145
+ # This method will split the original canva in 7 smaller canvases and encode them
146
+ # one by one, concatenating the resulting strings.
147
+ #
148
+ # @param [Integer] color_mode The color mode to use for encoding.
149
+ # @param [String] Th PNG encoded canvas as string.
102
150
  def encode_png_image_with_interlacing(color_mode)
103
151
  stream = ""
104
152
  0.upto(6) do |pass|
@@ -109,35 +157,37 @@ module ChunkyPNG
109
157
  stream
110
158
  end
111
159
 
160
+ # Encodes the canvas to a stream, in a given color mode.
161
+ # @param [String, IO, :<<] stream The stream to write to.
162
+ # @param [Integer] color_mode The color mode to use for encoding.
112
163
  def encode_png_image_pass_to_stream(stream, color_mode)
113
164
 
114
165
  case color_mode
115
- when ChunkyPNG::COLOR_TRUECOLOR_ALPHA
116
- stream << pixels.pack("xN#{width}" * height)
117
-
118
- when ChunkyPNG::COLOR_TRUECOLOR
119
- line_packer = 'x' + ('NX' * width)
120
- stream << pixels.pack(line_packer * height)
121
-
122
- else
123
-
124
- pixel_size = Color.bytesize(color_mode)
125
- pixel_encoder = case color_mode
126
- when ChunkyPNG::COLOR_TRUECOLOR then lambda { |color| Color.to_truecolor_bytes(color) }
127
- when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then lambda { |color| Color.to_truecolor_alpha_bytes(color) }
128
- when ChunkyPNG::COLOR_INDEXED then lambda { |color| [encoding_palette.index(color)] }
129
- when ChunkyPNG::COLOR_GRAYSCALE then lambda { |color| Color.to_grayscale_bytes(color) }
130
- when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then lambda { |color| Color.to_grayscale_alpha_bytes(color) }
131
- else raise "Cannot encode pixels for this mode: #{color_mode}!"
132
- end
133
-
134
- previous_bytes = Array.new(pixel_size * width, 0)
135
- each_scanline do |line|
136
- unencoded_bytes = line.map(&pixel_encoder).flatten
137
- stream << encode_png_scanline_up(unencoded_bytes, previous_bytes, pixel_size).pack('C*')
138
- previous_bytes = unencoded_bytes
139
- end
166
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA
167
+ stream << pixels.pack("xN#{width}" * height)
168
+
169
+ when ChunkyPNG::COLOR_TRUECOLOR
170
+ line_packer = 'x' + ('NX' * width)
171
+ stream << pixels.pack(line_packer * height)
172
+
173
+ else
174
+ pixel_size = Color.bytesize(color_mode)
175
+ pixel_encoder = case color_mode
176
+ when ChunkyPNG::COLOR_TRUECOLOR then lambda { |color| Color.to_truecolor_bytes(color) }
177
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then lambda { |color| Color.to_truecolor_alpha_bytes(color) }
178
+ when ChunkyPNG::COLOR_INDEXED then lambda { |color| [encoding_palette.index(color)] }
179
+ when ChunkyPNG::COLOR_GRAYSCALE then lambda { |color| Color.to_grayscale_bytes(color) }
180
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then lambda { |color| Color.to_grayscale_alpha_bytes(color) }
181
+ else raise "Cannot encode pixels for this mode: #{color_mode}!"
182
+ end
183
+
184
+ previous_bytes = Array.new(pixel_size * width, 0)
185
+ each_scanline do |line|
186
+ unencoded_bytes = line.map(&pixel_encoder).flatten
187
+ stream << encode_png_scanline_up(unencoded_bytes, previous_bytes, pixel_size).pack('C*')
188
+ previous_bytes = unencoded_bytes
140
189
  end
190
+ end
141
191
  end
142
192
 
143
193
  # Passes to this canvas of pixel values line by line.
@@ -150,6 +200,12 @@ module ChunkyPNG
150
200
  end
151
201
  end
152
202
 
203
+ # Encodes the bytes of a scanline with a given filter.
204
+ # @param [Integer] filter The filter method to use.
205
+ # @param [Array<Fixnum>] bytes The scanline bytes to encode.
206
+ # @param [Array<Fixnum>] previous_bytes The original bytes of the previous scanline.
207
+ # @param [Integer] pixelsize The number of bytes per pixel.
208
+ # @return [Array<Fixnum>] The filtered array of bytes.
153
209
  def encode_png_scanline(filter, bytes, previous_bytes = nil, pixelsize = 3)
154
210
  case filter
155
211
  when ChunkyPNG::FILTER_NONE then encode_png_scanline_none( bytes, previous_bytes, pixelsize)
@@ -161,10 +217,17 @@ module ChunkyPNG
161
217
  end
162
218
  end
163
219
 
220
+ # Encodes the bytes of a scanline without filtering.
221
+ # @param [Array<Fixnum>] bytes The scanline bytes to encode.
222
+ # @param [Array<Fixnum>] previous_bytes The original bytes of the previous scanline.
223
+ # @param [Integer] pixelsize The number of bytes per pixel.
224
+ # @return [Array<Fixnum>] The filtered array of bytes.
164
225
  def encode_png_scanline_none(original_bytes, previous_bytes = nil, pixelsize = 3)
165
226
  [ChunkyPNG::FILTER_NONE] + original_bytes
166
227
  end
167
228
 
229
+ # Encodes the bytes of a scanline with SUB filtering.
230
+ # @param (see ChunkyPNG::Canvas::PNGEncoding#encode_png_scanline_none)
168
231
  def encode_png_scanline_sub(original_bytes, previous_bytes = nil, pixelsize = 3)
169
232
  encoded_bytes = []
170
233
  for index in 0...original_bytes.length do
@@ -174,6 +237,8 @@ module ChunkyPNG
174
237
  [ChunkyPNG::FILTER_SUB] + encoded_bytes
175
238
  end
176
239
 
240
+ # Encodes the bytes of a scanline with UP filtering.
241
+ # @param (see ChunkyPNG::Canvas::PNGEncoding#encode_png_scanline_none)
177
242
  def encode_png_scanline_up(original_bytes, previous_bytes, pixelsize = 3)
178
243
  encoded_bytes = []
179
244
  for index in 0...original_bytes.length do
@@ -182,7 +247,9 @@ module ChunkyPNG
182
247
  end
183
248
  [ChunkyPNG::FILTER_UP] + encoded_bytes
184
249
  end
185
-
250
+
251
+ # Encodes the bytes of a scanline with AVERAGE filtering.
252
+ # @param (see ChunkyPNG::Canvas::PNGEncoding#encode_png_scanline_none)
186
253
  def encode_png_scanline_average(original_bytes, previous_bytes, pixelsize = 3)
187
254
  encoded_bytes = []
188
255
  for index in 0...original_bytes.length do
@@ -192,7 +259,9 @@ module ChunkyPNG
192
259
  end
193
260
  [ChunkyPNG::FILTER_AVERAGE] + encoded_bytes
194
261
  end
195
-
262
+
263
+ # Encodes the bytes of a scanline with PAETH filtering.
264
+ # @param (see ChunkyPNG::Canvas::PNGEncoding#encode_png_scanline_none)
196
265
  def encode_png_scanline_paeth(original_bytes, previous_bytes, pixelsize = 3)
197
266
  encoded_bytes = []
198
267
  for i in 0...original_bytes.length do
@@ -130,6 +130,13 @@ module ChunkyPNG
130
130
  a(value) == 0x000000ff
131
131
  end
132
132
 
133
+ # Returns the opaque value of this color by removing the alpha channel.
134
+ # @param [Fixnum] value The color to transform.
135
+ # @return [Fixnum] The opauq color
136
+ def opaque!(value)
137
+ value | 0x000000ff
138
+ end
139
+
133
140
  # Returns true if this color is fully transparent.
134
141
  #
135
142
  # @param [Fixnum] value The color to test.
@@ -230,6 +237,25 @@ module ChunkyPNG
230
237
  new_alpha = int8_mult(a(color), factor)
231
238
  (color & 0xffffff00) | new_alpha
232
239
  end
240
+
241
+ def alpha_decomposable?(color, mask, bg, tolerance = 1)
242
+ components = decompose_alpha_components(color, mask, bg)
243
+ sum = components.inject(0) { |a,b| a + b }
244
+ max = components.max * 3
245
+ return (sum + tolerance * 3) >= max
246
+ end
247
+
248
+ def decompose_alpha(color, mask, bg)
249
+ components = decompose_alpha_components(color, mask, bg)
250
+ (components.inject(0) { |a,b| a + b } / 3.0).round
251
+ end
252
+
253
+ def decompose_alpha_components(color, mask, bg)
254
+ a_r = ((r(bg) - r(color)).to_f / (r(bg) - r(mask)).to_f * MAX).round
255
+ a_g = ((g(bg) - g(color)).to_f / (g(bg) - g(mask)).to_f * MAX).round
256
+ a_b = ((b(bg) - b(color)).to_f / (b(bg) - b(mask)).to_f * MAX).round
257
+ [a_r, a_g, a_b]
258
+ end
233
259
 
234
260
  ####################################################################
235
261
  # CONVERSIONS
@@ -91,6 +91,12 @@ describe ChunkyPNG::Color do
91
91
  end
92
92
  end
93
93
 
94
+ describe '#decompose_alpha' do
95
+ it "should decompose the alpha channel correctly" do
96
+ decompose_alpha(0x9fc2d6ff, @opaque, @white).should == 0x00000064
97
+ end
98
+ end
99
+
94
100
  describe '#blend' do
95
101
  it "should blend colors correctly" do
96
102
  blend(@opaque, @black).should == 0x05324bff
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chunky_png
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-18 00:00:00 +01:00
12
+ date: 2010-02-16 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency