chunky_png 0.0.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +20 -10
- data/chunky_png.gemspec +18 -6
- data/lib/chunky_png.rb +11 -28
- data/lib/chunky_png/canvas.rb +186 -0
- data/lib/chunky_png/canvas/adam7_interlacing.rb +53 -0
- data/lib/chunky_png/canvas/drawing.rb +8 -0
- data/lib/chunky_png/{pixel_matrix → canvas}/operations.rb +12 -12
- data/lib/chunky_png/canvas/png_decoding.rb +145 -0
- data/lib/chunky_png/canvas/png_encoding.rb +182 -0
- data/lib/chunky_png/chunk.rb +101 -23
- data/lib/chunky_png/color.rb +307 -0
- data/lib/chunky_png/datastream.rb +143 -45
- data/lib/chunky_png/image.rb +31 -30
- data/lib/chunky_png/palette.rb +49 -47
- data/lib/chunky_png/rmagick.rb +43 -0
- data/spec/chunky_png/canvas/adam7_interlacing_spec.rb +108 -0
- data/spec/chunky_png/canvas/png_decoding_spec.rb +81 -0
- data/spec/chunky_png/canvas/png_encoding_spec.rb +70 -0
- data/spec/chunky_png/canvas_spec.rb +71 -0
- data/spec/chunky_png/color_spec.rb +104 -0
- data/spec/chunky_png/datastream_spec.rb +32 -0
- data/spec/chunky_png/image_spec.rb +25 -0
- data/spec/chunky_png/rmagick_spec.rb +21 -0
- data/spec/{integration/image_spec.rb → chunky_png_spec.rb} +14 -8
- data/spec/resources/composited.png +0 -0
- data/spec/resources/cropped.png +0 -0
- data/spec/resources/damaged_chunk.png +0 -0
- data/spec/resources/damaged_signature.png +13 -0
- data/spec/resources/pixelstream.rgba +67 -0
- data/spec/resources/replaced.png +0 -0
- data/spec/resources/text_chunk.png +0 -0
- data/spec/resources/ztxt_chunk.png +0 -0
- data/spec/spec_helper.rb +8 -5
- data/tasks/github-gem.rake +1 -1
- metadata +37 -18
- data/lib/chunky_png/pixel.rb +0 -272
- data/lib/chunky_png/pixel_matrix.rb +0 -136
- data/lib/chunky_png/pixel_matrix/decoding.rb +0 -159
- data/lib/chunky_png/pixel_matrix/encoding.rb +0 -89
- data/spec/unit/decoding_spec.rb +0 -83
- data/spec/unit/encoding_spec.rb +0 -27
- data/spec/unit/pixel_matrix_spec.rb +0 -93
- data/spec/unit/pixel_spec.rb +0 -47
@@ -0,0 +1,307 @@
|
|
1
|
+
module ChunkyPNG
|
2
|
+
|
3
|
+
# The Color module defines methods for handling colors. Within the ChunkyPNG
|
4
|
+
# library, the concepts of pixels and colors are both used, and they are
|
5
|
+
# both represented by a Fixnum.
|
6
|
+
#
|
7
|
+
# Pixels/colors are represented in RGBA componetns. Each of the four
|
8
|
+
# components is stored with a depth of 8 bits (maximum value = 255 =
|
9
|
+
# {ChunkyPNG::Color::MAX}). Together, these components are stored in a 4-byte
|
10
|
+
# Fixnum.
|
11
|
+
#
|
12
|
+
# A color will always be represented using these 4 components in memory.
|
13
|
+
# When the image is encoded, a more suitable representation can be used
|
14
|
+
# (e.g. rgb, grayscale, palette-based), for which several conversion methods
|
15
|
+
# are provided in this module.
|
16
|
+
module Color
|
17
|
+
extend self
|
18
|
+
|
19
|
+
# The maximum value of each color component.
|
20
|
+
MAX = 0xff
|
21
|
+
|
22
|
+
####################################################################
|
23
|
+
# CONSTRUCTING COLOR VALUES
|
24
|
+
####################################################################
|
25
|
+
|
26
|
+
# Creates a new color using an r, g, b triple and an alpha value.
|
27
|
+
# @return [Fixnum] The newly constructed color value.
|
28
|
+
def rgba(r, g, b, a)
|
29
|
+
r << 24 | g << 16 | b << 8 | a
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates a new color using an r, g, b triple.
|
33
|
+
# @return [Fixnum] The newly constructed color value.
|
34
|
+
def rgb(r, g, b, a = MAX)
|
35
|
+
rgba(r, g, b, a)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a new color using a grayscale teint.
|
39
|
+
# @return [ChunkyPNG::Color] The newly constructed color value.
|
40
|
+
def grayscale(teint, a = MAX)
|
41
|
+
rgba(teint, teint, teint, a)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Creates a new color using a grayscale teint and alpha value.
|
45
|
+
# @return [Fixnum] The newly constructed color value.
|
46
|
+
def grayscale_alpha(teint, a)
|
47
|
+
rgba(teint, teint, teint, a)
|
48
|
+
end
|
49
|
+
|
50
|
+
####################################################################
|
51
|
+
# COLOR IMPORTING
|
52
|
+
####################################################################
|
53
|
+
|
54
|
+
# Creates a color by unpacking an rgb triple from a string.
|
55
|
+
#
|
56
|
+
# @param [String] stream The string to load the color from. It should be
|
57
|
+
# at least 3 + pos bytes long.
|
58
|
+
# @param [Fixnum] pos The position in the string to load the triple from.
|
59
|
+
# @return [Fixnum] The newly constructed color value.
|
60
|
+
def from_rgb_stream(stream, pos = 0)
|
61
|
+
rgb(*stream.unpack("@#{pos}C3"))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates a color by unpacking an rgba triple from a string
|
65
|
+
#
|
66
|
+
# @param [String] stream The string to load the color from. It should be
|
67
|
+
# at least 4 + pos bytes long.
|
68
|
+
# @param [Fixnum] pos The position in the string to load the triple from.
|
69
|
+
# @return [Fixnum] The newly constructed color value.
|
70
|
+
def from_rgba_stream(stream, pos = 0)
|
71
|
+
rgba(*stream.unpack("@#{pos}C4"))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Creates a color by converting it from a string in hex notation.
|
75
|
+
#
|
76
|
+
# It supports colors with (#rrggbbaa) or without (#rrggbb) alpha channel.
|
77
|
+
# Color strings may include the prefix "0x" or "#".
|
78
|
+
#
|
79
|
+
# @param [String] str The color in hex notation. @return [Fixnum] The
|
80
|
+
# converted color value.
|
81
|
+
def from_hex(str)
|
82
|
+
case str
|
83
|
+
when /^(?:#|0x)?([0-9a-f]{6})$/i then ($1.hex << 8) | 0xff
|
84
|
+
when /^(?:#|0x)?([0-9a-f]{8})$/i then $1.hex
|
85
|
+
else raise "Not a valid hex color notation: #{str.inspect}!"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
####################################################################
|
90
|
+
# PROPERTIES
|
91
|
+
####################################################################
|
92
|
+
|
93
|
+
# Returns the red-component from the color value.
|
94
|
+
#
|
95
|
+
# @param [Fixnum] value The color value.
|
96
|
+
# @return [Fixnum] A value between 0 and MAX.
|
97
|
+
def r(value)
|
98
|
+
(value & 0xff000000) >> 24
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the green-component from the color value.
|
102
|
+
#
|
103
|
+
# @param [Fixnum] value The color value.
|
104
|
+
# @return [Fixnum] A value between 0 and MAX.
|
105
|
+
def g(value)
|
106
|
+
(value & 0x00ff0000) >> 16
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the blue-component from the color value.
|
110
|
+
#
|
111
|
+
# @param [Fixnum] value The color value.
|
112
|
+
# @return [Fixnum] A value between 0 and MAX.
|
113
|
+
def b(value)
|
114
|
+
(value & 0x0000ff00) >> 8
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the alpha channel value for the color value.
|
118
|
+
#
|
119
|
+
# @param [Fixnum] value The color value.
|
120
|
+
# @return [Fixnum] A value between 0 and MAX.
|
121
|
+
def a(value)
|
122
|
+
value & 0x000000ff
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns true if this color is fully opaque.
|
126
|
+
#
|
127
|
+
# @param [Fixnum] value The color to test.
|
128
|
+
# @return [true, false] True if the alpha channel equals MAX.
|
129
|
+
def opaque?(value)
|
130
|
+
a(value) == 0x000000ff
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns true if this color is fully transparent.
|
134
|
+
#
|
135
|
+
# @param [Fixnum] value The color to test.
|
136
|
+
# @return [true, false] True if the r, g and b component are equal.
|
137
|
+
def grayscale?(value)
|
138
|
+
r(value) == b(value) && b(value) == g(value)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns true if this color is fully transparent.
|
142
|
+
#
|
143
|
+
# @param [Fixnum] value The color to test.
|
144
|
+
# @return [true, false] True if the alpha channel equals 0.
|
145
|
+
def fully_transparent?(value)
|
146
|
+
a(value) == 0x00000000
|
147
|
+
end
|
148
|
+
|
149
|
+
####################################################################
|
150
|
+
# ALPHA COMPOSITION
|
151
|
+
####################################################################
|
152
|
+
|
153
|
+
# Multiplies two fractions using integer math, where the fractions are stored using an
|
154
|
+
# integer between 0 and 255. This method is used as a helper method for compositing
|
155
|
+
# colors using integer math.
|
156
|
+
#
|
157
|
+
# This is a quicker implementation of ((a * b) / 255.0).round.
|
158
|
+
#
|
159
|
+
# @param [Fixnum] a The first fraction.
|
160
|
+
# @param [Fixnum] b The second fraction.
|
161
|
+
# @return [Fixnum] The result of the multiplication.
|
162
|
+
def int8_mult(a, b)
|
163
|
+
t = a * b + 0x80
|
164
|
+
((t >> 8) + t) >> 8
|
165
|
+
end
|
166
|
+
|
167
|
+
# Composes two colors with an alpha channel using integer math.
|
168
|
+
#
|
169
|
+
# This version is faster than the version based on floating point math, so this
|
170
|
+
# compositing function is used by default.
|
171
|
+
#
|
172
|
+
# @param [Fixnum] fg The foreground color.
|
173
|
+
# @param [Fixnum] bg The foreground color.
|
174
|
+
# @return [Fixnum] The composited color.
|
175
|
+
# @see ChunkyPNG::Color#compose_precise
|
176
|
+
def compose_quick(fg, bg)
|
177
|
+
return fg if opaque?(fg)
|
178
|
+
return bg if fully_transparent?(fg)
|
179
|
+
|
180
|
+
a_com = int8_mult(0xff - a(fg), a(bg))
|
181
|
+
new_r = int8_mult(a(fg), r(fg)) + int8_mult(a_com, r(bg))
|
182
|
+
new_g = int8_mult(a(fg), g(fg)) + int8_mult(a_com, g(bg))
|
183
|
+
new_b = int8_mult(a(fg), b(fg)) + int8_mult(a_com, b(bg))
|
184
|
+
new_a = a(fg) + a_com
|
185
|
+
rgba(new_r, new_g, new_b, new_a)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Composes two colors with an alpha channel using floating point math.
|
189
|
+
#
|
190
|
+
# This method uses more precise floating point math, but this precision is lost
|
191
|
+
# when the result is converted back to an integer. Because it is slower than
|
192
|
+
# the version based on integer math, that version is preferred.
|
193
|
+
#
|
194
|
+
# @param [Fixnum] fg The foreground color.
|
195
|
+
# @param [Fixnum] bg The foreground color.
|
196
|
+
# @return [Fixnum] The composited color.
|
197
|
+
# @see ChunkyPNG::Color#compose_quick
|
198
|
+
def compose_precise(fg, bg)
|
199
|
+
return fg if opaque?(fg)
|
200
|
+
return bg if fully_transparent?(fg)
|
201
|
+
|
202
|
+
fg_a = a(fg).to_f / MAX
|
203
|
+
bg_a = a(bg).to_f / MAX
|
204
|
+
a_com = (1.0 - fg_a) * bg_a
|
205
|
+
|
206
|
+
new_r = (fg_a * r(fg) + a_com * r(bg)).round
|
207
|
+
new_g = (fg_a * g(fg) + a_com * g(bg)).round
|
208
|
+
new_b = (fg_a * b(fg) + a_com * b(bg)).round
|
209
|
+
new_a = ((fg_a + a_com) * MAX).round
|
210
|
+
rgba(new_r, new_g, new_b, new_a)
|
211
|
+
end
|
212
|
+
|
213
|
+
alias :compose :compose_quick
|
214
|
+
|
215
|
+
# Blends the foreground and background color by taking the average of
|
216
|
+
# the components.
|
217
|
+
#
|
218
|
+
# @param [Fixnum] fg The foreground color.
|
219
|
+
# @param [Fixnum] bg The foreground color.
|
220
|
+
# @return [Fixnum] The blended color.
|
221
|
+
def blend(fg, bg)
|
222
|
+
(fg + bg) >> 1
|
223
|
+
end
|
224
|
+
|
225
|
+
####################################################################
|
226
|
+
# CONVERSIONS
|
227
|
+
####################################################################
|
228
|
+
|
229
|
+
# Returns a string representing this color using hex notation (i.e. #rrggbbaa).
|
230
|
+
#
|
231
|
+
# @param [Fixnum] value The color to convert.
|
232
|
+
# @return [String] The color in hex notation, starting with a pound sign.
|
233
|
+
def to_hex(color, include_alpha = true)
|
234
|
+
include_alpha ? ('#%08x' % color) : ('#%06x' % [color >> 8])
|
235
|
+
end
|
236
|
+
|
237
|
+
# Returns an array with the separate RGBA values for this color.
|
238
|
+
#
|
239
|
+
# @param [Fixnum] color The color to convert.
|
240
|
+
# @return [Array<Fixnum>] An array with 4 Fixnum elements.
|
241
|
+
def to_truecolor_alpha_bytes(color)
|
242
|
+
[r(color), g(color), b(color), a(color)]
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns an array with the separate RGB values for this color.
|
246
|
+
# The alpha channel will be discarded.
|
247
|
+
#
|
248
|
+
# @param [Fixnum] color The color to convert.
|
249
|
+
# @return [Array<Fixnum>] An array with 3 Fixnum elements.
|
250
|
+
def to_truecolor_bytes(color)
|
251
|
+
[r(color), g(color), b(color)]
|
252
|
+
end
|
253
|
+
|
254
|
+
# Returns an array with the grayscale teint value for this color.
|
255
|
+
#
|
256
|
+
# This method expects the r,g and b value to be equal, and the alpha
|
257
|
+
# channel will be discarded.
|
258
|
+
#
|
259
|
+
# @param [Fixnum] color The grayscale color to convert.
|
260
|
+
# @return [Array<Fixnum>] An array with 1 Fixnum element.
|
261
|
+
def to_grayscale_bytes(color)
|
262
|
+
[r(color)] # assumption r == g == b
|
263
|
+
end
|
264
|
+
|
265
|
+
# Returns an array with the grayscale teint and alpha channel values
|
266
|
+
# for this color.
|
267
|
+
#
|
268
|
+
# This method expects the r,g and b value to be equal.
|
269
|
+
#
|
270
|
+
# @param [Fixnum] color The grayscale color to convert.
|
271
|
+
# @return [Array<Fixnum>] An array with 2 Fixnum elements.
|
272
|
+
def to_grayscale_alpha_bytes(color)
|
273
|
+
[r(color), a(color)] # assumption r == g == b
|
274
|
+
end
|
275
|
+
|
276
|
+
####################################################################
|
277
|
+
# COLOR CONSTANTS
|
278
|
+
####################################################################
|
279
|
+
|
280
|
+
# Black pixel/color
|
281
|
+
BLACK = rgb( 0, 0, 0)
|
282
|
+
|
283
|
+
# White pixel/color
|
284
|
+
WHITE = rgb(255, 255, 255)
|
285
|
+
|
286
|
+
# Fully transparent pixel/color
|
287
|
+
TRANSPARENT = rgba(255, 255, 255, 0)
|
288
|
+
|
289
|
+
####################################################################
|
290
|
+
# STATIC UTILITY METHODS
|
291
|
+
####################################################################
|
292
|
+
|
293
|
+
# Returns the size in bytes of a pixel when it is stored using a given color mode.
|
294
|
+
# @param [Fixnum] color_mode The color mode in which the pixels are stored.
|
295
|
+
# @return [Fixnum] The number of bytes used per pixel in a datastream.
|
296
|
+
def bytesize(color_mode)
|
297
|
+
case color_mode
|
298
|
+
when ChunkyPNG::COLOR_INDEXED then 1
|
299
|
+
when ChunkyPNG::COLOR_TRUECOLOR then 3
|
300
|
+
when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then 4
|
301
|
+
when ChunkyPNG::COLOR_GRAYSCALE then 1
|
302
|
+
when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then 2
|
303
|
+
else raise "Don't know the bytesize of pixels in this colormode: #{color_mode}!"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
@@ -1,71 +1,169 @@
|
|
1
1
|
module ChunkyPNG
|
2
|
-
|
2
|
+
|
3
|
+
# The Datastream class represents a PNG formatted datastream. It supports
|
4
|
+
# both reading from and writing to strings, stremas and files.
|
5
|
+
#
|
6
|
+
# A PNG datastream begins with the PNG signature, and than contains multiple
|
7
|
+
# chunks, starting with a header (IHDR) chunk and finishing with an end
|
8
|
+
# (IEND) chunk.
|
9
|
+
#
|
10
|
+
# @see ChunkyPNG::Chunk
|
3
11
|
class Datastream
|
4
|
-
|
12
|
+
|
13
|
+
# The signature that each PNG file or stream should begin with.
|
5
14
|
SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10].pack('C8')
|
6
|
-
|
15
|
+
|
16
|
+
# The header chunk of this datastream.
|
17
|
+
# @return [ChunkyPNG::Chunk::Header]
|
7
18
|
attr_accessor :header_chunk
|
19
|
+
|
20
|
+
# All other chunks in this PNG file.
|
21
|
+
# @return [Array<ChunkyPNG::Chunk::Generic>]
|
8
22
|
attr_accessor :other_chunks
|
23
|
+
|
24
|
+
# The chunk containing the image's palette.
|
25
|
+
# @return [ChunkyPNG::Chunk::Palette]
|
9
26
|
attr_accessor :palette_chunk
|
27
|
+
|
28
|
+
# The chunk containing the transparency information of the palette.
|
29
|
+
# @return [ChunkyPNG::Chunk::Transparency]
|
10
30
|
attr_accessor :transparency_chunk
|
31
|
+
|
32
|
+
# The chunks that together compose the images pixel data.
|
33
|
+
# @return [Array<ChunkyPNG::Chunk::ImageData>]
|
11
34
|
attr_accessor :data_chunks
|
35
|
+
|
36
|
+
# The empty chunk that signals the end of this datastream
|
37
|
+
# @return [ChunkyPNG::Chunk::Header]
|
12
38
|
attr_accessor :end_chunk
|
13
39
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
40
|
+
|
41
|
+
# Initializes a new Datastream instance.
|
42
|
+
def initialize
|
43
|
+
@other_chunks = []
|
44
|
+
@data_chunks = []
|
45
|
+
end
|
46
|
+
|
47
|
+
#############################################
|
48
|
+
# LOADING DATASTREAMS
|
49
|
+
#############################################
|
50
|
+
|
51
|
+
class << self
|
52
|
+
|
53
|
+
# Reads a PNG datastream from a string.
|
54
|
+
# @param [String] str The PNG encoded string to load from.
|
55
|
+
# @return [ChunkyPNG::Datastream] The loaded datastream instance.
|
56
|
+
def from_blob(str)
|
57
|
+
from_io(StringIO.new(str))
|
58
|
+
end
|
59
|
+
|
60
|
+
alias :from_string :from_blob
|
61
|
+
|
62
|
+
# Reads a PNG datastream from a file.
|
63
|
+
# @param [String] filename The path of the file to load from.
|
64
|
+
# @return [ChunkyPNG::Datastream] The loaded datastream instance.
|
65
|
+
def from_file(filename)
|
66
|
+
ds = nil
|
67
|
+
File.open(filename, 'rb') { |f| ds = from_io(f) }
|
68
|
+
ds
|
69
|
+
end
|
70
|
+
|
71
|
+
# Reads a PNG datastream from an input stream
|
72
|
+
# @param [IO] io The stream to read from.
|
73
|
+
# @return [ChunkyPNG::Datastream] The loaded datastream instance.
|
74
|
+
def from_io(io)
|
75
|
+
verify_signature!(io)
|
76
|
+
|
77
|
+
ds = self.new
|
78
|
+
until io.eof?
|
79
|
+
chunk = ChunkyPNG::Chunk.read(io)
|
80
|
+
case chunk
|
81
|
+
when ChunkyPNG::Chunk::Header then ds.header_chunk = chunk
|
82
|
+
when ChunkyPNG::Chunk::Palette then ds.palette_chunk = chunk
|
83
|
+
when ChunkyPNG::Chunk::Transparency then ds.transparency_chunk = chunk
|
84
|
+
when ChunkyPNG::Chunk::ImageData then ds.data_chunks << chunk
|
85
|
+
when ChunkyPNG::Chunk::End then ds.end_chunk = chunk
|
86
|
+
else ds.other_chunks << chunk
|
87
|
+
end
|
27
88
|
end
|
89
|
+
return ds
|
90
|
+
end
|
91
|
+
|
92
|
+
# Verifies that the current stream is a PNG datastream by checking its signature.
|
93
|
+
#
|
94
|
+
# This method reads the PNG signature from the stream, setting the current position
|
95
|
+
# of the stream directly after the signature, where the IHDR chunk should begin.
|
96
|
+
#
|
97
|
+
# @raise [RuntimeError] An exception is raised if the PNG signature is not found at
|
98
|
+
# the beginning of the stream.
|
99
|
+
def verify_signature!(io)
|
100
|
+
signature = io.read(ChunkyPNG::Datastream::SIGNATURE.length)
|
101
|
+
raise "PNG signature not found!" unless signature == ChunkyPNG::Datastream::SIGNATURE
|
28
102
|
end
|
29
|
-
return ds
|
30
103
|
end
|
31
104
|
|
32
|
-
|
33
|
-
|
34
|
-
|
105
|
+
#############################################
|
106
|
+
# CHUNKS
|
107
|
+
#############################################
|
108
|
+
|
109
|
+
# Enumerates the chunks in this datastream.
|
110
|
+
#
|
111
|
+
# This will iterate over the chunks using the order in which the chunks
|
112
|
+
# should appear in the PNG file.
|
113
|
+
#
|
114
|
+
# @yield [ChunkyPNG::Chunk::Base] The chunks in this datastrean, one by one.
|
115
|
+
# @see ChunkyPNG::Datastream#chunks
|
116
|
+
def each_chunk
|
117
|
+
yield(header_chunk)
|
118
|
+
other_chunks.each { |chunk| yield(chunk) }
|
119
|
+
yield(palette_chunk) if palette_chunk
|
120
|
+
yield(transparency_chunk) if transparency_chunk
|
121
|
+
data_chunks.each { |chunk| yield(chunk) }
|
122
|
+
yield(end_chunk)
|
35
123
|
end
|
36
|
-
|
124
|
+
|
125
|
+
# Returns an enumerator instance for this datastream's chunks.
|
126
|
+
# @return [Enumerable::Enumerator] An enumerator for the :each_chunk method.
|
127
|
+
# @see ChunkyPNG::Datastream#each_chunk
|
37
128
|
def chunks
|
38
|
-
|
39
|
-
cs += other_chunks
|
40
|
-
cs << palette_chunk if palette_chunk
|
41
|
-
cs << transparency_chunk if transparency_chunk
|
42
|
-
cs += data_chunks
|
43
|
-
cs << end_chunk
|
44
|
-
return cs
|
129
|
+
enum_for(:each_chunk)
|
45
130
|
end
|
46
131
|
|
47
|
-
|
48
|
-
|
49
|
-
|
132
|
+
# Returns all the textual metadata key/value pairs as hash.
|
133
|
+
def metadata
|
134
|
+
metadata = {}
|
135
|
+
other_chunks.select do |chunk|
|
136
|
+
metadata[chunk.keyword] = chunk.value if chunk.respond_to?(:keyword)
|
137
|
+
end
|
138
|
+
metadata
|
50
139
|
end
|
51
|
-
|
140
|
+
|
141
|
+
#############################################
|
142
|
+
# WRITING DATASTREAMS
|
143
|
+
#############################################
|
144
|
+
|
145
|
+
# Writes the datastream to the given output stream.
|
146
|
+
# @param [IO] io The output stream to write to.
|
52
147
|
def write(io)
|
53
148
|
io << SIGNATURE
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
def idat_chunks(data)
|
58
|
-
streamdata = Zlib::Deflate.deflate(data)
|
59
|
-
# TODO: Split long streamdata over multiple chunks
|
60
|
-
return [ ChunkyPNG::Chunk::ImageData.new('IDAT', streamdata) ]
|
149
|
+
each_chunk { |c| c.write(io) }
|
61
150
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
151
|
+
|
152
|
+
# Saves this datastream as a PNG file.
|
153
|
+
# @param [String] filename The filename to use.
|
154
|
+
def save(filename)
|
155
|
+
File.open(filename, 'wb') { |f| write(f) }
|
65
156
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
157
|
+
|
158
|
+
# Encodes this datastream into a string.
|
159
|
+
# @return [String] The encoded PNG datastream.
|
160
|
+
def to_blob
|
161
|
+
str = StringIO.new
|
162
|
+
write(str)
|
163
|
+
return str.string
|
69
164
|
end
|
165
|
+
|
166
|
+
alias :to_string :to_blob
|
167
|
+
alias :to_s :to_blob
|
70
168
|
end
|
71
169
|
end
|