chunky_png 1.3.11 → 1.3.12

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.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/.standard.yml +16 -0
  3. data/.travis.yml +5 -5
  4. data/.yardopts +1 -1
  5. data/CHANGELOG.rdoc +5 -1
  6. data/CONTRIBUTING.rdoc +17 -8
  7. data/Gemfile +3 -3
  8. data/LICENSE +1 -1
  9. data/README.md +6 -1
  10. data/Rakefile +3 -3
  11. data/benchmarks/decoding_benchmark.rb +17 -17
  12. data/benchmarks/encoding_benchmark.rb +22 -19
  13. data/benchmarks/filesize_benchmark.rb +6 -6
  14. data/bin/rake +29 -0
  15. data/bin/standardrb +29 -0
  16. data/chunky_png.gemspec +15 -15
  17. data/lib/chunky_png.rb +16 -25
  18. data/lib/chunky_png/canvas.rb +28 -27
  19. data/lib/chunky_png/canvas/adam7_interlacing.rb +14 -10
  20. data/lib/chunky_png/canvas/data_url_exporting.rb +1 -3
  21. data/lib/chunky_png/canvas/data_url_importing.rb +1 -3
  22. data/lib/chunky_png/canvas/drawing.rb +28 -43
  23. data/lib/chunky_png/canvas/masking.rb +12 -14
  24. data/lib/chunky_png/canvas/operations.rb +26 -24
  25. data/lib/chunky_png/canvas/png_decoding.rb +36 -32
  26. data/lib/chunky_png/canvas/png_encoding.rb +106 -100
  27. data/lib/chunky_png/canvas/resampling.rb +26 -33
  28. data/lib/chunky_png/canvas/stream_exporting.rb +6 -8
  29. data/lib/chunky_png/canvas/stream_importing.rb +6 -8
  30. data/lib/chunky_png/chunk.rb +69 -60
  31. data/lib/chunky_png/color.rb +211 -206
  32. data/lib/chunky_png/datastream.rb +20 -22
  33. data/lib/chunky_png/dimension.rb +16 -11
  34. data/lib/chunky_png/image.rb +9 -11
  35. data/lib/chunky_png/palette.rb +4 -9
  36. data/lib/chunky_png/point.rb +25 -26
  37. data/lib/chunky_png/rmagick.rb +8 -10
  38. data/lib/chunky_png/vector.rb +26 -29
  39. data/lib/chunky_png/version.rb +1 -1
  40. data/spec/chunky_png/canvas/adam7_interlacing_spec.rb +20 -21
  41. data/spec/chunky_png/canvas/data_url_exporting_spec.rb +8 -5
  42. data/spec/chunky_png/canvas/data_url_importing_spec.rb +5 -6
  43. data/spec/chunky_png/canvas/drawing_spec.rb +46 -38
  44. data/spec/chunky_png/canvas/masking_spec.rb +15 -16
  45. data/spec/chunky_png/canvas/operations_spec.rb +68 -67
  46. data/spec/chunky_png/canvas/png_decoding_spec.rb +37 -38
  47. data/spec/chunky_png/canvas/png_encoding_spec.rb +59 -50
  48. data/spec/chunky_png/canvas/resampling_spec.rb +19 -21
  49. data/spec/chunky_png/canvas/stream_exporting_spec.rb +47 -27
  50. data/spec/chunky_png/canvas/stream_importing_spec.rb +10 -11
  51. data/spec/chunky_png/canvas_spec.rb +57 -52
  52. data/spec/chunky_png/color_spec.rb +115 -114
  53. data/spec/chunky_png/datastream_spec.rb +49 -51
  54. data/spec/chunky_png/dimension_spec.rb +10 -10
  55. data/spec/chunky_png/image_spec.rb +11 -14
  56. data/spec/chunky_png/point_spec.rb +21 -23
  57. data/spec/chunky_png/rmagick_spec.rb +7 -8
  58. data/spec/chunky_png/vector_spec.rb +21 -17
  59. data/spec/chunky_png_spec.rb +2 -2
  60. data/spec/png_suite_spec.rb +35 -40
  61. data/spec/spec_helper.rb +6 -10
  62. data/tasks/benchmarks.rake +7 -8
  63. metadata +34 -5
  64. data/lib/chunky_png/compatibility.rb +0 -15
@@ -15,8 +15,8 @@ module ChunkyPNG
15
15
  # the current instance intact, use {#grayscale} instead.
16
16
  #
17
17
  # @return [ChunkyPNG::Canvas] Returns itself, converted to grayscale.
18
- # @see {#grayscale}
19
- # @see {ChunkyPNG::Color#to_grayscale}
18
+ # @see #grayscale
19
+ # @see ChunkyPNG::Color#to_grayscale
20
20
  def grayscale!
21
21
  pixels.map! { |pixel| ChunkyPNG::Color.to_grayscale(pixel) }
22
22
  self
@@ -29,8 +29,8 @@ module ChunkyPNG
29
29
  #
30
30
  # @return [ChunkyPNG::Canvas] A copy of the canvas, converted to
31
31
  # grayscale.
32
- # @see {#grayscale!}
33
- # @see {ChunkyPNG::Color#to_grayscale}
32
+ # @see #grayscale!
33
+ # @see ChunkyPNG::Color#to_grayscale
34
34
  def grayscale
35
35
  dup.grayscale!
36
36
  end
@@ -56,11 +56,14 @@ module ChunkyPNG
56
56
 
57
57
  for y in 0...other.height do
58
58
  for x in 0...other.width do
59
- set_pixel(x + offset_x,
60
- y + offset_y,
61
- ChunkyPNG::Color.compose(other.get_pixel(x, y),
62
- get_pixel(x + offset_x,
63
- y + offset_y)))
59
+ set_pixel(
60
+ x + offset_x,
61
+ y + offset_y,
62
+ ChunkyPNG::Color.compose(
63
+ other.get_pixel(x, y),
64
+ get_pixel(x + offset_x, y + offset_y)
65
+ )
66
+ )
64
67
  end
65
68
  end
66
69
  self
@@ -169,17 +172,16 @@ module ChunkyPNG
169
172
  # coordinates are bigger then the original image.
170
173
  def crop!(x, y, crop_width, crop_height)
171
174
  if crop_width + x > width
172
- raise ChunkyPNG::OutOfBounds, 'Original image width is too small!'
175
+ raise ChunkyPNG::OutOfBounds, "Original image width is too small!"
173
176
  end
174
177
  if crop_height + y > height
175
- raise ChunkyPNG::OutOfBounds, 'Original image height is too small!'
178
+ raise ChunkyPNG::OutOfBounds, "Original image height is too small!"
176
179
  end
177
180
 
178
181
  if crop_width == width && x == 0
179
182
  # We only need to crop off the top and/or bottom, so we can take a
180
183
  # shortcut.
181
- replace_canvas!(crop_width, crop_height,
182
- pixels.slice(y * width, width * crop_height))
184
+ replace_canvas!(crop_width, crop_height, pixels.slice(y * width, width * crop_height))
183
185
  else
184
186
  new_pixels = []
185
187
  for cy in 0...crop_height do
@@ -221,8 +223,8 @@ module ChunkyPNG
221
223
  self
222
224
  end
223
225
 
224
- alias_method :flip!, :flip_horizontally!
225
- alias_method :flip, :flip_horizontally
226
+ alias flip! flip_horizontally!
227
+ alias flip flip_horizontally
226
228
 
227
229
  # Flips the image vertically, leaving the original intact.
228
230
  #
@@ -253,8 +255,8 @@ module ChunkyPNG
253
255
  self
254
256
  end
255
257
 
256
- alias_method :mirror!, :flip_vertically!
257
- alias_method :mirror, :flip_vertically
258
+ alias mirror! flip_vertically!
259
+ alias mirror flip_vertically
258
260
 
259
261
  # Returns a new canvas instance that is rotated 90 degrees clockwise.
260
262
  #
@@ -278,8 +280,8 @@ module ChunkyPNG
278
280
  replace_canvas!(height, width, new_pixels)
279
281
  end
280
282
 
281
- alias_method :rotate_clockwise, :rotate_right
282
- alias_method :rotate_clockwise!, :rotate_right!
283
+ alias rotate_clockwise rotate_right
284
+ alias rotate_clockwise! rotate_right!
283
285
 
284
286
  # Returns an image that is rotated 90 degrees counter-clockwise.
285
287
  #
@@ -305,8 +307,8 @@ module ChunkyPNG
305
307
  replace_canvas!(height, width, new_pixels)
306
308
  end
307
309
 
308
- alias_method :rotate_counter_clockwise, :rotate_left
309
- alias_method :rotate_counter_clockwise!, :rotate_left!
310
+ alias rotate_counter_clockwise rotate_left
311
+ alias rotate_counter_clockwise! rotate_left!
310
312
 
311
313
  # Rotates the image 180 degrees.
312
314
  #
@@ -389,11 +391,11 @@ module ChunkyPNG
389
391
  # applied.
390
392
  # @raise [ChunkyPNG::OutOfBounds] when the other image doesn't fit.
391
393
  def check_size_constraints!(other, offset_x, offset_y)
392
- if width < other.width + offset_x
393
- raise ChunkyPNG::OutOfBounds, 'Background image width is too small!'
394
+ if width < other.width + offset_x
395
+ raise ChunkyPNG::OutOfBounds, "Background image width is too small!"
394
396
  end
395
397
  if height < other.height + offset_y
396
- raise ChunkyPNG::OutOfBounds, 'Background image height is too small!'
398
+ raise ChunkyPNG::OutOfBounds, "Background image height is too small!"
397
399
  end
398
400
  end
399
401
  end
@@ -1,6 +1,5 @@
1
1
  module ChunkyPNG
2
2
  class Canvas
3
-
4
3
  # The PNGDecoding contains methods for decoding PNG datastreams to create a
5
4
  # Canvas object. The datastream can be provided as filename, string or IO
6
5
  # stream.
@@ -28,7 +27,6 @@ module ChunkyPNG
28
27
  # @see ChunkyPNG::Canvas::PNGEncoding
29
28
  # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification
30
29
  module PNGDecoding
31
-
32
30
  # Decodes a Canvas from a PNG encoded string.
33
31
  # @param [String] str The string to read from.
34
32
  # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG encoded string.
@@ -36,7 +34,7 @@ module ChunkyPNG
36
34
  from_datastream(ChunkyPNG::Datastream.from_blob(str))
37
35
  end
38
36
 
39
- alias_method :from_string, :from_blob
37
+ alias from_string from_blob
40
38
 
41
39
  # Decodes a Canvas from a PNG encoded file.
42
40
  # @param [String] filename The file to read from.
@@ -52,7 +50,7 @@ module ChunkyPNG
52
50
  from_datastream(ChunkyPNG::Datastream.from_io(io))
53
51
  end
54
52
 
55
- alias_method :from_stream, :from_io
53
+ alias from_stream from_io
56
54
 
57
55
  # Decodes the Canvas from a PNG datastream instance.
58
56
  # @param [ChunkyPNG::Datastream] ds The datastream to decode.
@@ -85,7 +83,7 @@ module ChunkyPNG
85
83
  # color mode and interlacing mode.
86
84
  # @param [String] stream The pixelstream to read from.
87
85
  # @param [Integer] width The width of the image.
88
- # @param [Integer] width The height of the image.
86
+ # @param [Integer] height The height of the image.
89
87
  # @param [Integer] color_mode The color mode of the encoded pixelstream.
90
88
  # @param [Integer] depth The bit depth of the pixel samples.
91
89
  # @param [Integer] interlace The interlace method of the encoded pixelstream.
@@ -96,13 +94,13 @@ module ChunkyPNG
96
94
  raise ChunkyPNG::ExpectationFailed, "This palette is not suitable for decoding!" if decoding_palette && !decoding_palette.can_decode?
97
95
 
98
96
  image = case interlace
99
- when ChunkyPNG::INTERLACING_NONE; decode_png_without_interlacing(stream, width, height, color_mode, depth, decoding_palette)
100
- when ChunkyPNG::INTERLACING_ADAM7; decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth, decoding_palette)
97
+ when ChunkyPNG::INTERLACING_NONE then decode_png_without_interlacing(stream, width, height, color_mode, depth, decoding_palette)
98
+ when ChunkyPNG::INTERLACING_ADAM7 then decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth, decoding_palette)
101
99
  else raise ChunkyPNG::NotSupported, "Don't know how the handle interlacing method #{interlace}!"
102
100
  end
103
101
 
104
102
  image.pixels.map! { |c| c == transparent_color ? ChunkyPNG::Color::TRANSPARENT : c } if transparent_color
105
- return image
103
+ image
106
104
  end
107
105
 
108
106
  protected
@@ -147,7 +145,7 @@ module ChunkyPNG
147
145
  # the value will be modded by 2 to enforce this.
148
146
  # @return [Integer] The extracted 4bit value (0..15)
149
147
  def decode_png_extract_4bit_value(byte, index)
150
- (index & 0x01 == 0) ? ((byte & 0xf0) >> 4) : (byte & 0x0f)
148
+ index & 0x01 == 0 ? ((byte & 0xf0) >> 4) : (byte & 0x0f)
151
149
  end
152
150
 
153
151
  # Extract 2 consecutive bits from a byte.
@@ -205,7 +203,6 @@ module ChunkyPNG
205
203
  value == 0x01 ? 0xff : 0x00
206
204
  end
207
205
 
208
-
209
206
  # Decodes a scanline of a 1-bit, indexed image into a row of pixels.
210
207
  # @param [String] stream The stream to decode from.
211
208
  # @param [Integer] pos The position in the stream on which the scanline starts (including the filter byte).
@@ -259,17 +256,21 @@ module ChunkyPNG
259
256
  def decode_png_pixels_from_scanline_truecolor_alpha_16bit(stream, pos, width, _decoding_palette)
260
257
  pixels = []
261
258
  stream.unpack("@#{pos + 1}n#{width * 4}").each_slice(4) do |r, g, b, a|
262
- pixels << ChunkyPNG::Color.rgba(decode_png_resample_16bit_value(r), decode_png_resample_16bit_value(g),
263
- decode_png_resample_16bit_value(b), decode_png_resample_16bit_value(a))
259
+ pixels << ChunkyPNG::Color.rgba(
260
+ decode_png_resample_16bit_value(r),
261
+ decode_png_resample_16bit_value(g),
262
+ decode_png_resample_16bit_value(b),
263
+ decode_png_resample_16bit_value(a),
264
+ )
264
265
  end
265
- return pixels
266
+ pixels
266
267
  end
267
268
 
268
269
  # Decodes a scanline of an 8-bit, true color image into a row of pixels.
269
270
  # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
270
271
  # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
271
272
  def decode_png_pixels_from_scanline_truecolor_8bit(stream, pos, width, _decoding_palette)
272
- stream.unpack("@#{pos + 1}" << ('NX' * width)).map { |c| c | 0x000000ff }
273
+ stream.unpack("@#{pos + 1}" << ("NX" * width)).map { |c| c | 0x000000ff }
273
274
  end
274
275
 
275
276
  # Decodes a scanline of a 16-bit, true color image into a row of pixels.
@@ -280,7 +281,7 @@ module ChunkyPNG
280
281
  stream.unpack("@#{pos + 1}n#{width * 3}").each_slice(3) do |r, g, b|
281
282
  pixels << ChunkyPNG::Color.rgb(decode_png_resample_16bit_value(r), decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(b))
282
283
  end
283
- return pixels
284
+ pixels
284
285
  end
285
286
 
286
287
  # Decodes a scanline of an 8-bit, grayscale image with transparency into a row of pixels.
@@ -298,7 +299,7 @@ module ChunkyPNG
298
299
  stream.unpack("@#{pos + 1}n#{width * 2}").each_slice(2) do |g, a|
299
300
  pixels << ChunkyPNG::Color.grayscale_alpha(decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(a))
300
301
  end
301
- return pixels
302
+ pixels
302
303
  end
303
304
 
304
305
  # Decodes a scanline of a 1-bit, grayscale image into a row of pixels.
@@ -353,12 +354,11 @@ module ChunkyPNG
353
354
  # @raise [ChunkyPNG::NotSupported] when the color_mode and/or bit depth is not supported.
354
355
  def decode_png_pixels_from_scanline_method(color_mode, depth)
355
356
  decoder_method = case color_mode
356
- when ChunkyPNG::COLOR_TRUECOLOR; :"decode_png_pixels_from_scanline_truecolor_#{depth}bit"
357
- when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; :"decode_png_pixels_from_scanline_truecolor_alpha_#{depth}bit"
358
- when ChunkyPNG::COLOR_INDEXED; :"decode_png_pixels_from_scanline_indexed_#{depth}bit"
359
- when ChunkyPNG::COLOR_GRAYSCALE; :"decode_png_pixels_from_scanline_grayscale_#{depth}bit"
360
- when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; :"decode_png_pixels_from_scanline_grayscale_alpha_#{depth}bit"
361
- else nil
357
+ when ChunkyPNG::COLOR_TRUECOLOR then :"decode_png_pixels_from_scanline_truecolor_#{depth}bit"
358
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then :"decode_png_pixels_from_scanline_truecolor_alpha_#{depth}bit"
359
+ when ChunkyPNG::COLOR_INDEXED then :"decode_png_pixels_from_scanline_indexed_#{depth}bit"
360
+ when ChunkyPNG::COLOR_GRAYSCALE then :"decode_png_pixels_from_scanline_grayscale_#{depth}bit"
361
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then :"decode_png_pixels_from_scanline_grayscale_alpha_#{depth}bit"
362
362
  end
363
363
 
364
364
  raise ChunkyPNG::NotSupported, "No decoder found for color mode #{color_mode} and #{depth}-bit depth!" unless respond_to?(decoder_method, true)
@@ -379,7 +379,6 @@ module ChunkyPNG
379
379
  # @param [ChunkyPNG::Palette] decoding_palette The palette to use to decode colors.
380
380
  # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
381
381
  def decode_png_image_pass(stream, width, height, color_mode, depth, start_pos, decoding_palette)
382
-
383
382
  pixels = []
384
383
  if width > 0 && height > 0
385
384
 
@@ -419,11 +418,11 @@ module ChunkyPNG
419
418
  # @return [void]
420
419
  def decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size)
421
420
  case stream.getbyte(pos)
422
- when ChunkyPNG::FILTER_NONE; # noop
423
- when ChunkyPNG::FILTER_SUB; decode_png_str_scanline_sub( stream, pos, prev_pos, line_length, pixel_size)
424
- when ChunkyPNG::FILTER_UP; decode_png_str_scanline_up( stream, pos, prev_pos, line_length, pixel_size)
425
- when ChunkyPNG::FILTER_AVERAGE; decode_png_str_scanline_average( stream, pos, prev_pos, line_length, pixel_size)
426
- when ChunkyPNG::FILTER_PAETH; decode_png_str_scanline_paeth( stream, pos, prev_pos, line_length, pixel_size)
421
+ when ChunkyPNG::FILTER_NONE then # rubocop:disable Lint/EmptyWhen # no-op
422
+ when ChunkyPNG::FILTER_SUB then decode_png_str_scanline_sub(stream, pos, prev_pos, line_length, pixel_size)
423
+ when ChunkyPNG::FILTER_UP then decode_png_str_scanline_up(stream, pos, prev_pos, line_length, pixel_size)
424
+ when ChunkyPNG::FILTER_AVERAGE then decode_png_str_scanline_average(stream, pos, prev_pos, line_length, pixel_size)
425
+ when ChunkyPNG::FILTER_PAETH then decode_png_str_scanline_paeth(stream, pos, prev_pos, line_length, pixel_size)
427
426
  else raise ChunkyPNG::NotSupported, "Unknown filter type: #{stream.getbyte(pos)}!"
428
427
  end
429
428
  end
@@ -462,7 +461,7 @@ module ChunkyPNG
462
461
  # @return [void]
463
462
  def decode_png_str_scanline_average(stream, pos, prev_pos, line_length, pixel_size)
464
463
  for i in 1..line_length do
465
- a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0
464
+ a = i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0
466
465
  b = prev_pos ? stream.getbyte(prev_pos + i) : 0
467
466
  stream.setbyte(pos + i, (stream.getbyte(pos + i) + ((a + b) >> 1)) & 0xff)
468
467
  end
@@ -475,14 +474,19 @@ module ChunkyPNG
475
474
  def decode_png_str_scanline_paeth(stream, pos, prev_pos, line_length, pixel_size)
476
475
  for i in 1..line_length do
477
476
  cur_pos = pos + i
478
- a = (i > pixel_size) ? stream.getbyte(cur_pos - pixel_size) : 0
477
+ a = i > pixel_size ? stream.getbyte(cur_pos - pixel_size) : 0
479
478
  b = prev_pos ? stream.getbyte(prev_pos + i) : 0
480
- c = (prev_pos && i > pixel_size) ? stream.getbyte(prev_pos + i - pixel_size) : 0
479
+ c = prev_pos && i > pixel_size ? stream.getbyte(prev_pos + i - pixel_size) : 0
481
480
  p = a + b - c
482
481
  pa = (p - a).abs
483
482
  pb = (p - b).abs
484
483
  pc = (p - c).abs
485
- pr = (pa <= pb) ? (pa <= pc ? a : c) : (pb <= pc ? b : c)
484
+ pr = if pa <= pb
485
+ pa <= pc ? a : c
486
+ else
487
+ pb <= pc ? b : c
488
+ end
489
+
486
490
  stream.setbyte(cur_pos, (stream.getbyte(cur_pos) + pr) & 0xff)
487
491
  end
488
492
  end
@@ -1,17 +1,16 @@
1
1
  module ChunkyPNG
2
2
  class Canvas
3
-
4
3
  # Methods for encoding a Canvas instance into a PNG datastream.
5
4
  #
6
5
  # Overview of the encoding process:
7
6
  #
8
7
  # * The image is split up in scanlines (i.e. rows of pixels);
9
8
  # * All pixels are encoded as a pixelstream, based on the color mode.
10
- # * All the pixel bytes in the pixelstream are adjusted using a filtering
9
+ # * All the pixel bytes in the pixelstream are adjusted using a filtering
11
10
  # method if one is specified.
12
11
  # * Compress the resulting string using deflate compression.
13
12
  # * Split compressed data over one or more PNG chunks.
14
- # * These chunks should be embedded in a datastream with at least a IHDR and
13
+ # * These chunks should be embedded in a datastream with at least a IHDR and
15
14
  # IEND chunk and possibly a PLTE chunk.
16
15
  #
17
16
  # For interlaced images, the initial image is first split into 7 subimages.
@@ -21,7 +20,6 @@ module ChunkyPNG
21
20
  # @see ChunkyPNG::Canvas::PNGDecoding
22
21
  # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification
23
22
  module PNGEncoding
24
-
25
23
  # The palette used for encoding the image.This is only in used for images
26
24
  # that get encoded using indexed colors.
27
25
  # @return [ChunkyPNG::Palette]
@@ -40,28 +38,28 @@ module ChunkyPNG
40
38
  # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream)
41
39
  # @return [void]
42
40
  def save(filename, constraints = {})
43
- File.open(filename, 'wb') { |io| write(io, constraints) }
41
+ File.open(filename, "wb") { |io| write(io, constraints) }
44
42
  end
45
-
43
+
46
44
  # Encoded the canvas to a PNG formatted string.
47
45
  # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream)
48
46
  # @return [String] The PNG encoded canvas as string.
49
47
  def to_blob(constraints = {})
50
48
  to_datastream(constraints).to_blob
51
49
  end
52
-
53
- alias_method :to_string, :to_blob
54
- alias_method :to_s, :to_blob
50
+
51
+ alias to_string to_blob
52
+ alias to_s to_blob
55
53
 
56
54
  # Converts this Canvas to a datastream, so that it can be saved as a PNG image.
57
55
  # @param [Hash, Symbol] constraints The constraints to use when encoding the canvas.
58
- # This can either be a hash with different constraints, or a symbol which acts as a
59
- # preset for some constraints. If no constraints are given, ChunkyPNG will decide
60
- # for itself how to best create the PNG datastream.
56
+ # This can either be a hash with different constraints, or a symbol which acts as a
57
+ # preset for some constraints. If no constraints are given, ChunkyPNG will decide
58
+ # for itself how to best create the PNG datastream.
61
59
  # Supported presets are <tt>:fast_rgba</tt> for quickly saving images with transparency,
62
60
  # <tt>:fast_rgb</tt> for quickly saving opaque images, and <tt>:best_compression</tt> to
63
61
  # obtain the smallest possible filesize.
64
- # @option constraints [Fixnum] :color_mode The color mode to use. Use one of the
62
+ # @option constraints [Fixnum] :color_mode The color mode to use. Use one of the
65
63
  # ChunkyPNG::COLOR_* constants.
66
64
  # @option constraints [true, false] :interlace Whether to use interlacing.
67
65
  # @option constraints [Fixnum] :compression The compression level for Zlib. This can be a
@@ -75,8 +73,13 @@ module ChunkyPNG
75
73
  encoding = determine_png_encoding(constraints)
76
74
 
77
75
  ds = Datastream.new
78
- ds.header_chunk = Chunk::Header.new(:width => width, :height => height,
79
- :color => encoding[:color_mode], :depth => encoding[:bit_depth], :interlace => encoding[:interlace])
76
+ ds.header_chunk = Chunk::Header.new(
77
+ width: width,
78
+ height: height,
79
+ color: encoding[:color_mode],
80
+ depth: encoding[:bit_depth],
81
+ interlace: encoding[:interlace]
82
+ )
80
83
 
81
84
  if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED
82
85
  ds.palette_chunk = encoding_palette.to_plte_chunk
@@ -85,43 +88,42 @@ module ChunkyPNG
85
88
  data = encode_png_pixelstream(encoding[:color_mode], encoding[:bit_depth], encoding[:interlace], encoding[:filtering])
86
89
  ds.data_chunks = Chunk::ImageData.split_in_chunks(data, encoding[:compression])
87
90
  ds.end_chunk = Chunk::End.new
88
- return ds
91
+ ds
89
92
  end
90
93
 
91
94
  protected
92
95
 
93
- # Determines the best possible PNG encoding variables for this image, by analyzing
96
+ # Determines the best possible PNG encoding variables for this image, by analyzing
94
97
  # the colors used for the image.
95
98
  #
96
- # You can provide constraints for the encoding variables by passing a hash with
99
+ # You can provide constraints for the encoding variables by passing a hash with
97
100
  # encoding variables to this method.
98
101
  #
99
102
  # @param [Hash, Symbol] constraints The constraints for the encoding. This can be a
100
103
  # Hash or a preset symbol.
101
104
  # @return [Hash] A hash with encoding options for {ChunkyPNG::Canvas::PNGEncoding#to_datastream}
102
105
  def determine_png_encoding(constraints = {})
103
-
104
106
  encoding = case constraints
105
- when :fast_rgb; { :color_mode => ChunkyPNG::COLOR_TRUECOLOR, :compression => Zlib::BEST_SPEED }
106
- when :fast_rgba; { :color_mode => ChunkyPNG::COLOR_TRUECOLOR_ALPHA, :compression => Zlib::BEST_SPEED }
107
- when :best_compression; { :compression => Zlib::BEST_COMPRESSION, :filtering => ChunkyPNG::FILTER_PAETH }
108
- when :good_compression; { :compression => Zlib::BEST_COMPRESSION, :filtering => ChunkyPNG::FILTER_NONE }
109
- when :no_compression; { :compression => Zlib::NO_COMPRESSION }
110
- when :black_and_white; { :color_mode => ChunkyPNG::COLOR_GRAYSCALE, :bit_depth => 1 }
111
- when Hash; constraints
107
+ when :fast_rgb then {color_mode: ChunkyPNG::COLOR_TRUECOLOR, compression: Zlib::BEST_SPEED}
108
+ when :fast_rgba then {color_mode: ChunkyPNG::COLOR_TRUECOLOR_ALPHA, compression: Zlib::BEST_SPEED}
109
+ when :best_compression then {compression: Zlib::BEST_COMPRESSION, filtering: ChunkyPNG::FILTER_PAETH}
110
+ when :good_compression then {compression: Zlib::BEST_COMPRESSION, filtering: ChunkyPNG::FILTER_NONE}
111
+ when :no_compression then {compression: Zlib::NO_COMPRESSION}
112
+ when :black_and_white then {color_mode: ChunkyPNG::COLOR_GRAYSCALE, bit_depth: 1}
113
+ when Hash then constraints
112
114
  else raise ChunkyPNG::Exception, "Unknown encoding preset: #{constraints.inspect}"
113
115
  end
114
116
 
115
117
  # Do not create a palette when the encoding is given and does not require a palette.
116
118
  if encoding[:color_mode]
117
119
  if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED
118
- self.encoding_palette = self.palette
119
- encoding[:bit_depth] ||= self.encoding_palette.determine_bit_depth
120
+ self.encoding_palette = palette
121
+ encoding[:bit_depth] ||= encoding_palette.determine_bit_depth
120
122
  else
121
123
  encoding[:bit_depth] ||= 8
122
124
  end
123
125
  else
124
- self.encoding_palette = self.palette
126
+ self.encoding_palette = palette
125
127
  suggested_color_mode, suggested_bit_depth = encoding_palette.best_color_settings
126
128
  encoding[:color_mode] ||= suggested_color_mode
127
129
  encoding[:bit_depth] ||= suggested_bit_depth
@@ -131,35 +133,34 @@ module ChunkyPNG
131
133
  encoding[:compression] ||= Zlib::DEFAULT_COMPRESSION
132
134
 
133
135
  encoding[:interlace] = case encoding[:interlace]
134
- when nil, false, ChunkyPNG::INTERLACING_NONE; ChunkyPNG::INTERLACING_NONE
135
- when true, ChunkyPNG::INTERLACING_ADAM7; ChunkyPNG::INTERLACING_ADAM7
136
+ when nil, false then ChunkyPNG::INTERLACING_NONE
137
+ when true then ChunkyPNG::INTERLACING_ADAM7
136
138
  else encoding[:interlace]
137
139
  end
138
140
 
139
141
  encoding[:filtering] ||= case encoding[:compression]
140
- when Zlib::BEST_COMPRESSION; ChunkyPNG::FILTER_PAETH
141
- when Zlib::NO_COMPRESSION..Zlib::BEST_SPEED; ChunkyPNG::FILTER_NONE
142
+ when Zlib::BEST_COMPRESSION then ChunkyPNG::FILTER_PAETH
143
+ when Zlib::NO_COMPRESSION..Zlib::BEST_SPEED then ChunkyPNG::FILTER_NONE
142
144
  else ChunkyPNG::FILTER_UP
143
145
  end
144
- return encoding
146
+ encoding
145
147
  end
146
-
147
- # Encodes the canvas according to the PNG format specification with a given color
148
+
149
+ # Encodes the canvas according to the PNG format specification with a given color
148
150
  # mode, possibly with interlacing.
149
151
  # @param [Integer] color_mode The color mode to use for encoding.
150
152
  # @param [Integer] bit_depth The bit depth of the image.
151
153
  # @param [Integer] interlace The interlacing method to use.
152
154
  # @return [String] The PNG encoded canvas as string.
153
155
  def encode_png_pixelstream(color_mode = ChunkyPNG::COLOR_TRUECOLOR, bit_depth = 8, interlace = ChunkyPNG::INTERLACING_NONE, filtering = ChunkyPNG::FILTER_NONE)
154
-
155
- if color_mode == ChunkyPNG::COLOR_INDEXED
156
+ if color_mode == ChunkyPNG::COLOR_INDEXED
156
157
  raise ChunkyPNG::ExpectationFailed, "This palette is not suitable for encoding!" if encoding_palette.nil? || !encoding_palette.can_encode?
157
158
  raise ChunkyPNG::ExpectationFailed, "This palette has too many colors!" if encoding_palette.size > (1 << bit_depth)
158
159
  end
159
160
 
160
161
  case interlace
161
- when ChunkyPNG::INTERLACING_NONE; encode_png_image_without_interlacing(color_mode, bit_depth, filtering)
162
- when ChunkyPNG::INTERLACING_ADAM7; encode_png_image_with_interlacing(color_mode, bit_depth, filtering)
162
+ when ChunkyPNG::INTERLACING_NONE then encode_png_image_without_interlacing(color_mode, bit_depth, filtering)
163
+ when ChunkyPNG::INTERLACING_ADAM7 then encode_png_image_with_interlacing(color_mode, bit_depth, filtering)
163
164
  else raise ChunkyPNG::NotSupported, "Unknown interlacing method: #{interlace}!"
164
165
  end
165
166
  end
@@ -175,7 +176,7 @@ module ChunkyPNG
175
176
  stream
176
177
  end
177
178
 
178
- # Encodes the canvas according to the PNG format specification with a given color
179
+ # Encodes the canvas according to the PNG format specification with a given color
179
180
  # mode and Adam7 interlacing.
180
181
  #
181
182
  # This method will split the original canvas in 7 smaller canvases and encode them
@@ -201,42 +202,42 @@ module ChunkyPNG
201
202
  # @param [Integer] bit_depth The bit depth of the image.
202
203
  # @param [Integer] filtering The filtering method to use.
203
204
  def encode_png_image_pass_to_stream(stream, color_mode, bit_depth, filtering)
204
-
205
205
  start_pos = stream.bytesize
206
206
  pixel_size = Color.pixel_bytesize(color_mode)
207
207
  line_width = Color.scanline_bytesize(color_mode, bit_depth, width)
208
-
208
+
209
209
  # Determine the filter method
210
210
  encode_method = encode_png_pixels_to_scanline_method(color_mode, bit_depth)
211
211
  filter_method = case filtering
212
- when ChunkyPNG::FILTER_SUB; :encode_png_str_scanline_sub
213
- when ChunkyPNG::FILTER_UP; :encode_png_str_scanline_up
214
- when ChunkyPNG::FILTER_AVERAGE; :encode_png_str_scanline_average
215
- when ChunkyPNG::FILTER_PAETH; :encode_png_str_scanline_paeth
216
- else nil
212
+ when ChunkyPNG::FILTER_NONE then nil
213
+ when ChunkyPNG::FILTER_SUB then :encode_png_str_scanline_sub
214
+ when ChunkyPNG::FILTER_UP then :encode_png_str_scanline_up
215
+ when ChunkyPNG::FILTER_AVERAGE then :encode_png_str_scanline_average
216
+ when ChunkyPNG::FILTER_PAETH then :encode_png_str_scanline_paeth
217
+ else raise ArgumentError, "Filtering method #{filtering} is not supported"
217
218
  end
218
-
219
+
219
220
  0.upto(height - 1) do |y|
220
221
  stream << send(encode_method, row(y))
221
222
  end
222
-
223
+
223
224
  # Now, apply filtering if any
224
225
  if filter_method
225
226
  (height - 1).downto(0) do |y|
226
227
  pos = start_pos + y * (line_width + 1)
227
- prev_pos = (y == 0) ? nil : pos - (line_width + 1)
228
+ prev_pos = y == 0 ? nil : pos - (line_width + 1)
228
229
  send(filter_method, stream, pos, prev_pos, line_width, pixel_size)
229
230
  end
230
231
  end
231
232
  end
232
-
233
+
233
234
  # Encodes a line of pixels using 8-bit truecolor mode.
234
235
  # @param [Array<Integer>] pixels A row of pixels of the original image.
235
236
  # @return [String] The encoded scanline as binary string
236
237
  def encode_png_pixels_to_scanline_truecolor_8bit(pixels)
237
- pixels.pack('x' + ('NX' * width))
238
+ pixels.pack("x" + ("NX" * width))
238
239
  end
239
-
240
+
240
241
  # Encodes a line of pixels using 8-bit truecolor alpha mode.
241
242
  # @param [Array<Integer>] pixels A row of pixels of the original image.
242
243
  # @return [String] The encoded scanline as binary string
@@ -250,50 +251,54 @@ module ChunkyPNG
250
251
  def encode_png_pixels_to_scanline_indexed_1bit(pixels)
251
252
  chars = []
252
253
  pixels.each_slice(8) do |p1, p2, p3, p4, p5, p6, p7, p8|
253
- chars << ((encoding_palette.index(p1) << 7) |
254
- (encoding_palette.index(p2) << 6) |
255
- (encoding_palette.index(p3) << 5) |
256
- (encoding_palette.index(p4) << 4) |
257
- (encoding_palette.index(p5) << 3) |
258
- (encoding_palette.index(p6) << 2) |
259
- (encoding_palette.index(p7) << 1) |
260
- (encoding_palette.index(p8)))
254
+ chars << (
255
+ (encoding_palette.index(p1) << 7) |
256
+ (encoding_palette.index(p2) << 6) |
257
+ (encoding_palette.index(p3) << 5) |
258
+ (encoding_palette.index(p4) << 4) |
259
+ (encoding_palette.index(p5) << 3) |
260
+ (encoding_palette.index(p6) << 2) |
261
+ (encoding_palette.index(p7) << 1) |
262
+ encoding_palette.index(p8)
263
+ )
261
264
  end
262
- chars.pack('xC*')
265
+ chars.pack("xC*")
263
266
  end
264
-
267
+
265
268
  # Encodes a line of pixels using 2-bit indexed mode.
266
269
  # @param [Array<Integer>] pixels A row of pixels of the original image.
267
270
  # @return [String] The encoded scanline as binary string
268
271
  def encode_png_pixels_to_scanline_indexed_2bit(pixels)
269
272
  chars = []
270
273
  pixels.each_slice(4) do |p1, p2, p3, p4|
271
- chars << ((encoding_palette.index(p1) << 6) |
272
- (encoding_palette.index(p2) << 4) |
273
- (encoding_palette.index(p3) << 2) |
274
- (encoding_palette.index(p4)))
274
+ chars << (
275
+ (encoding_palette.index(p1) << 6) |
276
+ (encoding_palette.index(p2) << 4) |
277
+ (encoding_palette.index(p3) << 2) |
278
+ encoding_palette.index(p4)
279
+ )
275
280
  end
276
- chars.pack('xC*')
281
+ chars.pack("xC*")
277
282
  end
278
-
283
+
279
284
  # Encodes a line of pixels using 4-bit indexed mode.
280
285
  # @param [Array<Integer>] pixels A row of pixels of the original image.
281
286
  # @return [String] The encoded scanline as binary string
282
287
  def encode_png_pixels_to_scanline_indexed_4bit(pixels)
283
288
  chars = []
284
289
  pixels.each_slice(2) do |p1, p2|
285
- chars << ((encoding_palette.index(p1) << 4) | (encoding_palette.index(p2)))
290
+ chars << ((encoding_palette.index(p1) << 4) | encoding_palette.index(p2))
286
291
  end
287
- chars.pack('xC*')
292
+ chars.pack("xC*")
288
293
  end
289
-
294
+
290
295
  # Encodes a line of pixels using 8-bit indexed mode.
291
296
  # @param [Array<Integer>] pixels A row of pixels of the original image.
292
297
  # @return [String] The encoded scanline as binary string
293
298
  def encode_png_pixels_to_scanline_indexed_8bit(pixels)
294
299
  pixels.map { |p| encoding_palette.index(p) }.pack("xC#{width}")
295
300
  end
296
-
301
+
297
302
  # Encodes a line of pixels using 1-bit grayscale mode.
298
303
  # @param [Array<Integer>] pixels A row of pixels of the original image.
299
304
  # @return [String] The encoded scanline as binary string
@@ -309,9 +314,9 @@ module ChunkyPNG
309
314
  (p7.nil? ? 0 : (p7 & 0x0000ffff) >> 15 << 1) |
310
315
  (p8.nil? ? 0 : (p8 & 0x0000ffff) >> 15))
311
316
  end
312
- chars.pack('xC*')
317
+ chars.pack("xC*")
313
318
  end
314
-
319
+
315
320
  # Encodes a line of pixels using 2-bit grayscale mode.
316
321
  # @param [Array<Integer>] pixels A row of pixels of the original image.
317
322
  # @return [String] The encoded scanline as binary string
@@ -323,9 +328,9 @@ module ChunkyPNG
323
328
  (p3.nil? ? 0 : (p3 & 0x0000ffff) >> 14 << 2) |
324
329
  (p4.nil? ? 0 : (p4 & 0x0000ffff) >> 14))
325
330
  end
326
- chars.pack('xC*')
331
+ chars.pack("xC*")
327
332
  end
328
-
333
+
329
334
  # Encodes a line of pixels using 2-bit grayscale mode.
330
335
  # @param [Array<Integer>] pixels A row of pixels of the original image.
331
336
  # @return [String] The encoded scanline as binary string
@@ -334,9 +339,9 @@ module ChunkyPNG
334
339
  pixels.each_slice(2) do |p1, p2|
335
340
  chars << ((p1.nil? ? 0 : ((p1 & 0x0000ffff) >> 12) << 4) | (p2.nil? ? 0 : ((p2 & 0x0000ffff) >> 12)))
336
341
  end
337
- chars.pack('xC*')
342
+ chars.pack("xC*")
338
343
  end
339
-
344
+
340
345
  # Encodes a line of pixels using 8-bit grayscale mode.
341
346
  # @param [Array<Integer>] pixels A row of pixels of the original image.
342
347
  # @return [String] The encoded scanline as binary string
@@ -350,8 +355,7 @@ module ChunkyPNG
350
355
  def encode_png_pixels_to_scanline_grayscale_alpha_8bit(pixels)
351
356
  pixels.pack("xn#{width}")
352
357
  end
353
-
354
-
358
+
355
359
  # Returns the method name to use to decode scanlines into pixels.
356
360
  # @param [Integer] color_mode The color mode of the image.
357
361
  # @param [Integer] depth The bit depth of the image.
@@ -359,19 +363,16 @@ module ChunkyPNG
359
363
  # @raise [ChunkyPNG::NotSupported] when the color_mode and/or bit depth is not supported.
360
364
  def encode_png_pixels_to_scanline_method(color_mode, depth)
361
365
  encoder_method = case color_mode
362
- when ChunkyPNG::COLOR_TRUECOLOR; :"encode_png_pixels_to_scanline_truecolor_#{depth}bit"
363
- when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; :"encode_png_pixels_to_scanline_truecolor_alpha_#{depth}bit"
364
- when ChunkyPNG::COLOR_INDEXED; :"encode_png_pixels_to_scanline_indexed_#{depth}bit"
365
- when ChunkyPNG::COLOR_GRAYSCALE; :"encode_png_pixels_to_scanline_grayscale_#{depth}bit"
366
- when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; :"encode_png_pixels_to_scanline_grayscale_alpha_#{depth}bit"
367
- else nil
366
+ when ChunkyPNG::COLOR_TRUECOLOR then :"encode_png_pixels_to_scanline_truecolor_#{depth}bit"
367
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then :"encode_png_pixels_to_scanline_truecolor_alpha_#{depth}bit"
368
+ when ChunkyPNG::COLOR_INDEXED then :"encode_png_pixels_to_scanline_indexed_#{depth}bit"
369
+ when ChunkyPNG::COLOR_GRAYSCALE then :"encode_png_pixels_to_scanline_grayscale_#{depth}bit"
370
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then :"encode_png_pixels_to_scanline_grayscale_alpha_#{depth}bit"
368
371
  end
369
-
372
+
370
373
  raise ChunkyPNG::NotSupported, "No encoder found for color mode #{color_mode} and #{depth}-bit depth!" unless respond_to?(encoder_method, true)
371
374
  encoder_method
372
375
  end
373
-
374
-
375
376
 
376
377
  # Encodes a scanline of a pixelstream without filtering. This is a no-op.
377
378
  # @param [String] stream The pixelstream to work on. This string will be modified.
@@ -391,7 +392,7 @@ module ChunkyPNG
391
392
  # @return [void]
392
393
  def encode_png_str_scanline_sub(stream, pos, prev_pos, line_width, pixel_size)
393
394
  line_width.downto(1) do |i|
394
- a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0
395
+ a = i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0
395
396
  stream.setbyte(pos + i, (stream.getbyte(pos + i) - a) & 0xff)
396
397
  end
397
398
  stream.setbyte(pos, ChunkyPNG::FILTER_SUB)
@@ -407,36 +408,41 @@ module ChunkyPNG
407
408
  end
408
409
  stream.setbyte(pos, ChunkyPNG::FILTER_UP)
409
410
  end
410
-
411
+
411
412
  # Encodes a scanline of a pixelstream using AVERAGE filtering. This will modify the stream.
412
413
  # @param (see #encode_png_str_scanline_none)
413
414
  # @return [void]
414
415
  def encode_png_str_scanline_average(stream, pos, prev_pos, line_width, pixel_size)
415
416
  line_width.downto(1) do |i|
416
- a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0
417
+ a = i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0
417
418
  b = prev_pos ? stream.getbyte(prev_pos + i) : 0
418
419
  stream.setbyte(pos + i, (stream.getbyte(pos + i) - ((a + b) >> 1)) & 0xff)
419
420
  end
420
421
  stream.setbyte(pos, ChunkyPNG::FILTER_AVERAGE)
421
422
  end
422
-
423
+
423
424
  # Encodes a scanline of a pixelstream using PAETH filtering. This will modify the stream.
424
425
  # @param (see #encode_png_str_scanline_none)
425
426
  # @return [void]
426
427
  def encode_png_str_scanline_paeth(stream, pos, prev_pos, line_width, pixel_size)
427
428
  line_width.downto(1) do |i|
428
- a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0
429
- b = (prev_pos) ? stream.getbyte(prev_pos + i) : 0
430
- c = (prev_pos && i > pixel_size) ? stream.getbyte(prev_pos + i - pixel_size) : 0
429
+ a = i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0
430
+ b = prev_pos ? stream.getbyte(prev_pos + i) : 0
431
+ c = prev_pos && i > pixel_size ? stream.getbyte(prev_pos + i - pixel_size) : 0
431
432
  p = a + b - c
432
433
  pa = (p - a).abs
433
434
  pb = (p - b).abs
434
435
  pc = (p - c).abs
435
- pr = (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c)
436
+ pr = if pa <= pb && pa <= pc
437
+ a
438
+ else
439
+ pb <= pc ? b : c
440
+ end
441
+
436
442
  stream.setbyte(pos + i, (stream.getbyte(pos + i) - pr) & 0xff)
437
443
  end
438
444
  stream.setbyte(pos, ChunkyPNG::FILTER_PAETH)
439
- end
445
+ end
440
446
  end
441
447
  end
442
448
  end