chunky_png 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  spec/resources/testing.png
3
3
  /pkg
4
4
  /tmp
5
+ /doc
6
+ .yardoc
@@ -1,19 +1,46 @@
1
1
  = Chunky PNG
2
2
 
3
3
  This library can read and write PNG files. It is written in pure Ruby for
4
- maximum portability. It requires the standard Zlib library.
4
+ maximum portability. Let me rephrase: it does NOT require RMagick or any other
5
+ memory leaking image library.
6
+
7
+ Source code:: http://github.com/wvanbergen/chunky_png/tree
8
+ RDoc:: http://rdoc.info/projects/wvanbergen/chunky_png
9
+ Wiki:: http://wiki.github.com/wvanbergen/chunky_png
10
+ Issue tracker:: http://github.com/wvanbergen/chunky_png/issues
11
+
12
+ == Features
13
+
14
+ * Decodes almost any image that the PNG standard allows, except for images
15
+ that use a different color depth than 8 bits. This includes all standard
16
+ color modes and all transparency, interlacing and filtering options.
17
+ * Encodes images supports all color modes (true color, grayscale and indexed)
18
+ and transparency for all these color modes. The best color mode will be
19
+ chosen automatically, based on the image's colors.
20
+ * R/W access to the image's pixels.
21
+ * R/W access to all image metadata that is stored in chunks.
22
+
23
+ == Classes
24
+
25
+ The main classes used within ChunkyPNG are:
26
+
27
+ <tt>ChunkyPNG::Image</tt> :: create PNG images from scratch or based on another PNG image.
28
+ <tt>ChunkyPNG::Datastream</tt> :: low-level read and write access to PNG images from or to a file or stream.
29
+ <tt>ChunkyPNG::PixelMatrix</tt> :: represents an images as a matrix of pixels.
30
+ <tt>ChunkyPNG::Pixel</tt> :: represents a single pixel or color value.
5
31
 
6
32
  == Usage
7
33
 
8
34
  require 'chunky_png'
9
35
 
10
- png = ChunkyPNG.load('filename.png')
11
- puts png.chunks.inspect
12
- puts png.pixel_matrix.inspect
13
-
14
- # do something with the PNG file
15
-
16
- File.open('out.png', 'w') { |f| png.write(f) }
36
+ # Creating an image from scratch
37
+ png = ChunkyPNG::Image.new(16, 16, ChunkyPNG::Pixel::TRANSPARENT)
38
+ png[1,1] = ChunkyPNG::Pixel.rgba(255, 255, 255, 128)
39
+ png[2,1] = ChunkyPNG::Pixel.rgba(255, 255, 255, 128)
40
+ png.save('filename.png')
41
+
42
+ # Modify an image - TODO
43
+ # Inspecting metadata - TODO
17
44
 
18
45
  (Note: this is subject to change while work is in progress)
19
46
 
@@ -21,4 +48,6 @@ maximum portability. It requires the standard Zlib library.
21
48
 
22
49
  The library is written by Willem van Bergen for Floorplanner.com, and released
23
50
  under the MIT license (see LICENSE). Please contact me for questions or
24
- remarks. Patches are greatly appreciated! :-)
51
+ remarks. Patches are greatly appreciated! :-)
52
+
53
+ P.S.: _why is why this library is so chunky.
@@ -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.0.2"
7
- s.date = "2010-01-12"
6
+ s.version = "0.0.3"
7
+ s.date = "2010-01-13"
8
8
 
9
9
  s.summary = "Pure ruby library for read/write, chunk-level access to PNG files"
10
10
  s.description = "Pure ruby library for read/write, chunk-level access to PNG files"
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
20
20
 
21
21
  # Do not change the files and test_files fields by hand. This will be done
22
22
  # automatically by the gem release script.
23
- s.files = %w(spec/spec_helper.rb spec/resources/gray_10x10_grayscale.png spec/resources/gray_10x10.png .gitignore spec/resources/gray_10x10_truecolor_alpha.png lib/chunky_png/pixel_matrix.rb LICENSE spec/resources/gray_10x10_truecolor.png lib/chunky_png/pixel_matrix/decoding.rb lib/chunky_png/chunk.rb spec/unit/encoding_spec.rb Rakefile README.rdoc spec/resources/gray_10x10_indexed.png spec/integration/image_spec.rb lib/chunky_png/pixel.rb lib/chunky_png/palette.rb lib/chunky_png/datastream.rb chunky_png.gemspec tasks/github-gem.rake spec/unit/decoding_spec.rb spec/resources/gray_10x10_grayscale_alpha.png lib/chunky_png/pixel_matrix/encoding.rb lib/chunky_png/image.rb spec/unit/pixel_spec.rb spec/unit/pixel_matrix_spec.rb lib/chunky_png.rb)
23
+ s.files = %w(spec/spec_helper.rb spec/resources/gray_10x10_grayscale.png spec/resources/gray_10x10.png .gitignore spec/resources/gray_10x10_truecolor_alpha.png lib/chunky_png/pixel_matrix.rb LICENSE spec/resources/gray_10x10_truecolor.png lib/chunky_png/pixel_matrix/decoding.rb lib/chunky_png/chunk.rb spec/unit/encoding_spec.rb Rakefile spec/resources/transparent_gray_10x10.png README.rdoc spec/resources/gray_10x10_indexed.png spec/resources/16x16_non_interlaced.png spec/integration/image_spec.rb lib/chunky_png/pixel.rb lib/chunky_png/palette.rb lib/chunky_png/datastream.rb chunky_png.gemspec tasks/github-gem.rake spec/unit/decoding_spec.rb spec/resources/gray_10x10_grayscale_alpha.png spec/resources/16x16_interlaced.png spec/resources/adam7.png lib/chunky_png/pixel_matrix/encoding.rb lib/chunky_png/image.rb spec/unit/pixel_spec.rb spec/unit/pixel_matrix_spec.rb lib/chunky_png.rb)
24
24
  s.test_files = %w(spec/unit/encoding_spec.rb spec/integration/image_spec.rb spec/unit/decoding_spec.rb spec/unit/pixel_spec.rb spec/unit/pixel_matrix_spec.rb)
25
25
  end
@@ -10,9 +10,36 @@ require 'chunky_png/pixel_matrix/decoding'
10
10
  require 'chunky_png/pixel_matrix'
11
11
  require 'chunky_png/image'
12
12
 
13
+ # ChunkyPNG
14
+ #
15
+ # The ChunkyPNG module defines some constants that are used in the
16
+ # PNG specification.
13
17
  module ChunkyPNG
14
18
  extend self
15
19
 
20
+ ###################################################
21
+ # PNG international standard defined constants
22
+ ###################################################
23
+
24
+ COLOR_GRAYSCALE = 0
25
+ COLOR_TRUECOLOR = 2
26
+ COLOR_INDEXED = 3
27
+ COLOR_GRAYSCALE_ALPHA = 4
28
+ COLOR_TRUECOLOR_ALPHA = 6
29
+
30
+ FILTERING_DEFAULT = 0
31
+
32
+ COMPRESSION_DEFAULT = 0
33
+
34
+ INTERLACING_NONE = 0
35
+ INTERLACING_ADAM7 = 1
36
+
37
+ FILTER_NONE = 0
38
+ FILTER_SUB = 1
39
+ FILTER_UP = 2
40
+ FILTER_AVERAGE = 3
41
+ FILTER_PAETH = 4
42
+
16
43
  def load_from_io(io)
17
44
  ChunkyPNG::Datastream.read(io)
18
45
  end
@@ -45,21 +45,15 @@ module ChunkyPNG
45
45
 
46
46
  class Header < Base
47
47
 
48
- COLOR_GRAYSCALE = 0
49
- COLOR_TRUECOLOR = 2
50
- COLOR_INDEXED = 3
51
- COLOR_GRAYSCALE_ALPHA = 4
52
- COLOR_TRUECOLOR_ALPHA = 6
53
-
54
48
  attr_accessor :width, :height, :depth, :color, :compression, :filtering, :interlace
55
49
 
56
50
  def initialize(attrs = {})
57
51
  super('IHDR', attrs)
58
52
  @depth ||= 8
59
- @color ||= COLOR_TRUECOLOR
60
- @compression ||= 0
61
- @filtering ||= 0
62
- @interlace ||= 0
53
+ @color ||= ChunkyPNG::COLOR_TRUECOLOR
54
+ @compression ||= ChunkyPNG::COMPRESSION_DEFAULT
55
+ @filtering ||= ChunkyPNG::FILTERING_DEFAULT
56
+ @interlace ||= ChunkyPNG::INTERLACING_NONE
63
57
  end
64
58
 
65
59
  def self.read(type, content)
@@ -1,23 +1,43 @@
1
1
  module ChunkyPNG
2
2
  class Image
3
3
 
4
- attr_reader :pixels
4
+ attr_reader :pixel_matrix
5
5
 
6
6
  def initialize(width, height, background_color = ChunkyPNG::Pixel::TRANSPARENT)
7
- @pixels = ChunkyPNG::PixelMatrix.new(width, height, background_color)
7
+ @pixel_matrix = ChunkyPNG::PixelMatrix.new(width, height, background_color)
8
+ end
9
+
10
+ def self.from_pixel_matrix(matrix)
11
+ self.new(matrix.width, matrix.height, matrix.pixels)
8
12
  end
9
13
 
10
14
  def width
11
- pixels.width
15
+ pixel_matrix.width
12
16
  end
13
17
 
14
18
  def height
15
- pixels.height
19
+ pixel_matrix.height
20
+ end
21
+
22
+ def [](x, y)
23
+ pixel_matrix[x,y]
24
+ end
25
+
26
+ def []=(x, y, pixel)
27
+ pixel_matrix[x,y] = pixel
28
+ end
29
+
30
+ def pixels
31
+ pixel_matrix.pixels
16
32
  end
17
33
 
18
34
  def write(io, constraints = {})
19
- datastream = pixels.to_datastream(constraints)
35
+ datastream = pixel_matrix.to_datastream(constraints)
20
36
  datastream.write(io)
21
37
  end
38
+
39
+ def save(filename, constraints = {})
40
+ File.open(filename, 'w') { |io| write(io, constraints) }
41
+ end
22
42
  end
23
43
  end
@@ -1,16 +1,36 @@
1
1
  module ChunkyPNG
2
-
3
- ###########################################
4
- # PALETTE CLASS
5
- ###########################################
6
-
2
+
3
+ # A palette describes the set of colors that is being used for an image.
4
+ #
5
+ # A PNG image can contain an explicit palette which defines the colors of that image,
6
+ # but can also use an implicit palette, e.g. all truecolor colors or all grayscale colors.
7
+ #
8
+ # This palette supports decoding colors from a palette if an explicit palette is provided
9
+ # in a PNG datastream, and it supports encoding colors to an explicit matrix.
10
+ #
11
+ # @see ChunkyPNG::Pixel
7
12
  class Palette < SortedSet
8
13
 
9
- def initialize(enum)
14
+ # Builds a new palette given a set (Enumerable instance) of colors.
15
+ #
16
+ # @param [Enumerbale<ChunkyPNG::Pixel>] enum The set of colors to include in this palette.
17
+ # This Enumerbale can contains duplicates.
18
+ # @param [Array] decoding_map An array of colors in the exact order at which
19
+ # they appeared in the palette chunk, so that this array can be used for decoding.
20
+ def initialize(enum, decoding_map = nil)
10
21
  super(enum)
11
- @decoding_map = enum if enum.kind_of?(Array)
22
+ @decoding_map = decoding_map if decoding_map
12
23
  end
13
24
 
25
+ # Builds a palette instance from a PLTE chunk and optionally a tRNS chunk
26
+ # from a PNG datastream.
27
+ #
28
+ # This method will cerate a palette that is suitable for decoding an image.
29
+ #
30
+ # @param [ChunkyPNG::Chunk::Palette] The palette chunk to load from
31
+ # @param [ChunkyPNG::Chunk::Transparency, nil] The optional transparency chunk.
32
+ # @return [ChunkyPNG::Palette] The loaded palette instance.
33
+ # @see ChunkyPNG::Palette#can_decode?
14
34
  def self.from_chunks(palette_chunk, transparency_chunk = nil)
15
35
  return nil if palette_chunk.nil?
16
36
 
@@ -31,45 +51,99 @@ module ChunkyPNG
31
51
  index += 1
32
52
  end
33
53
 
34
- self.new(decoding_map)
54
+ self.new(decoding_map, decoding_map)
35
55
  end
36
56
 
57
+ # Builds a palette instance from a given pixel matrix.
58
+ # @param [ChunkyPNG::PixelMatrix] pixel_matrix The pixel matrix to create a palette for.
59
+ # @return [ChunkyPNG::Palette] The palette instance.
37
60
  def self.from_pixel_matrix(pixel_matrix)
38
61
  self.new(pixel_matrix.pixels)
39
62
  end
40
63
 
64
+ # Builds a palette instance from a given set of pixels.
65
+ # @param [Enumerable<ChunkyPNG::Pixel>] pixels An enumeration of pixels to create a palette for
66
+ # @return [ChunkyPNG::Palette] The palette instance.
41
67
  def self.from_pixels(pixels)
42
68
  self.new(pixels)
43
69
  end
44
70
 
71
+ # Checks whether the size of this palette is suitable for indexed storage.
72
+ # @return [true, false] True if the number of colors in this palette is less than 256.
45
73
  def indexable?
46
74
  size < 256
47
75
  end
48
76
 
77
+ # Check whether this pelette only contains opaque colors.
78
+ # @return [true, false] True if all colors in this palette are opaque.
79
+ # @see ChunkyPNG::Pixel#opaque?
49
80
  def opaque?
50
81
  all? { |pixel| pixel.opaque? }
51
82
  end
83
+
84
+ # Check whether this pelette only contains grayscale colors.
85
+ # @return [true, false] True if all colors in this palette are grayscale teints.
86
+ # @see ChunkyPNG::Pixel#grayscale??
87
+ def grayscale?
88
+ all? { |pixel| pixel.grayscale? }
89
+ end
52
90
 
91
+ # Checks whether this palette is suitable for decoding an image from a datastream.
92
+ #
93
+ # This requires that the positions of the colors in the original palette chunk is known,
94
+ # which is stored as an array in the +@decoding_map+ instance variable.
95
+ #
96
+ # @return [true, false] True if a decoding map was built when this palette was loaded.
53
97
  def can_decode?
54
98
  !@decoding_map.nil?
55
99
  end
56
100
 
101
+ # Checks whether this palette is suitable for encoding an image from to datastream.
102
+ #
103
+ # This requires that the position of the color in the future palette chunk is known,
104
+ # which is stored as a hash in the +@encoding_map+ instance variable.
105
+ #
106
+ # @return [true, false] True if a encoding map was built when this palette was loaded.
57
107
  def can_encode?
58
108
  !@encoding_map.nil?
59
109
  end
60
110
 
111
+ # Returns a color, given the position in the original palette chunk.
112
+ # @param [Fixnum] index The 0-based position of the color in the palette.
113
+ # @return [ChunkyPNG::Pixel] The color that is stored in the palette under the given index
114
+ # @see ChunkyPNG::Palette#can_decode?
61
115
  def [](index)
62
116
  @decoding_map[index]
63
117
  end
64
118
 
119
+ # Returns the position of a color in the palette
120
+ # @param [ChunkyPNG::Pixel] color The color for which to look up the index.
121
+ # @return [Fixnum] The 0-based position of the color in the palette.
122
+ # @see ChunkyPNG::Palette#can_encode?
65
123
  def index(color)
66
124
  @encoding_map[color]
67
125
  end
68
126
 
127
+ # Creates a tRNS chunk that corresponds with this palette to store the
128
+ # alpha channel of all colors.
129
+ #
130
+ # Note that this chunk can be left out of every color in the palette is
131
+ # opaque, and the image is encoded using indexed colors.
132
+ #
133
+ # @return [ChunkyPNG::Chunk::Transparency] The tRNS chunk.
69
134
  def to_trns_chunk
70
135
  ChunkyPNG::Chunk::Transparency.new('tRNS', map(&:a).pack('C*'))
71
136
  end
72
137
 
138
+ # Creates a PLTE chunk that corresponds with this palette to store the
139
+ # r, g and b channels of all colors.
140
+ #
141
+ # Note that a PLTE chunk should only be included if the image is
142
+ # encoded using index colors. After this chunk has been built, the
143
+ # palette becomes suitable for encoding an image.
144
+ #
145
+ # @return [ChunkyPNG::Chunk::Palette] The PLTE chunk.
146
+ # @see ChunkyPNG::Palette#can_encode?
73
147
  def to_plte_chunk
74
148
  @encoding_map = {}
75
149
  colors = []
@@ -82,13 +156,22 @@ module ChunkyPNG
82
156
  ChunkyPNG::Chunk::Palette.new('PLTE', colors.pack('C*'))
83
157
  end
84
158
 
159
+ # Determines the most suitable colormode for this palette.
160
+ # @return [Fixnum] The colormode which would create the smalles possible
161
+ # file for images that use this exact palette.
85
162
  def best_colormode
86
- if indexable?
87
- ChunkyPNG::Chunk::Header::COLOR_INDEXED
163
+ if grayscale?
164
+ if opaque?
165
+ ChunkyPNG::COLOR_GRAYSCALE
166
+ else
167
+ ChunkyPNG::COLOR_GRAYSCALE_ALPHA
168
+ end
169
+ elsif indexable?
170
+ ChunkyPNG::COLOR_INDEXED
88
171
  elsif opaque?
89
- ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR
172
+ ChunkyPNG::COLOR_TRUECOLOR
90
173
  else
91
- ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR_ALPHA
174
+ ChunkyPNG::COLOR_TRUECOLOR_ALPHA
92
175
  end
93
176
  end
94
177
  end
@@ -48,16 +48,28 @@ module ChunkyPNG
48
48
  a == 0x000000ff
49
49
  end
50
50
 
51
+ def grayscale?
52
+ r == g && r == b
53
+ end
54
+
51
55
  def inspect
52
56
  '#%08x' % @value
53
57
  end
54
58
 
59
+ def hash
60
+ @value.hash
61
+ end
62
+
55
63
  def eql?(other)
56
64
  other.kind_of?(self.class) && other.value == self.value
57
65
  end
58
66
 
59
67
  alias :== :eql?
60
68
 
69
+ def <=>(other)
70
+ other.value <=> self.value
71
+ end
72
+
61
73
  def to_truecolor_alpha_bytes
62
74
  [r,g,b,a]
63
75
  end
@@ -89,11 +101,11 @@ module ChunkyPNG
89
101
 
90
102
  def self.bytesize(color_mode)
91
103
  case color_mode
92
- when ChunkyPNG::Chunk::Header::COLOR_INDEXED then 1
93
- when ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR then 3
94
- when ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR_ALPHA then 4
95
- when ChunkyPNG::Chunk::Header::COLOR_GRAYSCALE then 1
96
- when ChunkyPNG::Chunk::Header::COLOR_GRAYSCALE_ALPHA then 2
104
+ when ChunkyPNG::COLOR_INDEXED then 1
105
+ when ChunkyPNG::COLOR_TRUECOLOR then 3
106
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then 4
107
+ when ChunkyPNG::COLOR_GRAYSCALE then 1
108
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then 2
97
109
  else raise "Don't know the bytesize of pixels in this colormode: #{color_mode}!"
98
110
  end
99
111
  end
@@ -1,19 +1,38 @@
1
1
  module ChunkyPNG
2
2
 
3
+ # The ChunkPNG::PixelMatrix class represents a matrix of pixels of which an i
4
+ # mage consists. This class supports loading a PixelMatrix from a PNG datastream,
5
+ # and creating a PNG datastream bse don this matrix.
6
+ #
7
+ # This class offers per-pixel access to the matrix by using x,y coordinates. It uses
8
+ # a palette (see {ChunkyPNG::Palette}) to keep track of the different colors used in
9
+ # this matrix.
10
+ #
11
+ # @see ChunkyPNG::Datastream
3
12
  class PixelMatrix
4
13
 
5
14
  include Encoding
6
15
  extend Decoding
7
16
 
8
- FILTER_NONE = 0
9
- FILTER_SUB = 1
10
- FILTER_UP = 2
11
- FILTER_AVERAGE = 3
12
- FILTER_PAETH = 4
13
-
14
- attr_reader :width, :height, :pixels
17
+ # @return [Integer] The number of columns in this pixel matrix
18
+ attr_reader :width
19
+
20
+ # @return [Integer] The number of rows in this pixel matrix
21
+ attr_reader :height
15
22
 
23
+ # @return [Array<ChunkyPNG::Pixel>] The list of pixels in this matrix.
24
+ # This array always should have +width * height+ elements.
25
+ attr_reader :pixels
16
26
 
27
+ # Initializes a new PixelMatrix instance
28
+ # @param [Integer] width The width in pixels of this matrix
29
+ # @param [Integer] width The height in pixels of this matrix
30
+ # @param [ChunkyPNG::Pixel, Array<ChunkyPNG::Pixel>] initial The initial value of te pixels:
31
+ #
32
+ # * If a color is passed to this parameter, this color will be used as background color.
33
+ #
34
+ # * If an array of pixels is provided, these pixels will be used as initial value. Note
35
+ # that the amount of pixels in this array should equal +width * height+.
17
36
  def initialize(width, height, initial = ChunkyPNG::Pixel::TRANSPARENT)
18
37
 
19
38
  @width, @height = width, height
@@ -27,14 +46,24 @@ module ChunkyPNG
27
46
  end
28
47
  end
29
48
 
49
+ # Replaces a single pixel in this matrix.
50
+ # @param [Integer] x The x-coordinate of the pixel (column)
51
+ # @param [Integer] y The y-coordinate of the pixel (row)
52
+ # @param [ChunkyPNG::Pixel] pixel The new pixel for the provided coordinates.
30
53
  def []=(x, y, pixel)
31
54
  @pixels[y * width + x] = pixel
32
55
  end
33
56
 
57
+ # Returns a single pixel from this matrix.
58
+ # @param [Integer] x The x-coordinate of the pixel (column)
59
+ # @param [Integer] y The y-coordinate of the pixel (row)
60
+ # @return [ChunkyPNG::Pixel] The current pixel at the provided coordinates.
34
61
  def [](x, y)
35
62
  @pixels[y * width + x]
36
63
  end
37
64
 
65
+ # Passes to this matrix of pixels line by line.
66
+ # @yield [Array<ChunkyPNG::Pixel>] An line of pixels
38
67
  def each_scanline(&block)
39
68
  height.times do |i|
40
69
  scanline = @pixels[width * i, width]
@@ -42,18 +71,15 @@ module ChunkyPNG
42
71
  end
43
72
  end
44
73
 
74
+ # Returns the palette used for this pixel matrix.
75
+ # @return [ChunkyPNG::Palette] A pallete which contains all the colors that are
76
+ # being used for this image.
45
77
  def palette
46
78
  ChunkyPNG::Palette.from_pixels(@pixels)
47
79
  end
48
80
 
49
- def opaque?
50
- pixels.all? { |pixel| pixel.opaque? }
51
- end
52
-
53
- def indexable?
54
- palette.indexable?
55
- end
56
-
81
+ # Converts this PixelMatrix to a datastream, so that it can be saved as a PNG image.
82
+ # @param [Hash] constraints The constraints to use when encoding the matrix.
57
83
  def to_datastream(constraints = {})
58
84
  data = encode(constraints)
59
85
  ds = Datastream.new
@@ -65,6 +91,10 @@ module ChunkyPNG
65
91
  return ds
66
92
  end
67
93
 
94
+ # Equality check to compare this pixel matrix with other matrices.
95
+ # @param other The object to compare this Matrix to.
96
+ # @return [true, false] True if the size and pixel values of the other matrix
97
+ # are exactly the same as this matrix size and pixel values.
68
98
  def eql?(other)
69
99
  other.kind_of?(self.class) && other.pixels == self.pixels &&
70
100
  other.width == self.width && other.height == self.height
@@ -3,56 +3,116 @@ module ChunkyPNG
3
3
  module Decoding
4
4
 
5
5
  def decode(ds)
6
+ raise "Only 8-bit color depth is currently supported by ChunkyPNG!" unless ds.header_chunk.depth == 8
7
+
6
8
  stream = Zlib::Inflate.inflate(ds.data_chunks.map(&:content).join(''))
7
9
  width = ds.header_chunk.width
8
10
  height = ds.header_chunk.height
9
11
  color_mode = ds.header_chunk.color
12
+ interlace = ds.header_chunk.interlace
10
13
  palette = ChunkyPNG::Palette.from_chunks(ds.palette_chunk, ds.transparency_chunk)
11
- decode_pixelstream(stream, width, height, color_mode, palette)
14
+ decode_pixelstream(stream, width, height, color_mode, palette, interlace)
12
15
  end
13
16
 
14
- def decode_pixelstream(stream, width, height, color_mode = ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR, palette = nil)
15
-
16
- pixel_size = Pixel.bytesize(color_mode)
17
- raise "Invalid stream length!" unless stream.length == width * height * pixel_size + height
17
+ def decode_pixelstream(stream, width, height, color_mode = ChunkyPNG::COLOR_TRUECOLOR, palette = nil, interlace = ChunkyPNG::INTERLACING_NONE)
18
18
  raise "This palette is not suitable for decoding!" if palette && !palette.can_decode?
19
-
19
+
20
+ pixel_size = Pixel.bytesize(color_mode)
20
21
  pixel_decoder = case color_mode
21
- when ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR then lambda { |bytes| ChunkyPNG::Pixel.rgb(*bytes) }
22
- when ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR_ALPHA then lambda { |bytes| ChunkyPNG::Pixel.rgba(*bytes) }
23
- when ChunkyPNG::Chunk::Header::COLOR_INDEXED then lambda { |bytes| palette[bytes.first] }
24
- when ChunkyPNG::Chunk::Header::COLOR_GRAYSCALE then lambda { |bytes| ChunkyPNG::Pixel.grayscale(*bytes) }
25
- when ChunkyPNG::Chunk::Header::COLOR_GRAYSCALE_ALPHA then lambda { |bytes| ChunkyPNG::Pixel.grayscale(*bytes) }
22
+ when ChunkyPNG::COLOR_TRUECOLOR then lambda { |bytes| ChunkyPNG::Pixel.rgb(*bytes) }
23
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then lambda { |bytes| ChunkyPNG::Pixel.rgba(*bytes) }
24
+ when ChunkyPNG::COLOR_INDEXED then lambda { |bytes| palette[bytes.first] }
25
+ when ChunkyPNG::COLOR_GRAYSCALE then lambda { |bytes| ChunkyPNG::Pixel.grayscale(*bytes) }
26
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then lambda { |bytes| ChunkyPNG::Pixel.grayscale(*bytes) }
26
27
  else raise "No suitable pixel decoder found for color mode #{color_mode}!"
27
28
  end
28
29
 
30
+ pixels = case interlace
31
+ when ChunkyPNG::INTERLACING_NONE then decode_interlacing_none(stream, width, height, pixel_size, pixel_decoder)
32
+ when ChunkyPNG::INTERLACING_ADAM7 then decode_interlacing_adam7(stream, width, height, pixel_size, pixel_decoder)
33
+ else raise "Don't know how the handle interlacing method #{interlace}!"
34
+ end
35
+
36
+ return ChunkyPNG::PixelMatrix.new(width, height, pixels)
37
+ end
38
+
39
+ protected
40
+
41
+ def decode_image_pass(stream, width, height, pixel_size, pixel_decoder, start_pos = 0)
29
42
  pixels = []
30
43
  decoded_bytes = Array.new(width * pixel_size, 0)
31
44
  height.times do |line_no|
32
45
 
33
- # get bytes of scanline
34
- position = line_no * (width * pixel_size + 1)
35
- line_length = width * pixel_size
36
- bytes = stream.unpack("@#{position}CC#{line_length}")
37
- filter = bytes.shift
38
- decoded_bytes = decode_scanline(filter, bytes, decoded_bytes, pixel_size)
39
-
40
- # decode bytes into colors
41
- decoded_bytes.each_slice(pixel_size) { |bytes| pixels << pixel_decoder.call(bytes) }
42
- end
46
+ if width > 0
47
+
48
+ # get bytes of scanline
49
+ position = start_pos + line_no * (width * pixel_size + 1)
50
+ line_length = width * pixel_size
51
+ bytes = stream.unpack("@#{position}CC#{line_length}")
52
+ filter = bytes.shift
53
+ decoded_bytes = decode_scanline(filter, bytes, decoded_bytes, pixel_size)
43
54
 
44
- return ChunkyPNG::PixelMatrix.new(width, height, pixels)
55
+ # decode bytes into colors
56
+ decoded_bytes.each_slice(pixel_size) { |bytes| pixels << pixel_decoder.call(bytes) }
57
+ end
58
+ end
59
+ pixels
45
60
  end
46
61
 
47
- protected
62
+ def decode_interlacing_none(stream, width, height, pixel_size, pixel_decoder)
63
+ raise "Invalid stream length!" unless stream.length == width * height * pixel_size + height
64
+ decode_image_pass(stream, width, height, pixel_size, pixel_decoder)
65
+ end
48
66
 
67
+ def decode_interlacing_adam7(stream, width, height, pixel_size, pixel_decoder)
68
+ start_pos = 0
69
+ sub_matrices = adam7_pass_sizes(width, height).map do |(pass_width, pass_height)|
70
+ pixels = decode_image_pass(stream, pass_width, pass_height, pixel_size, pixel_decoder, start_pos)
71
+ start_pos += (pass_width * pass_height * pixel_size) + pass_height
72
+ [pass_width, pass_height, pixels]
73
+ end
74
+
75
+ pixels = Array.new(width * height, ChunkyPNG::Pixel::TRANSPARENT)
76
+ 0.upto(6) { |pass| adam7_merge_pass(pass, width, height, pixels, *sub_matrices[pass]) }
77
+ pixels
78
+ end
79
+
80
+ def adam7_multiplier_offset(pass)
81
+ {
82
+ :x_multiplier => 8 >> (pass >> 1),
83
+ :x_offset => (pass & 1 == 0) ? 0 : 8 >> ((pass + 1) >> 1),
84
+ :y_multiplier => pass == 0 ? 8 : 8 >> ((pass - 1) >> 1),
85
+ :y_offset => (pass == 0 || pass & 1 == 1) ? 0 : 8 >> (pass >> 1)
86
+ }
87
+ end
88
+
89
+ def adam7_merge_pass(pass, width, height, pixels, sm_width, sm_height, sm_pixels)
90
+ m_o = adam7_multiplier_offset(pass)
91
+ 0.upto(sm_height - 1) do |y|
92
+ 0.upto(sm_width - 1) do |x|
93
+ new_x = x * m_o[:x_multiplier] + m_o[:x_offset]
94
+ new_y = y * m_o[:y_multiplier] + m_o[:y_offset]
95
+ pixels[width * new_y + new_x] = sm_pixels[sm_width * y + x]
96
+ end
97
+ end
98
+ pixels
99
+ end
100
+
101
+ def adam7_pass_sizes(width, height)
102
+ (0...7).map do |pass|
103
+ m_o = adam7_multiplier_offset(pass)
104
+ [ ((width - m_o[:x_offset] ) / m_o[:x_multiplier].to_f).ceil,
105
+ ((height - m_o[:y_offset] ) / m_o[:y_multiplier].to_f).ceil,]
106
+ end
107
+ end
108
+
49
109
  def decode_scanline(filter, bytes, previous_bytes, pixelsize = 3)
50
110
  case filter
51
- when FILTER_NONE then decode_scanline_none( bytes, previous_bytes, pixelsize)
52
- when FILTER_SUB then decode_scanline_sub( bytes, previous_bytes, pixelsize)
53
- when FILTER_UP then decode_scanline_up( bytes, previous_bytes, pixelsize)
54
- when FILTER_AVERAGE then raise "Average filter are not yet supported!"
55
- when FILTER_PAETH then raise "Paeth filter are not yet supported!"
111
+ when ChunkyPNG::FILTER_NONE then decode_scanline_none( bytes, previous_bytes, pixelsize)
112
+ when ChunkyPNG::FILTER_SUB then decode_scanline_sub( bytes, previous_bytes, pixelsize)
113
+ when ChunkyPNG::FILTER_UP then decode_scanline_up( bytes, previous_bytes, pixelsize)
114
+ when ChunkyPNG::FILTER_AVERAGE then decode_scanline_average( bytes, previous_bytes, pixelsize)
115
+ when ChunkyPNG::FILTER_PAETH then decode_scanline_paeth( bytes, previous_bytes, pixelsize)
56
116
  else raise "Unknown filter type"
57
117
  end
58
118
  end
@@ -70,6 +130,30 @@ module ChunkyPNG
70
130
  bytes.each_with_index { |b, i| bytes[i] = (b + previous_bytes[i]) % 256 }
71
131
  bytes
72
132
  end
133
+
134
+ def decode_scanline_average(bytes, previous_bytes, pixelsize = 3)
135
+ bytes.each_with_index do |byte, i|
136
+ a = (i >= pixelsize) ? bytes[i - pixelsize] : 0
137
+ b = previous_bytes[i]
138
+ bytes[i] = (byte + (a + b / 2).floor) % 256
139
+ end
140
+ bytes
141
+ end
142
+
143
+ def decode_scanline_paeth(bytes, previous_bytes, pixelsize = 3)
144
+ bytes.each_with_index do |byte, i|
145
+ a = (i >= pixelsize) ? bytes[i - pixelsize] : 0
146
+ b = previous_bytes[i]
147
+ c = (i >= pixelsize) ? previous_bytes[i - pixelsize] : 0
148
+ p = a + b - c
149
+ pa = (p - a).abs
150
+ pb = (p - b).abs
151
+ pc = (p - c).abs
152
+ pr = (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c)
153
+ bytes[i] = (byte + pr) % 256
154
+ end
155
+ bytes
156
+ end
73
157
  end
74
158
  end
75
159
  end
@@ -7,7 +7,7 @@ module ChunkyPNG
7
7
  result = {}
8
8
  result[:header] = { :width => width, :height => height, :color => encoding[:color_mode] }
9
9
 
10
- if encoding[:color_mode] == ChunkyPNG::Chunk::Header::COLOR_INDEXED
10
+ if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED
11
11
  result[:palette_chunk] = encoding[:palette].to_plte_chunk
12
12
  result[:transparency_chunk] = encoding[:palette].to_trns_chunk unless encoding[:palette].opaque?
13
13
  end
@@ -25,58 +25,64 @@ module ChunkyPNG
25
25
  return encoding
26
26
  end
27
27
 
28
- def encode_pixelstream(color_mode = ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR, palette = nil)
28
+ def encode_pixelstream(color_mode = ChunkyPNG::COLOR_TRUECOLOR, palette = nil)
29
29
 
30
30
  pixel_encoder = case color_mode
31
- when ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR then lambda { |pixel| pixel.to_truecolor_bytes }
32
- when ChunkyPNG::Chunk::Header::COLOR_TRUECOLOR_ALPHA then lambda { |pixel| pixel.to_truecolor_alpha_bytes }
33
- when ChunkyPNG::Chunk::Header::COLOR_INDEXED then lambda { |pixel| pixel.to_indexed_bytes(palette) }
34
- when ChunkyPNG::Chunk::Header::COLOR_GRAYSCALE then lambda { |pixel| pixel.to_grayscale_bytes }
35
- when ChunkyPNG::Chunk::Header::COLOR_GRAYSCALE_ALPHA then lambda { |pixel| pixel.to_grayscale_alpha_bytes }
31
+ when ChunkyPNG::COLOR_TRUECOLOR then lambda { |pixel| pixel.to_truecolor_bytes }
32
+ when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then lambda { |pixel| pixel.to_truecolor_alpha_bytes }
33
+ when ChunkyPNG::COLOR_INDEXED then lambda { |pixel| pixel.to_indexed_bytes(palette) }
34
+ when ChunkyPNG::COLOR_GRAYSCALE then lambda { |pixel| pixel.to_grayscale_bytes }
35
+ when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then lambda { |pixel| pixel.to_grayscale_alpha_bytes }
36
36
  else raise "Cannot encode pixels for this mode: #{color_mode}!"
37
37
  end
38
38
 
39
- raise "This palette is not suitable for encoding!" if palette && !palette.can_encode?
39
+ if color_mode == ChunkyPNG::COLOR_INDEXED && !palette.can_encode?
40
+ raise "This palette is not suitable for encoding!"
41
+ end
40
42
 
41
- pixelsize = Pixel.bytesize(color_mode)
43
+ pixel_size = Pixel.bytesize(color_mode)
42
44
 
43
45
  stream = ""
44
- previous = nil
46
+ previous_bytes = Array.new(pixel_size * width, 0)
45
47
  each_scanline do |line|
46
- bytes = line.map(&pixel_encoder).flatten
47
- if previous
48
- stream << encode_scanline_up(bytes, previous, pixelsize).pack('C*')
49
- else
50
- stream << encode_scanline_sub(bytes, previous, pixelsize).pack('C*')
51
- end
52
- previous = bytes
48
+ unencoded_bytes = line.map(&pixel_encoder).flatten
49
+ stream << encode_scanline_up(unencoded_bytes, previous_bytes, pixel_size).pack('C*')
50
+ previous_bytes = unencoded_bytes
53
51
  end
54
52
  return stream
55
- end
53
+ end
56
54
 
57
55
  def encode_scanline(filter, bytes, previous_bytes = nil, pixelsize = 3)
58
56
  case filter
59
- when FILTER_NONE then encode_scanline_none( bytes, previous_bytes, pixelsize)
60
- when FILTER_SUB then encode_scanline_sub( bytes, previous_bytes, pixelsize)
61
- when FILTER_UP then encode_scanline_up( bytes, previous_bytes, pixelsize)
62
- when FILTER_AVERAGE then raise "Average filter are not yet supported!"
63
- when FILTER_PAETH then raise "Paeth filter are not yet supported!"
57
+ when ChunkyPNG::FILTER_NONE then encode_scanline_none( bytes, previous_bytes, pixelsize)
58
+ when ChunkyPNG::FILTER_SUB then encode_scanline_sub( bytes, previous_bytes, pixelsize)
59
+ when ChunkyPNG::FILTER_UP then encode_scanline_up( bytes, previous_bytes, pixelsize)
60
+ when ChunkyPNG::FILTER_AVERAGE then raise "Average filter are not yet supported!"
61
+ when ChunkyPNG::FILTER_PAETH then raise "Paeth filter are not yet supported!"
64
62
  else raise "Unknown filter type"
65
63
  end
66
64
  end
67
65
 
68
- def encode_scanline_none(bytes, previous_bytes = nil, pixelsize = 3)
69
- [FILTER_NONE] + bytes
66
+ def encode_scanline_none(original_bytes, previous_bytes = nil, pixelsize = 3)
67
+ [ChunkyPNG::FILTER_NONE] + original_bytes
70
68
  end
71
69
 
72
- def encode_scanline_sub(bytes, previous_bytes = nil, pixelsize = 3)
73
- encoded = (pixelsize...bytes.length).map { |n| (bytes[n-pixelsize] - bytes[n]) % 256 }
74
- [FILTER_SUB] + bytes[0...pixelsize] + encoded
70
+ def encode_scanline_sub(original_bytes, previous_bytes = nil, pixelsize = 3)
71
+ encoded_bytes = []
72
+ original_bytes.length.times do |index|
73
+ a = (index >= pixelsize) ? original_bytes[index - pixelsize] : 0
74
+ encoded_bytes[index] = (original_bytes[index] - a) % 256
75
+ end
76
+ [ChunkyPNG::FILTER_SUB] + encoded_bytes
75
77
  end
76
78
 
77
- def encode_scanline_up(bytes, previous_bytes, pixelsize = 3)
78
- encoded = (0...bytes.length).map { |n| previous_bytes[n] - bytes[n] % 256 }
79
- [FILTER_UP] + encoded
79
+ def encode_scanline_up(original_bytes, previous_bytes, pixelsize = 3)
80
+ encoded_bytes = []
81
+ original_bytes.length.times do |index|
82
+ b = previous_bytes[index]
83
+ encoded_bytes[index] = (original_bytes[index] - b) % 256
84
+ end
85
+ [ChunkyPNG::FILTER_UP] + encoded_bytes
80
86
  end
81
87
  end
82
88
  end
Binary file
@@ -7,23 +7,23 @@ describe ChunkyPNG::PixelMatrix::Decoding do
7
7
 
8
8
  it "should decode a line without filtering as is" do
9
9
  bytes = [255, 255, 255, 255, 255, 255, 255, 255, 255]
10
- decode_scanline(ChunkyPNG::PixelMatrix::FILTER_NONE, bytes, nil).should == bytes
10
+ decode_scanline(ChunkyPNG::FILTER_NONE, bytes, nil).should == bytes
11
11
  end
12
12
 
13
13
  it "should decode a line with sub filtering correctly" do
14
14
  # all white pixels
15
15
  bytes = [255, 255, 255, 0, 0, 0, 0, 0, 0]
16
- decoded_bytes = decode_scanline(ChunkyPNG::PixelMatrix::FILTER_SUB, bytes, nil)
16
+ decoded_bytes = decode_scanline(ChunkyPNG::FILTER_SUB, bytes, nil)
17
17
  decoded_bytes.should == [255, 255, 255, 255, 255, 255, 255, 255, 255]
18
18
 
19
19
  # all black pixels
20
20
  bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0]
21
- decoded_bytes = decode_scanline(ChunkyPNG::PixelMatrix::FILTER_SUB, bytes, nil)
21
+ decoded_bytes = decode_scanline(ChunkyPNG::FILTER_SUB, bytes, nil)
22
22
  decoded_bytes.should == [0, 0, 0, 0, 0, 0, 0, 0, 0]
23
23
 
24
24
  # various colors
25
25
  bytes = [255, 0, 45, 0, 255, 0, 112, 200, 178]
26
- decoded_bytes = decode_scanline(ChunkyPNG::PixelMatrix::FILTER_SUB, bytes, nil)
26
+ decoded_bytes = decode_scanline(ChunkyPNG::FILTER_SUB, bytes, nil)
27
27
  decoded_bytes.should == [255, 0, 45, 255, 255, 45, 111, 199, 223]
28
28
  end
29
29
 
@@ -31,14 +31,53 @@ describe ChunkyPNG::PixelMatrix::Decoding do
31
31
  # previous line is all black
32
32
  previous_bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0]
33
33
  bytes = [255, 255, 255, 127, 127, 127, 0, 0, 0]
34
- decoded_bytes = decode_scanline(ChunkyPNG::PixelMatrix::FILTER_UP, bytes, previous_bytes)
34
+ decoded_bytes = decode_scanline(ChunkyPNG::FILTER_UP, bytes, previous_bytes)
35
35
  decoded_bytes.should == [255, 255, 255, 127, 127, 127, 0, 0, 0]
36
36
 
37
37
  # previous line has various pixels
38
38
  previous_bytes = [255, 255, 255, 127, 127, 127, 0, 0, 0]
39
39
  bytes = [0, 127, 255, 0, 127, 255, 0, 127, 255]
40
- decoded_bytes = decode_scanline(ChunkyPNG::PixelMatrix::FILTER_UP, bytes, previous_bytes)
40
+ decoded_bytes = decode_scanline(ChunkyPNG::FILTER_UP, bytes, previous_bytes)
41
41
  decoded_bytes.should == [255, 126, 254, 127, 254, 126, 0, 127, 255]
42
42
  end
43
43
  end
44
+
45
+ describe '#adam7_pass_sizes' do
46
+ it "should get the pass sizes for a 8x8 image correctly" do
47
+ adam7_pass_sizes(8, 8).should == [
48
+ [1, 1], [1, 1], [2, 1], [2, 2], [4, 2], [4, 4], [8, 4]
49
+ ]
50
+ end
51
+
52
+ it "should get the pass sizes for a 12x12 image correctly" do
53
+ adam7_pass_sizes(12, 12).should == [
54
+ [2, 2], [1, 2], [3, 1], [3, 3], [6, 3], [6, 6], [12, 6]
55
+ ]
56
+ end
57
+
58
+ it "should get the pass sizes for a 33x47 image correctly" do
59
+ adam7_pass_sizes(33, 47).should == [
60
+ [5, 6], [4, 6], [9, 6], [8, 12], [17, 12], [16, 24], [33, 23]
61
+ ]
62
+ end
63
+
64
+ it "should get the pass sizes for a 1x1 image correctly" do
65
+ adam7_pass_sizes(1, 1).should == [
66
+ [1, 1], [0, 1], [1, 0], [0, 1], [1, 0], [0, 1], [1, 0]
67
+ ]
68
+ end
69
+
70
+ it "should get the pass sizes for a 0x0 image correctly" do
71
+ adam7_pass_sizes(0, 0).should == [
72
+ [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]
73
+ ]
74
+ end
75
+
76
+ it "should always maintain the same amount of pixels in total" do
77
+ [[8, 8], [12, 12], [33, 47], [1, 1], [0, 0]].each do |(width, height)|
78
+ pass_sizes = adam7_pass_sizes(width, height)
79
+ pass_sizes.inject(0) { |sum, (w, h)| sum + (w*h) }.should == width * height
80
+ end
81
+ end
82
+ end
44
83
  end
@@ -7,20 +7,20 @@ describe ChunkyPNG::PixelMatrix::Encoding do
7
7
 
8
8
  it "should encode a scanline without filtering correctly" do
9
9
  bytes = [0, 0, 0, 1, 1, 1, 2, 2, 2]
10
- encoded_bytes = encode_scanline(ChunkyPNG::PixelMatrix::FILTER_NONE, bytes, nil)
10
+ encoded_bytes = encode_scanline(ChunkyPNG::FILTER_NONE, bytes, nil)
11
11
  encoded_bytes.should == [0, 0, 0, 0, 1, 1, 1, 2, 2, 2]
12
12
  end
13
13
 
14
14
  it "should encode a scanline with sub filtering correctly" do
15
15
  bytes = [255, 255, 255, 255, 255, 255, 255, 255, 255]
16
- encoded_bytes = encode_scanline(ChunkyPNG::PixelMatrix::FILTER_SUB, bytes, nil)
16
+ encoded_bytes = encode_scanline(ChunkyPNG::FILTER_SUB, bytes, nil)
17
17
  encoded_bytes.should == [1, 255, 255, 255, 0, 0, 0, 0, 0, 0]
18
18
  end
19
19
 
20
20
  it "should encode a scanline with up filtering correctly" do
21
21
  bytes = [255, 255, 255, 255, 255, 255, 255, 255, 255]
22
22
  previous_bytes = [255, 255, 255, 255, 255, 255, 255, 255, 255]
23
- encoded_bytes = encode_scanline(ChunkyPNG::PixelMatrix::FILTER_UP, bytes, previous_bytes)
23
+ encoded_bytes = encode_scanline(ChunkyPNG::FILTER_UP, bytes, previous_bytes)
24
24
  encoded_bytes.should == [2, 0, 0, 0, 0, 0, 0, 0, 0, 0]
25
25
  end
26
26
  end
@@ -4,16 +4,25 @@ describe ChunkyPNG::PixelMatrix do
4
4
 
5
5
  describe '.decode' do
6
6
 
7
- before(:each) do
8
- @reference = ChunkyPNG::PixelMatrix.new(10, 10, ChunkyPNG::Pixel.rgb(100, 100, 100))
9
- end
10
-
11
7
  [:indexed, :grayscale, :grayscale_alpha, :truecolor, :truecolor_alpha].each do |color_mode|
12
8
  it "should decode an image with color mode #{color_mode} correctly" do
9
+ reference = ChunkyPNG::PixelMatrix.new(10, 10, ChunkyPNG::Pixel.rgb(100, 100, 100))
13
10
  ds = ChunkyPNG.load(resource_file("gray_10x10_#{color_mode}.png"))
14
- ds.pixel_matrix.should == @reference
11
+ ds.pixel_matrix.should == reference
15
12
  end
16
13
  end
14
+
15
+ it "should decode a transparent image correctly" do
16
+ reference = ChunkyPNG::PixelMatrix.new(10, 10, ChunkyPNG::Pixel.rgba(100, 100, 100, 128))
17
+ ds = ChunkyPNG.load(resource_file("transparent_gray_10x10.png"))
18
+ ds.pixel_matrix.should == reference
19
+ end
20
+
21
+ it "should decode an interlaced image correctly" do
22
+ ds_i = ChunkyPNG.load(resource_file("16x16_interlaced.png"))
23
+ ds_ni = ChunkyPNG.load(resource_file("16x16_non_interlaced.png"))
24
+ ds_i.pixel_matrix.should == ds_ni.pixel_matrix
25
+ end
17
26
  end
18
27
 
19
28
  describe '.encode' do
@@ -124,7 +124,7 @@ module GithubGem
124
124
  desc "Perform all checks that would occur before a release"
125
125
  task(:release_checks => checks)
126
126
 
127
- release_tasks = [:release_checks, :set_version, :build, :github_release]
127
+ release_tasks = [:release_checks, :set_version, :build, :github_release, :gemcutter_release]
128
128
  release_tasks << [:rubyforge_release] if gemspec.rubyforge_project
129
129
 
130
130
  desc "Release a new verison of the gem"
@@ -132,6 +132,7 @@ module GithubGem
132
132
 
133
133
  task(:check_rubyforge) { check_rubyforge_task }
134
134
  task(:rubyforge_release) { rubyforge_release_task }
135
+ task(:gemcutter_release) { gemcutter_release_task }
135
136
  task(:github_release => [:commit_modified_files, :tag_version]) { github_release_task }
136
137
  task(:tag_version) { tag_version_task }
137
138
  task(:commit_modified_files) { commit_modified_files_task }
@@ -228,6 +229,10 @@ module GithubGem
228
229
  def rubyforge_release_task
229
230
  sh 'rubyforge', 'add_release', gemspec.rubyforge_project, gemspec.name, gemspec.version.to_s, "pkg/#{gemspec.name}-#{gemspec.version}.gem"
230
231
  end
232
+
233
+ def gemcutter_release_task
234
+ sh "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
235
+ end
231
236
 
232
237
  # Gem release task.
233
238
  # All work is done by the task's dependencies, so just display a release completed message.
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.0.2
4
+ version: 0.0.3
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-12 00:00:00 +01:00
12
+ date: 2010-01-13 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -44,8 +44,10 @@ files:
44
44
  - lib/chunky_png/chunk.rb
45
45
  - spec/unit/encoding_spec.rb
46
46
  - Rakefile
47
+ - spec/resources/transparent_gray_10x10.png
47
48
  - README.rdoc
48
49
  - spec/resources/gray_10x10_indexed.png
50
+ - spec/resources/16x16_non_interlaced.png
49
51
  - spec/integration/image_spec.rb
50
52
  - lib/chunky_png/pixel.rb
51
53
  - lib/chunky_png/palette.rb
@@ -54,6 +56,8 @@ files:
54
56
  - tasks/github-gem.rake
55
57
  - spec/unit/decoding_spec.rb
56
58
  - spec/resources/gray_10x10_grayscale_alpha.png
59
+ - spec/resources/16x16_interlaced.png
60
+ - spec/resources/adam7.png
57
61
  - lib/chunky_png/pixel_matrix/encoding.rb
58
62
  - lib/chunky_png/image.rb
59
63
  - spec/unit/pixel_spec.rb