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.
- checksums.yaml +5 -5
- data/.standard.yml +16 -0
- data/.travis.yml +5 -5
- data/.yardopts +1 -1
- data/CHANGELOG.rdoc +5 -1
- data/CONTRIBUTING.rdoc +17 -8
- data/Gemfile +3 -3
- data/LICENSE +1 -1
- data/README.md +6 -1
- data/Rakefile +3 -3
- data/benchmarks/decoding_benchmark.rb +17 -17
- data/benchmarks/encoding_benchmark.rb +22 -19
- data/benchmarks/filesize_benchmark.rb +6 -6
- data/bin/rake +29 -0
- data/bin/standardrb +29 -0
- data/chunky_png.gemspec +15 -15
- data/lib/chunky_png.rb +16 -25
- data/lib/chunky_png/canvas.rb +28 -27
- data/lib/chunky_png/canvas/adam7_interlacing.rb +14 -10
- data/lib/chunky_png/canvas/data_url_exporting.rb +1 -3
- data/lib/chunky_png/canvas/data_url_importing.rb +1 -3
- data/lib/chunky_png/canvas/drawing.rb +28 -43
- data/lib/chunky_png/canvas/masking.rb +12 -14
- data/lib/chunky_png/canvas/operations.rb +26 -24
- data/lib/chunky_png/canvas/png_decoding.rb +36 -32
- data/lib/chunky_png/canvas/png_encoding.rb +106 -100
- data/lib/chunky_png/canvas/resampling.rb +26 -33
- data/lib/chunky_png/canvas/stream_exporting.rb +6 -8
- data/lib/chunky_png/canvas/stream_importing.rb +6 -8
- data/lib/chunky_png/chunk.rb +69 -60
- data/lib/chunky_png/color.rb +211 -206
- data/lib/chunky_png/datastream.rb +20 -22
- data/lib/chunky_png/dimension.rb +16 -11
- data/lib/chunky_png/image.rb +9 -11
- data/lib/chunky_png/palette.rb +4 -9
- data/lib/chunky_png/point.rb +25 -26
- data/lib/chunky_png/rmagick.rb +8 -10
- data/lib/chunky_png/vector.rb +26 -29
- data/lib/chunky_png/version.rb +1 -1
- data/spec/chunky_png/canvas/adam7_interlacing_spec.rb +20 -21
- data/spec/chunky_png/canvas/data_url_exporting_spec.rb +8 -5
- data/spec/chunky_png/canvas/data_url_importing_spec.rb +5 -6
- data/spec/chunky_png/canvas/drawing_spec.rb +46 -38
- data/spec/chunky_png/canvas/masking_spec.rb +15 -16
- data/spec/chunky_png/canvas/operations_spec.rb +68 -67
- data/spec/chunky_png/canvas/png_decoding_spec.rb +37 -38
- data/spec/chunky_png/canvas/png_encoding_spec.rb +59 -50
- data/spec/chunky_png/canvas/resampling_spec.rb +19 -21
- data/spec/chunky_png/canvas/stream_exporting_spec.rb +47 -27
- data/spec/chunky_png/canvas/stream_importing_spec.rb +10 -11
- data/spec/chunky_png/canvas_spec.rb +57 -52
- data/spec/chunky_png/color_spec.rb +115 -114
- data/spec/chunky_png/datastream_spec.rb +49 -51
- data/spec/chunky_png/dimension_spec.rb +10 -10
- data/spec/chunky_png/image_spec.rb +11 -14
- data/spec/chunky_png/point_spec.rb +21 -23
- data/spec/chunky_png/rmagick_spec.rb +7 -8
- data/spec/chunky_png/vector_spec.rb +21 -17
- data/spec/chunky_png_spec.rb +2 -2
- data/spec/png_suite_spec.rb +35 -40
- data/spec/spec_helper.rb +6 -10
- data/tasks/benchmarks.rake +7 -8
- metadata +34 -5
- data/lib/chunky_png/compatibility.rb +0 -15
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
1
|
module ChunkyPNG
|
5
2
|
class Canvas
|
6
|
-
|
7
|
-
# The ChunkyPNG::Canvas::Resampling module defines methods to perform image resampling to
|
3
|
+
# The ChunkyPNG::Canvas::Resampling module defines methods to perform image resampling to
|
8
4
|
# a {ChunkyPNG::Canvas}.
|
9
5
|
#
|
10
6
|
# Currently, only the nearest neighbor algorithm is implemented. Bilinear and cubic
|
@@ -12,22 +8,21 @@ module ChunkyPNG
|
|
12
8
|
#
|
13
9
|
# @see ChunkyPNG::Canvas
|
14
10
|
module Resampling
|
15
|
-
|
16
11
|
# Integer Interpolation between two values
|
17
12
|
#
|
18
13
|
# Used for generating indicies for interpolation (eg, nearest
|
19
14
|
# neighbour).
|
20
15
|
#
|
21
|
-
# @param [Integer] width The width of the source
|
16
|
+
# @param [Integer] width The width of the source
|
22
17
|
# @param [Integer] new_width The width of the destination
|
23
18
|
# @return [Array<Integer>] An Array of Integer indicies
|
24
19
|
def steps(width, new_width)
|
25
20
|
indicies, residues = steps_residues(width, new_width)
|
26
|
-
|
21
|
+
|
27
22
|
for i in 1..new_width
|
28
|
-
indicies[i-1] = (indicies[i-1] + (residues[i-1] + 127)/255)
|
23
|
+
indicies[i - 1] = (indicies[i - 1] + (residues[i - 1] + 127) / 255)
|
29
24
|
end
|
30
|
-
|
25
|
+
indicies
|
31
26
|
end
|
32
27
|
|
33
28
|
# Fractional Interpolation between two values
|
@@ -39,9 +34,9 @@ module ChunkyPNG
|
|
39
34
|
# @param [Integer] new_width The width of the destination
|
40
35
|
# @return [Array<Integer>, Array<Integer>] Two arrays of indicies and residues
|
41
36
|
def steps_residues(width, new_width)
|
42
|
-
indicies = Array.new(new_width,
|
43
|
-
residues = Array.new(new_width,
|
44
|
-
|
37
|
+
indicies = Array.new(new_width, nil)
|
38
|
+
residues = Array.new(new_width, nil)
|
39
|
+
|
45
40
|
# This works by accumulating the fractional error and
|
46
41
|
# overflowing when necessary.
|
47
42
|
|
@@ -49,15 +44,15 @@ module ChunkyPNG
|
|
49
44
|
# 2 * new_width
|
50
45
|
base_step = width / new_width
|
51
46
|
err_step = (width % new_width) << 1
|
52
|
-
denominator =
|
53
|
-
|
47
|
+
denominator = new_width << 1
|
48
|
+
|
54
49
|
# Initial pixel
|
55
50
|
index = (width - new_width) / denominator
|
56
51
|
err = (width - new_width) % denominator
|
57
52
|
|
58
53
|
for i in 1..new_width
|
59
|
-
indicies[i-1] = index
|
60
|
-
residues[i-1] = (255.0 * err.to_f / denominator.to_f).round
|
54
|
+
indicies[i - 1] = index
|
55
|
+
residues[i - 1] = (255.0 * err.to_f / denominator.to_f).round
|
61
56
|
|
62
57
|
index += base_step
|
63
58
|
err += err_step
|
@@ -67,10 +62,9 @@ module ChunkyPNG
|
|
67
62
|
end
|
68
63
|
end
|
69
64
|
|
70
|
-
|
65
|
+
[indicies, residues]
|
71
66
|
end
|
72
67
|
|
73
|
-
|
74
68
|
# Resamples the canvas using nearest neighbor interpolation.
|
75
69
|
# @param [Integer] new_width The width of the resampled canvas.
|
76
70
|
# @param [Integer] new_height The height of the resampled canvas.
|
@@ -79,8 +73,7 @@ module ChunkyPNG
|
|
79
73
|
steps_x = steps(width, new_width)
|
80
74
|
steps_y = steps(height, new_height)
|
81
75
|
|
82
|
-
|
83
|
-
pixels = Array.new(new_width*new_height)
|
76
|
+
pixels = Array.new(new_width * new_height)
|
84
77
|
i = 0
|
85
78
|
for y in steps_y
|
86
79
|
for x in steps_x
|
@@ -88,10 +81,10 @@ module ChunkyPNG
|
|
88
81
|
i += 1
|
89
82
|
end
|
90
83
|
end
|
91
|
-
|
84
|
+
|
92
85
|
replace_canvas!(new_width.to_i, new_height.to_i, pixels)
|
93
86
|
end
|
94
|
-
|
87
|
+
|
95
88
|
def resample_nearest_neighbor(new_width, new_height)
|
96
89
|
dup.resample_nearest_neighbor!(new_width, new_height)
|
97
90
|
end
|
@@ -104,19 +97,19 @@ module ChunkyPNG
|
|
104
97
|
index_x, interp_x = steps_residues(width, new_width)
|
105
98
|
index_y, interp_y = steps_residues(height, new_height)
|
106
99
|
|
107
|
-
pixels = Array.new(new_width*new_height)
|
100
|
+
pixels = Array.new(new_width * new_height)
|
108
101
|
i = 0
|
109
102
|
for y in 1..new_height
|
110
103
|
# Clamp the indicies to the edges of the image
|
111
|
-
y1 = [index_y[y-1], 0].max
|
112
|
-
y2 = [index_y[y-1] + 1, height - 1].min
|
113
|
-
y_residue = interp_y[y-1]
|
104
|
+
y1 = [index_y[y - 1], 0].max
|
105
|
+
y2 = [index_y[y - 1] + 1, height - 1].min
|
106
|
+
y_residue = interp_y[y - 1]
|
114
107
|
|
115
108
|
for x in 1..new_width
|
116
109
|
# Clamp the indicies to the edges of the image
|
117
|
-
x1 = [index_x[x-1], 0].max
|
118
|
-
x2 = [index_x[x-1] + 1, width - 1].min
|
119
|
-
x_residue = interp_x[x-1]
|
110
|
+
x1 = [index_x[x - 1], 0].max
|
111
|
+
x2 = [index_x[x - 1] + 1, width - 1].min
|
112
|
+
x_residue = interp_x[x - 1]
|
120
113
|
|
121
114
|
pixel_11 = get_pixel(x1, y1)
|
122
115
|
pixel_21 = get_pixel(x2, y1)
|
@@ -139,9 +132,9 @@ module ChunkyPNG
|
|
139
132
|
def resample_bilinear(new_width, new_height)
|
140
133
|
dup.resample_bilinear!(new_width, new_height)
|
141
134
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
135
|
+
|
136
|
+
alias resample resample_nearest_neighbor
|
137
|
+
alias resize resample
|
145
138
|
end
|
146
139
|
end
|
147
140
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module ChunkyPNG
|
2
2
|
class Canvas
|
3
|
-
|
4
3
|
# Methods to save load a canvas from to stream, encoded in RGB, RGBA, BGR or ABGR format.
|
5
4
|
module StreamExporting
|
6
|
-
|
7
5
|
# Creates an RGB-formatted pixelstream with the pixel data from this canvas.
|
8
6
|
#
|
9
7
|
# Note that this format is fast but bloated, because no compression is used
|
@@ -12,7 +10,7 @@ module ChunkyPNG
|
|
12
10
|
#
|
13
11
|
# @return [String] The RGBA-formatted pixel data.
|
14
12
|
def to_rgba_stream
|
15
|
-
pixels.pack(
|
13
|
+
pixels.pack("N*")
|
16
14
|
end
|
17
15
|
|
18
16
|
# Creates an RGB-formatted pixelstream with the pixel data from this canvas.
|
@@ -23,14 +21,14 @@ module ChunkyPNG
|
|
23
21
|
#
|
24
22
|
# @return [String] The RGB-formatted pixel data.
|
25
23
|
def to_rgb_stream
|
26
|
-
pixels.pack(
|
24
|
+
pixels.pack("NX" * pixels.length)
|
27
25
|
end
|
28
|
-
|
26
|
+
|
29
27
|
# Creates a stream of the alpha channel of this canvas.
|
30
28
|
#
|
31
29
|
# @return [String] The 0-255 alpha values of all pixels packed as string
|
32
30
|
def to_alpha_channel_stream
|
33
|
-
pixels.pack(
|
31
|
+
pixels.pack("C*")
|
34
32
|
end
|
35
33
|
|
36
34
|
# Creates a grayscale stream of this canvas.
|
@@ -40,7 +38,7 @@ module ChunkyPNG
|
|
40
38
|
#
|
41
39
|
# @return [String] The 0-255 grayscale values of all pixels packed as string.
|
42
40
|
def to_grayscale_stream
|
43
|
-
pixels.pack(
|
41
|
+
pixels.pack("nX" * pixels.length)
|
44
42
|
end
|
45
43
|
|
46
44
|
# Creates an ABGR-formatted pixelstream with the pixel data from this canvas.
|
@@ -51,7 +49,7 @@ module ChunkyPNG
|
|
51
49
|
#
|
52
50
|
# @return [String] The RGBA-formatted pixel data.
|
53
51
|
def to_abgr_stream
|
54
|
-
pixels.pack(
|
52
|
+
pixels.pack("V*")
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module ChunkyPNG
|
2
2
|
class Canvas
|
3
|
-
|
4
3
|
# Methods to quickly load a canvas from a stream, encoded in RGB, RGBA, BGR or ABGR format.
|
5
4
|
module StreamImporting
|
6
|
-
|
7
5
|
# Creates a canvas by reading pixels from an RGB formatted stream with a
|
8
6
|
# provided with and height.
|
9
7
|
#
|
@@ -18,9 +16,9 @@ module ChunkyPNG
|
|
18
16
|
def from_rgb_stream(width, height, stream)
|
19
17
|
string = stream.respond_to?(:read) ? stream.read(3 * width * height) : stream.to_s[0, 3 * width * height]
|
20
18
|
string << ChunkyPNG::EXTRA_BYTE # Add a fourth byte to the last RGB triple.
|
21
|
-
unpacker =
|
19
|
+
unpacker = "NX" * (width * height)
|
22
20
|
pixels = string.unpack(unpacker).map { |color| color | 0x000000ff }
|
23
|
-
|
21
|
+
new(width, height, pixels)
|
24
22
|
end
|
25
23
|
|
26
24
|
# Creates a canvas by reading pixels from an RGBA formatted stream with a
|
@@ -36,7 +34,7 @@ module ChunkyPNG
|
|
36
34
|
# @return [ChunkyPNG::Canvas] The newly constructed canvas instance.
|
37
35
|
def from_rgba_stream(width, height, stream)
|
38
36
|
string = stream.respond_to?(:read) ? stream.read(4 * width * height) : stream.to_s[0, 4 * width * height]
|
39
|
-
|
37
|
+
new(width, height, string.unpack("N*"))
|
40
38
|
end
|
41
39
|
|
42
40
|
# Creates a canvas by reading pixels from an BGR formatted stream with a
|
@@ -53,8 +51,8 @@ module ChunkyPNG
|
|
53
51
|
def from_bgr_stream(width, height, stream)
|
54
52
|
string = ChunkyPNG::EXTRA_BYTE.dup # Add a first byte to the first BGR triple.
|
55
53
|
string << (stream.respond_to?(:read) ? stream.read(3 * width * height) : stream.to_s[0, 3 * width * height])
|
56
|
-
pixels = string.unpack("@1" << (
|
57
|
-
|
54
|
+
pixels = string.unpack("@1" << ("XV" * (width * height))).map { |color| color | 0x000000ff }
|
55
|
+
new(width, height, pixels)
|
58
56
|
end
|
59
57
|
|
60
58
|
# Creates a canvas by reading pixels from an ARGB formatted stream with a
|
@@ -70,7 +68,7 @@ module ChunkyPNG
|
|
70
68
|
# @return [ChunkyPNG::Canvas] The newly constructed canvas instance.
|
71
69
|
def from_abgr_stream(width, height, stream)
|
72
70
|
string = stream.respond_to?(:read) ? stream.read(4 * width * height) : stream.to_s[0, 4 * width * height]
|
73
|
-
|
71
|
+
new(width, height, string.unpack("V*"))
|
74
72
|
end
|
75
73
|
end
|
76
74
|
end
|
data/lib/chunky_png/chunk.rb
CHANGED
@@ -16,10 +16,10 @@ module ChunkyPNG
|
|
16
16
|
# @param io [IO, #read] The IO stream to read from.
|
17
17
|
# @return [ChunkyPNG::Chung::Base] The loaded chunk instance.
|
18
18
|
def self.read(io)
|
19
|
-
length, type = read_bytes(io, 8).unpack(
|
19
|
+
length, type = read_bytes(io, 8).unpack("Na4")
|
20
20
|
|
21
21
|
content = read_bytes(io, length)
|
22
|
-
crc = read_bytes(io, 4).unpack(
|
22
|
+
crc = read_bytes(io, 4).unpack("N").first
|
23
23
|
verify_crc!(type, content, crc)
|
24
24
|
|
25
25
|
CHUNK_TYPES.fetch(type, Generic).read(type, content)
|
@@ -74,8 +74,8 @@ module ChunkyPNG
|
|
74
74
|
# @param io [IO] The IO stream to write to.
|
75
75
|
# @param content [String] The content for this chunk.
|
76
76
|
def write_with_crc(io, content)
|
77
|
-
io << [content.length].pack(
|
78
|
-
io << [Zlib.crc32(content, Zlib.crc32(type))].pack(
|
77
|
+
io << [content.length].pack("N") << type << content
|
78
|
+
io << [Zlib.crc32(content, Zlib.crc32(type))].pack("N")
|
79
79
|
end
|
80
80
|
|
81
81
|
# Writes the chunk to the IO stream.
|
@@ -84,7 +84,7 @@ module ChunkyPNG
|
|
84
84
|
# and will calculate and append the checksum automatically.
|
85
85
|
# @param io [IO] The IO stream to write to.
|
86
86
|
def write(io)
|
87
|
-
write_with_crc(io, content ||
|
87
|
+
write_with_crc(io, content || "")
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
@@ -95,8 +95,8 @@ module ChunkyPNG
|
|
95
95
|
# written by the +write+ method.
|
96
96
|
attr_accessor :content
|
97
97
|
|
98
|
-
def initialize(type, content =
|
99
|
-
super(type, :
|
98
|
+
def initialize(type, content = "")
|
99
|
+
super(type, content: content)
|
100
100
|
end
|
101
101
|
|
102
102
|
# Creates an instance, given the chunk's type and content.
|
@@ -118,11 +118,10 @@ module ChunkyPNG
|
|
118
118
|
# Note that it is still possible to access the chunk for such an image, but
|
119
119
|
# ChunkyPNG will raise an exception if you try to access the pixel data.
|
120
120
|
class Header < Base
|
121
|
-
attr_accessor :width, :height, :depth, :color, :compression, :filtering,
|
122
|
-
:interlace
|
121
|
+
attr_accessor :width, :height, :depth, :color, :compression, :filtering, :interlace
|
123
122
|
|
124
123
|
def initialize(attrs = {})
|
125
|
-
super(
|
124
|
+
super("IHDR", attrs)
|
126
125
|
@depth ||= 8
|
127
126
|
@color ||= ChunkyPNG::COLOR_TRUECOLOR
|
128
127
|
@compression ||= ChunkyPNG::COMPRESSION_DEFAULT
|
@@ -137,31 +136,39 @@ module ChunkyPNG
|
|
137
136
|
# @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the
|
138
137
|
# variables set to the values according to the content.
|
139
138
|
def self.read(type, content)
|
140
|
-
fields = content.unpack(
|
141
|
-
new(
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
139
|
+
fields = content.unpack("NNC5")
|
140
|
+
new(
|
141
|
+
width: fields[0],
|
142
|
+
height: fields[1],
|
143
|
+
depth: fields[2],
|
144
|
+
color: fields[3],
|
145
|
+
compression: fields[4],
|
146
|
+
filtering: fields[5],
|
147
|
+
interlace: fields[6]
|
148
|
+
)
|
148
149
|
end
|
149
150
|
|
150
151
|
# Returns the content for this chunk when it gets written to a file, by
|
151
152
|
# packing the image information variables into the correct format.
|
152
153
|
# @return [String] The 13-byte content for the header chunk.
|
153
154
|
def content
|
154
|
-
[
|
155
|
-
|
155
|
+
[
|
156
|
+
width,
|
157
|
+
height,
|
158
|
+
depth,
|
159
|
+
color,
|
160
|
+
compression,
|
161
|
+
filtering,
|
162
|
+
interlace,
|
163
|
+
].pack("NNC5")
|
156
164
|
end
|
157
165
|
end
|
158
166
|
|
159
167
|
# The End (IEND) chunk indicates the last chunk of a PNG stream. It does
|
160
168
|
# not contain any data.
|
161
169
|
class End < Base
|
162
|
-
|
163
170
|
def initialize
|
164
|
-
super(
|
171
|
+
super("IEND")
|
165
172
|
end
|
166
173
|
|
167
174
|
# Reads the END chunk. It will check if the content is empty.
|
@@ -172,8 +179,8 @@ module ChunkyPNG
|
|
172
179
|
# @return [ChunkyPNG::Chunk::End] The new End chunk instance.
|
173
180
|
# @raise [ChunkyPNG::ExpectationFailed] Raises an exception if the content was not empty.
|
174
181
|
def self.read(type, content)
|
175
|
-
raise ExpectationFailed,
|
176
|
-
|
182
|
+
raise ExpectationFailed, "The IEND chunk should be empty!" if content.bytesize > 0
|
183
|
+
new
|
177
184
|
end
|
178
185
|
|
179
186
|
# Returns an empty string, because this chunk should always be empty.
|
@@ -214,7 +221,7 @@ module ChunkyPNG
|
|
214
221
|
# @return [Array<Integer>] Returns an array of alpha channel values
|
215
222
|
# [0-255].
|
216
223
|
def palette_alpha_channel
|
217
|
-
content.unpack(
|
224
|
+
content.unpack("C*")
|
218
225
|
end
|
219
226
|
|
220
227
|
# Returns the truecolor entry to be replaced by transparent pixels,
|
@@ -224,9 +231,8 @@ module ChunkyPNG
|
|
224
231
|
#
|
225
232
|
# @return [Integer] The color to replace with fully transparent pixels.
|
226
233
|
def truecolor_entry(bit_depth)
|
227
|
-
|
228
|
-
|
229
|
-
end
|
234
|
+
decode_method_name = :"decode_png_resample_#{bit_depth}bit_value"
|
235
|
+
values = content.unpack("nnn").map { |c| ChunkyPNG::Canvas.send(decode_method_name, c) }
|
230
236
|
ChunkyPNG::Color.rgb(*values)
|
231
237
|
end
|
232
238
|
|
@@ -238,7 +244,7 @@ module ChunkyPNG
|
|
238
244
|
# @return [Integer] The (grayscale) color to replace with fully
|
239
245
|
# transparent pixels.
|
240
246
|
def grayscale_entry(bit_depth)
|
241
|
-
value = ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", content.unpack(
|
247
|
+
value = ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", content.unpack("n")[0])
|
242
248
|
ChunkyPNG::Color.grayscale(value)
|
243
249
|
end
|
244
250
|
end
|
@@ -255,7 +261,7 @@ module ChunkyPNG
|
|
255
261
|
def self.split_in_chunks(data, level = Zlib::DEFAULT_COMPRESSION, chunk_size = 2147483647)
|
256
262
|
streamdata = Zlib::Deflate.deflate(data, level)
|
257
263
|
# TODO: Split long streamdata over multiple chunks
|
258
|
-
[
|
264
|
+
[ChunkyPNG::Chunk::ImageData.new("IDAT", streamdata)]
|
259
265
|
end
|
260
266
|
end
|
261
267
|
|
@@ -271,12 +277,12 @@ module ChunkyPNG
|
|
271
277
|
attr_accessor :keyword, :value
|
272
278
|
|
273
279
|
def initialize(keyword, value)
|
274
|
-
super(
|
280
|
+
super("tEXt")
|
275
281
|
@keyword, @value = keyword, value
|
276
282
|
end
|
277
283
|
|
278
284
|
def self.read(type, content)
|
279
|
-
keyword, value = content.unpack(
|
285
|
+
keyword, value = content.unpack("Z*a*")
|
280
286
|
new(keyword, value)
|
281
287
|
end
|
282
288
|
|
@@ -285,7 +291,7 @@ module ChunkyPNG
|
|
285
291
|
#
|
286
292
|
# @return The content that should be written to the datastream.
|
287
293
|
def content
|
288
|
-
[keyword, value].pack(
|
294
|
+
[keyword, value].pack("Z*a*")
|
289
295
|
end
|
290
296
|
end
|
291
297
|
|
@@ -299,12 +305,12 @@ module ChunkyPNG
|
|
299
305
|
attr_accessor :keyword, :value
|
300
306
|
|
301
307
|
def initialize(keyword, value)
|
302
|
-
super(
|
308
|
+
super("zTXt")
|
303
309
|
@keyword, @value = keyword, value
|
304
310
|
end
|
305
311
|
|
306
312
|
def self.read(type, content)
|
307
|
-
keyword, compression, value = content.unpack(
|
313
|
+
keyword, compression, value = content.unpack("Z*Ca*")
|
308
314
|
raise ChunkyPNG::NotSupported, "Compression method #{compression.inspect} not supported!" unless compression == ChunkyPNG::COMPRESSION_DEFAULT
|
309
315
|
new(keyword, Zlib::Inflate.inflate(value))
|
310
316
|
end
|
@@ -314,8 +320,11 @@ module ChunkyPNG
|
|
314
320
|
#
|
315
321
|
# @return The content that should be written to the datastream.
|
316
322
|
def content
|
317
|
-
[
|
318
|
-
|
323
|
+
[
|
324
|
+
keyword,
|
325
|
+
ChunkyPNG::COMPRESSION_DEFAULT,
|
326
|
+
Zlib::Deflate.deflate(value),
|
327
|
+
].pack("Z*Ca*")
|
319
328
|
end
|
320
329
|
end
|
321
330
|
|
@@ -327,23 +336,23 @@ module ChunkyPNG
|
|
327
336
|
attr_accessor :ppux, :ppuy, :unit
|
328
337
|
|
329
338
|
def initialize(ppux, ppuy, unit = :unknown)
|
330
|
-
raise ArgumentError,
|
331
|
-
super(
|
339
|
+
raise ArgumentError, "unit must be either :meters or :unknown" unless [:meters, :unknown].member?(unit)
|
340
|
+
super("pHYs")
|
332
341
|
@ppux, @ppuy, @unit = ppux, ppuy, unit
|
333
342
|
end
|
334
343
|
|
335
344
|
def dpix
|
336
|
-
raise ChunkyPNG::UnitsUnknown,
|
345
|
+
raise ChunkyPNG::UnitsUnknown, "the PNG specifies its physical aspect ratio, but does not specify the units of its pixels' physical dimensions" unless unit == :meters
|
337
346
|
ppux * INCHES_PER_METER
|
338
347
|
end
|
339
348
|
|
340
349
|
def dpiy
|
341
|
-
raise ChunkyPNG::UnitsUnknown,
|
350
|
+
raise ChunkyPNG::UnitsUnknown, "the PNG specifies its physical aspect ratio, but does not specify the units of its pixels' physical dimensions" unless unit == :meters
|
342
351
|
ppuy * INCHES_PER_METER
|
343
352
|
end
|
344
353
|
|
345
354
|
def self.read(type, content)
|
346
|
-
ppux, ppuy, unit = content.unpack(
|
355
|
+
ppux, ppuy, unit = content.unpack("NNC")
|
347
356
|
unit = unit == 1 ? :meters : :unknown
|
348
357
|
new(ppux, ppuy, unit)
|
349
358
|
end
|
@@ -351,7 +360,7 @@ module ChunkyPNG
|
|
351
360
|
# Assembles the content to write to the stream for this chunk.
|
352
361
|
# @return [String] The binary content that should be written to the datastream.
|
353
362
|
def content
|
354
|
-
[ppux, ppuy, unit == :meters ? 1 : 0].pack(
|
363
|
+
[ppux, ppuy, unit == :meters ? 1 : 0].pack("NNC")
|
355
364
|
end
|
356
365
|
|
357
366
|
INCHES_PER_METER = 0.0254
|
@@ -372,8 +381,8 @@ module ChunkyPNG
|
|
372
381
|
class InternationalText < Base
|
373
382
|
attr_accessor :keyword, :text, :language_tag, :translated_keyword, :compressed, :compression
|
374
383
|
|
375
|
-
def initialize(keyword, text, language_tag =
|
376
|
-
super(
|
384
|
+
def initialize(keyword, text, language_tag = "", translated_keyword = "", compressed = ChunkyPNG::UNCOMPRESSED_CONTENT, compression = ChunkyPNG::COMPRESSION_DEFAULT)
|
385
|
+
super("iTXt")
|
377
386
|
@keyword = keyword
|
378
387
|
@text = text
|
379
388
|
@language_tag = language_tag
|
@@ -390,16 +399,16 @@ module ChunkyPNG
|
|
390
399
|
# @raise [ChunkyPNG::NotSupported] If the chunk refers to an unsupported compression method.
|
391
400
|
# Currently uncompressed data and deflate are supported.
|
392
401
|
def self.read(type, content)
|
393
|
-
keyword, compressed, compression, language_tag, translated_keyword, text = content.unpack(
|
402
|
+
keyword, compressed, compression, language_tag, translated_keyword, text = content.unpack("Z*CCZ*Z*a*")
|
394
403
|
raise ChunkyPNG::NotSupported, "Compression flag #{compressed.inspect} not supported!" unless compressed == ChunkyPNG::UNCOMPRESSED_CONTENT || compressed == ChunkyPNG::COMPRESSED_CONTENT
|
395
404
|
raise ChunkyPNG::NotSupported, "Compression method #{compression.inspect} not supported!" unless compression == ChunkyPNG::COMPRESSION_DEFAULT
|
396
405
|
|
397
406
|
text = Zlib::Inflate.inflate(text) if compressed == ChunkyPNG::COMPRESSED_CONTENT
|
398
407
|
|
399
|
-
text.force_encoding(
|
408
|
+
text.force_encoding("utf-8")
|
400
409
|
raise ChunkyPNG::InvalidUTF8, "Invalid unicode encountered in iTXt chunk" unless text.valid_encoding?
|
401
410
|
|
402
|
-
translated_keyword.force_encoding(
|
411
|
+
translated_keyword.force_encoding("utf-8")
|
403
412
|
raise ChunkyPNG::InvalidUTF8, "Invalid unicode encountered in iTXt chunk" unless translated_keyword.valid_encoding?
|
404
413
|
|
405
414
|
new(keyword, text, language_tag, translated_keyword, compressed, compression)
|
@@ -408,10 +417,10 @@ module ChunkyPNG
|
|
408
417
|
# Assembles the content to write to the stream for this chunk.
|
409
418
|
# @return [String] The binary content that should be written to the datastream.
|
410
419
|
def content
|
411
|
-
text_field = text.encode(
|
412
|
-
text_field =
|
420
|
+
text_field = text.encode("utf-8")
|
421
|
+
text_field = compressed == ChunkyPNG::COMPRESSED_CONTENT ? Zlib::Deflate.deflate(text_field) : text_field
|
413
422
|
|
414
|
-
[keyword, compressed, compression, language_tag, translated_keyword.encode(
|
423
|
+
[keyword, compressed, compression, language_tag, translated_keyword.encode("utf-8"), text_field].pack("Z*CCZ*Z*a*")
|
415
424
|
end
|
416
425
|
end
|
417
426
|
|
@@ -423,15 +432,15 @@ module ChunkyPNG
|
|
423
432
|
#
|
424
433
|
# @see ChunkyPNG::Chunk.read
|
425
434
|
CHUNK_TYPES = {
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
+
"IHDR" => Header,
|
436
|
+
"IEND" => End,
|
437
|
+
"IDAT" => ImageData,
|
438
|
+
"PLTE" => Palette,
|
439
|
+
"tRNS" => Transparency,
|
440
|
+
"tEXt" => Text,
|
441
|
+
"zTXt" => CompressedText,
|
442
|
+
"iTXt" => InternationalText,
|
443
|
+
"pHYs" => Physical,
|
435
444
|
}
|
436
445
|
end
|
437
446
|
end
|