chunky_png 1.3.0 → 1.3.1
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 +3 -0
- data/lib/chunky_png/canvas.rb +99 -62
- data/lib/chunky_png/canvas/drawing.rb +91 -68
- data/lib/chunky_png/canvas/operations.rb +155 -120
- data/lib/chunky_png/canvas/stream_importing.rb +2 -2
- data/lib/chunky_png/chunk.rb +111 -93
- data/lib/chunky_png/color.rb +312 -253
- data/lib/chunky_png/datastream.rb +1 -1
- data/lib/chunky_png/palette.rb +66 -49
- data/lib/chunky_png/version.rb +1 -1
- data/spec/chunky_png/canvas/stream_importing_spec.rb +9 -0
- data/spec/chunky_png/color_spec.rb +92 -68
- data/spec/resources/pixelstream.bgr +67 -0
- metadata +4 -2
@@ -51,8 +51,8 @@ module ChunkyPNG
|
|
51
51
|
# @param [#read, String] stream The stream to read the pixel data from.
|
52
52
|
# @return [ChunkyPNG::Canvas] The newly constructed canvas instance.
|
53
53
|
def from_bgr_stream(width, height, stream)
|
54
|
-
string = ChunkyPNG::EXTRA_BYTE # Add a first byte to the first BGR triple.
|
55
|
-
string << stream.respond_to?(:read) ? stream.read(3 * width * height) : stream.to_s[0, 3 * width * height]
|
54
|
+
string = ChunkyPNG::EXTRA_BYTE.dup # Add a first byte to the first BGR triple.
|
55
|
+
string << (stream.respond_to?(:read) ? stream.read(3 * width * height) : stream.to_s[0, 3 * width * height])
|
56
56
|
pixels = string.unpack("@1" << ('XV' * (width * height))).map { |color| color | 0x000000ff }
|
57
57
|
self.new(width, height, pixels)
|
58
58
|
end
|
data/lib/chunky_png/chunk.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module ChunkyPNG
|
2
|
-
|
3
2
|
# A PNG datastream consists of multiple chunks. This module, and the classes
|
4
|
-
# contained within, help with handling these chunks. It supports both
|
5
|
-
#
|
3
|
+
# contained within, help with handling these chunks. It supports both reading
|
4
|
+
# and writing chunks.
|
6
5
|
#
|
7
6
|
# All chunk types are instances of the {ChunkyPNG::Chunk::Base} class. For
|
8
7
|
# some chunk types a specialized class is available, e.g. the IHDR chunk is
|
@@ -12,13 +11,11 @@ module ChunkyPNG
|
|
12
11
|
#
|
13
12
|
# @see ChunkyPNG::Datastream
|
14
13
|
module Chunk
|
15
|
-
|
16
14
|
# Reads a chunk from an IO stream.
|
17
15
|
#
|
18
|
-
# @param [IO, #read]
|
16
|
+
# @param io [IO, #read] The IO stream to read from.
|
19
17
|
# @return [ChunkyPNG::Chung::Base] The loaded chunk instance.
|
20
18
|
def self.read(io)
|
21
|
-
|
22
19
|
length, type = io.read(8).unpack('Na4')
|
23
20
|
content = io.read(length)
|
24
21
|
crc = io.read(4).unpack('N').first
|
@@ -27,13 +24,13 @@ module ChunkyPNG
|
|
27
24
|
|
28
25
|
CHUNK_TYPES.fetch(type, Generic).read(type, content)
|
29
26
|
end
|
30
|
-
|
27
|
+
|
31
28
|
# Verifies the CRC of a chunk.
|
32
|
-
# @param [String]
|
33
|
-
# @param [String]
|
34
|
-
# @param [Integer]
|
35
|
-
# @raise [RuntimeError] An exception is raised if the found CRC value
|
36
|
-
#
|
29
|
+
# @param type [String] The chunk's type.
|
30
|
+
# @param content [String] The chunk's content.
|
31
|
+
# @param found_crc [Integer] The chunk's found CRC value.
|
32
|
+
# @raise [RuntimeError] An exception is raised if the found CRC value is
|
33
|
+
# not equal to the expected CRC value.
|
37
34
|
def self.verify_crc!(type, content, found_crc)
|
38
35
|
expected_crc = Zlib.crc32(content, Zlib.crc32(type))
|
39
36
|
raise ChunkyPNG::CRCMismatch, "Chuck CRC mismatch!" if found_crc != expected_crc
|
@@ -47,15 +44,14 @@ module ChunkyPNG
|
|
47
44
|
#
|
48
45
|
# @abstract
|
49
46
|
class Base
|
50
|
-
|
51
47
|
# The four-character type indicator for the chunk. This field is used to
|
52
48
|
# find the correct class for a chunk when it is loaded from a PNG stream.
|
53
49
|
# @return [String]
|
54
50
|
attr_accessor :type
|
55
51
|
|
56
52
|
# Initializes the chunk instance.
|
57
|
-
# @param [String]
|
58
|
-
# @param [Hash]
|
53
|
+
# @param type [String] The four character chunk type indicator.
|
54
|
+
# @param attributes [Hash] A hash of attributes to set on this chunk.
|
59
55
|
def initialize(type, attributes = {})
|
60
56
|
self.type = type
|
61
57
|
attributes.each { |k, v| send("#{k}=", v) }
|
@@ -63,8 +59,8 @@ module ChunkyPNG
|
|
63
59
|
|
64
60
|
# Writes the chunk to the IO stream, using the provided content.
|
65
61
|
# The checksum will be calculated and appended to the stream.
|
66
|
-
# @param [IO]
|
67
|
-
# @param [String]
|
62
|
+
# @param io [IO] The IO stream to write to.
|
63
|
+
# @param content [String] The content for this chunk.
|
68
64
|
def write_with_crc(io, content)
|
69
65
|
io << [content.length].pack('N') << type << content
|
70
66
|
io << [Zlib.crc32(content, Zlib.crc32(type))].pack('N')
|
@@ -74,47 +70,44 @@ module ChunkyPNG
|
|
74
70
|
#
|
75
71
|
# It will call the +content+ method to get the content for this chunk,
|
76
72
|
# and will calculate and append the checksum automatically.
|
77
|
-
# @param [IO]
|
73
|
+
# @param io [IO] The IO stream to write to.
|
78
74
|
def write(io)
|
79
75
|
write_with_crc(io, content || '')
|
80
76
|
end
|
81
77
|
end
|
82
78
|
|
83
|
-
# The Generic chunk type will read the content from the chunk as it,
|
79
|
+
# The Generic chunk type will read the content from the chunk as it,
|
84
80
|
# and will write it back as it was read.
|
85
81
|
class Generic < Base
|
86
|
-
|
87
|
-
# The attribute to store the content from the chunk, which gets
|
82
|
+
# The attribute to store the content from the chunk, which gets
|
88
83
|
# written by the +write+ method.
|
89
84
|
attr_accessor :content
|
90
85
|
|
91
|
-
|
92
86
|
def initialize(type, content = '')
|
93
87
|
super(type, :content => content)
|
94
88
|
end
|
95
89
|
|
96
90
|
# Creates an instance, given the chunk's type and content.
|
97
|
-
# @param [String]
|
98
|
-
# @param [String]
|
91
|
+
# @param type [String] The four character chunk type indicator.
|
92
|
+
# @param content [String] The content read from the chunk.
|
99
93
|
# @return [ChunkyPNG::Chunk::Generic] The new chunk instance.
|
100
94
|
def self.read(type, content)
|
101
|
-
|
95
|
+
new(type, content)
|
102
96
|
end
|
103
97
|
end
|
104
98
|
|
105
|
-
# The header (IHDR) chunk is the first chunk of every PNG image, and
|
106
|
-
# contains information about the image: i.e. its width, height, color
|
99
|
+
# The header (IHDR) chunk is the first chunk of every PNG image, and
|
100
|
+
# contains information about the image: i.e. its width, height, color
|
107
101
|
# depth, color mode, compression method, filtering method and interlace
|
108
102
|
# method.
|
109
103
|
#
|
110
|
-
# ChunkyPNG supports all values for these variables that are defined in
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# the pixel data.
|
104
|
+
# ChunkyPNG supports all values for these variables that are defined in the
|
105
|
+
# PNG spec, except for color depth: Only 8-bit depth images are supported.
|
106
|
+
# Note that it is still possible to access the chunk for such an image, but
|
107
|
+
# ChunkyPNG will raise an exception if you try to access the pixel data.
|
115
108
|
class Header < Base
|
116
|
-
|
117
|
-
|
109
|
+
attr_accessor :width, :height, :depth, :color, :compression, :filtering,
|
110
|
+
:interlace
|
118
111
|
|
119
112
|
def initialize(attrs = {})
|
120
113
|
super('IHDR', attrs)
|
@@ -125,36 +118,45 @@ module ChunkyPNG
|
|
125
118
|
@interlace ||= ChunkyPNG::INTERLACING_NONE
|
126
119
|
end
|
127
120
|
|
128
|
-
# Reads the 13 bytes of content from the header chunk to set the image
|
129
|
-
#
|
130
|
-
# @param [String]
|
131
|
-
# @
|
132
|
-
#
|
121
|
+
# Reads the 13 bytes of content from the header chunk to set the image
|
122
|
+
# attributes.
|
123
|
+
# @param type [String] The four character chunk type indicator (= "IHDR").
|
124
|
+
# @param content [String] The 13 bytes of content read from the chunk.
|
125
|
+
# @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the
|
126
|
+
# variables set to the values according to the content.
|
133
127
|
def self.read(type, content)
|
134
128
|
fields = content.unpack('NNC5')
|
135
|
-
|
136
|
-
|
129
|
+
new(:width => fields[0],
|
130
|
+
:height => fields[1],
|
131
|
+
:depth => fields[2],
|
132
|
+
:color => fields[3],
|
133
|
+
:compression => fields[4],
|
134
|
+
:filtering => fields[5],
|
135
|
+
:interlace => fields[6])
|
137
136
|
end
|
138
137
|
|
139
|
-
# Returns the content for this chunk when it gets written to a file, by
|
140
|
-
# image information variables into the correct format.
|
138
|
+
# Returns the content for this chunk when it gets written to a file, by
|
139
|
+
# packing the image information variables into the correct format.
|
141
140
|
# @return [String] The 13-byte content for the header chunk.
|
142
141
|
def content
|
143
|
-
[width, height, depth, color, compression, filtering, interlace].
|
142
|
+
[width, height, depth, color, compression, filtering, interlace].
|
143
|
+
pack('NNC5')
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
# The End (IEND) chunk indicates the last chunk of a PNG stream. It does
|
148
|
-
# contain any data.
|
147
|
+
# The End (IEND) chunk indicates the last chunk of a PNG stream. It does
|
148
|
+
# not contain any data.
|
149
149
|
class End < Base
|
150
|
-
|
150
|
+
|
151
151
|
def initialize
|
152
152
|
super('IEND')
|
153
153
|
end
|
154
|
-
|
154
|
+
|
155
155
|
# Reads the END chunk. It will check if the content is empty.
|
156
|
-
# @param [String]
|
157
|
-
#
|
156
|
+
# @param type [String] The four character chunk type indicator (=
|
157
|
+
# "IEND").
|
158
|
+
# @param content [String] The content read from the chunk. Should be
|
159
|
+
# empty.
|
158
160
|
# @return [ChunkyPNG::Chunk::End] The new End chunk instance.
|
159
161
|
# @raise [RuntimeError] Raises an exception if the content was not empty.
|
160
162
|
def self.read(type, content)
|
@@ -173,46 +175,56 @@ module ChunkyPNG
|
|
173
175
|
# 8-bit RGB colors this image is using.
|
174
176
|
#
|
175
177
|
# @see ChunkyPNG::Chunk::Transparency
|
176
|
-
# @see ChunkyPNG::Palette
|
178
|
+
# @see ChunkyPNG::Palette
|
177
179
|
class Palette < Generic
|
178
180
|
end
|
179
181
|
|
180
182
|
# A transparency (tRNS) chunk defines the transparency for an image.
|
181
183
|
#
|
182
|
-
# * For indexed images, it contains the alpha channel for the colors
|
183
|
-
#
|
184
|
-
# * For
|
184
|
+
# * For indexed images, it contains the alpha channel for the colors
|
185
|
+
# defined in the Palette (PLTE) chunk.
|
186
|
+
# * For grayscale images, it contains the grayscale teint that should be
|
187
|
+
# considered fully transparent.
|
188
|
+
# * For truecolor images, it contains the color that should be considered
|
189
|
+
# fully transparent.
|
185
190
|
#
|
186
|
-
# Images having a color mode that already includes an alpha channel, this
|
191
|
+
# Images having a color mode that already includes an alpha channel, this
|
192
|
+
# chunk should not be included.
|
187
193
|
#
|
188
194
|
# @see ChunkyPNG::Chunk::Palette
|
189
195
|
# @see ChunkyPNG::Palette
|
190
196
|
class Transparency < Generic
|
191
|
-
|
192
197
|
# Returns the alpha channel for the palette of an indexed image.
|
193
198
|
#
|
194
|
-
# This method should only be used for images having color mode
|
199
|
+
# This method should only be used for images having color mode
|
200
|
+
# ChunkyPNG::COLOR_INDEXED (3).
|
195
201
|
#
|
196
|
-
# @return [Array<Integer>] Returns an array of alpha channel values
|
202
|
+
# @return [Array<Integer>] Returns an array of alpha channel values
|
203
|
+
# [0-255].
|
197
204
|
def palette_alpha_channel
|
198
205
|
content.unpack('C*')
|
199
206
|
end
|
200
|
-
|
207
|
+
|
201
208
|
# Returns the truecolor entry to be replaced by transparent pixels,
|
202
209
|
#
|
203
|
-
# This method should only be used for images having color mode
|
210
|
+
# This method should only be used for images having color mode
|
211
|
+
# ChunkyPNG::COLOR_TRUECOLOR (2).
|
204
212
|
#
|
205
213
|
# @return [Integer] The color to replace with fully transparent pixels.
|
206
214
|
def truecolor_entry(bit_depth)
|
207
|
-
values = content.unpack('nnn').map
|
215
|
+
values = content.unpack('nnn').map do |c|
|
216
|
+
ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", c)
|
217
|
+
end
|
208
218
|
ChunkyPNG::Color.rgb(*values)
|
209
219
|
end
|
210
220
|
|
211
221
|
# Returns the grayscale entry to be replaced by transparent pixels.
|
212
222
|
#
|
213
|
-
# This method should only be used for images having color mode
|
223
|
+
# This method should only be used for images having color mode
|
224
|
+
# ChunkyPNG::COLOR_GRAYSCALE (0).
|
214
225
|
#
|
215
|
-
# @return [Integer] The (grayscale) color to replace with fully
|
226
|
+
# @return [Integer] The (grayscale) color to replace with fully
|
227
|
+
# transparent pixels.
|
216
228
|
def grayscale_entry(bit_depth)
|
217
229
|
value = ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", content.unpack('n')[0])
|
218
230
|
ChunkyPNG::Color.grayscale(value)
|
@@ -220,16 +232,15 @@ module ChunkyPNG
|
|
220
232
|
end
|
221
233
|
|
222
234
|
class ImageData < Generic
|
223
|
-
|
224
235
|
def self.read(type, content)
|
225
236
|
raise ExpectationFailed, 'The IDAT chunk should not be empty!' if content.bytesize == 0
|
226
237
|
super
|
227
238
|
end
|
228
|
-
|
239
|
+
|
229
240
|
def self.combine_chunks(data_chunks)
|
230
241
|
Zlib::Inflate.inflate(data_chunks.map { |c| c.content }.join(''))
|
231
242
|
end
|
232
|
-
|
243
|
+
|
233
244
|
def self.split_in_chunks(data, level = Zlib::DEFAULT_COMPRESSION, chunk_size = 2147483647)
|
234
245
|
streamdata = Zlib::Deflate.deflate(data, level)
|
235
246
|
# TODO: Split long streamdata over multiple chunks
|
@@ -237,16 +248,15 @@ module ChunkyPNG
|
|
237
248
|
end
|
238
249
|
end
|
239
250
|
|
240
|
-
# The Text (tEXt) chunk contains keyword/value metadata about the PNG
|
241
|
-
# In this chunk, the value is stored uncompressed.
|
251
|
+
# The Text (tEXt) chunk contains keyword/value metadata about the PNG
|
252
|
+
# stream. In this chunk, the value is stored uncompressed.
|
242
253
|
#
|
243
|
-
# The tEXt chunk only supports Latin-1 encoded textual data. If you need
|
244
|
-
# support, check out the InternationalText chunk type.
|
254
|
+
# The tEXt chunk only supports Latin-1 encoded textual data. If you need
|
255
|
+
# UTF-8 support, check out the InternationalText chunk type.
|
245
256
|
#
|
246
257
|
# @see ChunkyPNG::Chunk::CompressedText
|
247
258
|
# @see ChunkyPNG::Chunk::InternationalText
|
248
259
|
class Text < Base
|
249
|
-
|
250
260
|
attr_accessor :keyword, :value
|
251
261
|
|
252
262
|
def initialize(keyword, value)
|
@@ -259,8 +269,8 @@ module ChunkyPNG
|
|
259
269
|
new(keyword, value)
|
260
270
|
end
|
261
271
|
|
262
|
-
# Creates the content to write to the stream, by concatenating the
|
263
|
-
# with the value, joined by a null character.
|
272
|
+
# Creates the content to write to the stream, by concatenating the
|
273
|
+
# keyword with the value, joined by a null character.
|
264
274
|
#
|
265
275
|
# @return The content that should be written to the datastream.
|
266
276
|
def content
|
@@ -268,14 +278,13 @@ module ChunkyPNG
|
|
268
278
|
end
|
269
279
|
end
|
270
280
|
|
271
|
-
# The CompressedText (zTXt) chunk contains keyword/value metadata about
|
272
|
-
#
|
281
|
+
# The CompressedText (zTXt) chunk contains keyword/value metadata about the
|
282
|
+
# PNG stream. In this chunk, the value is compressed using Deflate
|
273
283
|
# compression.
|
274
284
|
#
|
275
285
|
# @see ChunkyPNG::Chunk::CompressedText
|
276
286
|
# @see ChunkyPNG::Chunk::InternationalText
|
277
287
|
class CompressedText < Base
|
278
|
-
|
279
288
|
attr_accessor :keyword, :value
|
280
289
|
|
281
290
|
def initialize(keyword, value)
|
@@ -289,40 +298,49 @@ module ChunkyPNG
|
|
289
298
|
new(keyword, Zlib::Inflate.inflate(value))
|
290
299
|
end
|
291
300
|
|
292
|
-
# Creates the content to write to the stream, by concatenating the
|
293
|
-
# with the deflated value, joined by a null character.
|
301
|
+
# Creates the content to write to the stream, by concatenating the
|
302
|
+
# keyword with the deflated value, joined by a null character.
|
294
303
|
#
|
295
304
|
# @return The content that should be written to the datastream.
|
296
305
|
def content
|
297
|
-
[keyword, ChunkyPNG::COMPRESSION_DEFAULT, Zlib::Deflate.deflate(value)].
|
306
|
+
[keyword, ChunkyPNG::COMPRESSION_DEFAULT, Zlib::Deflate.deflate(value)].
|
307
|
+
pack('Z*Ca*')
|
298
308
|
end
|
299
309
|
end
|
300
310
|
|
301
|
-
# The Text (iTXt) chunk contains keyword/value metadata about the PNG
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
#
|
311
|
+
# The Text (iTXt) chunk contains keyword/value metadata about the PNG
|
312
|
+
# stream.
|
313
|
+
#
|
314
|
+
# The metadata in this chunk can be encoded using UTF-8 characters.
|
315
|
+
# Moreover, it is possible to define the language of the metadata, and give
|
316
|
+
# a translation of the keyword name. Finally, it supports bot compressed
|
317
|
+
# and uncompressed values.
|
318
|
+
#
|
319
|
+
# @todo This chunk is currently not implemented, but merely read and
|
320
|
+
# written back intact.
|
309
321
|
#
|
310
322
|
# @see ChunkyPNG::Chunk::Text
|
311
323
|
# @see ChunkyPNG::Chunk::CompressedText
|
312
324
|
class InternationalText < Generic
|
313
|
-
|
314
325
|
# TODO
|
315
326
|
end
|
316
327
|
|
317
|
-
# Maps chunk types to classes, based on the four byte chunk type indicator
|
318
|
-
# beginning of a chunk.
|
328
|
+
# Maps chunk types to classes, based on the four byte chunk type indicator
|
329
|
+
# at the beginning of a chunk.
|
319
330
|
#
|
320
|
-
# If a chunk type is not specified in this hash, the Generic chunk type
|
331
|
+
# If a chunk type is not specified in this hash, the Generic chunk type
|
332
|
+
# will be used.
|
321
333
|
#
|
322
334
|
# @see ChunkyPNG::Chunk.read
|
323
335
|
CHUNK_TYPES = {
|
324
|
-
'IHDR' => Header,
|
325
|
-
'
|
336
|
+
'IHDR' => Header,
|
337
|
+
'IEND' => End,
|
338
|
+
'IDAT' => ImageData,
|
339
|
+
'PLTE' => Palette,
|
340
|
+
'tRNS' => Transparency,
|
341
|
+
'tEXt' => Text,
|
342
|
+
'zTXt' => CompressedText,
|
343
|
+
'iTXt' => InternationalText,
|
326
344
|
}
|
327
345
|
end
|
328
346
|
end
|
data/lib/chunky_png/color.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module ChunkyPNG
|
2
|
-
|
2
|
+
|
3
3
|
# Factory method to return a color value, based on the arguments given.
|
4
4
|
#
|
5
5
|
# @overload Color(r, g, b, a)
|
@@ -12,15 +12,18 @@ module ChunkyPNG
|
|
12
12
|
#
|
13
13
|
# @overload Color(hex_value, opacity = nil)
|
14
14
|
# @param (see ChunkyPNG::Color.from_hex)
|
15
|
-
# @return [Integer] The hex color value, with the opacity applied if one
|
15
|
+
# @return [Integer] The hex color value, with the opacity applied if one
|
16
|
+
# was given.
|
16
17
|
#
|
17
18
|
# @overload Color(color_name, opacity = nil)
|
18
19
|
# @param (see ChunkyPNG::Color.html_color)
|
19
|
-
# @return [Integer] The hex color value, with the opacity applied if one
|
20
|
+
# @return [Integer] The hex color value, with the opacity applied if one
|
21
|
+
# was given.
|
20
22
|
#
|
21
23
|
# @overload Color(color_value, opacity = nil)
|
22
24
|
# @param [Integer, :to_i] The color value.
|
23
|
-
# @return [Integer] The color value, with the opacity applied if one was
|
25
|
+
# @return [Integer] The color value, with the opacity applied if one was
|
26
|
+
# given.
|
24
27
|
#
|
25
28
|
# @return [Integer] The determined color value as RGBA integer.
|
26
29
|
# @raise [ArgumentError] if the arguments weren't understood as a color.
|
@@ -54,7 +57,7 @@ module ChunkyPNG
|
|
54
57
|
|
55
58
|
# @return [Integer] The maximum value of each color component.
|
56
59
|
MAX = 0xff
|
57
|
-
|
60
|
+
|
58
61
|
# @private
|
59
62
|
# @return [Regexp] The regexp to parse 3-digit hex color values.
|
60
63
|
HEX3_COLOR_REGEXP = /\A(?:#|0x)?([0-9a-f]{3})\z/i
|
@@ -66,7 +69,7 @@ module ChunkyPNG
|
|
66
69
|
# @private
|
67
70
|
# @return [Regexp] The regexp to parse named color values.
|
68
71
|
HTML_COLOR_REGEXP = /^([a-z][a-z_ ]+[a-z])(?:\ ?\@\ ?(1\.0|0\.\d+))?$/i
|
69
|
-
|
72
|
+
|
70
73
|
####################################################################
|
71
74
|
# CONSTRUCTING COLOR VALUES
|
72
75
|
####################################################################
|
@@ -76,7 +79,8 @@ module ChunkyPNG
|
|
76
79
|
# It supports color numbers, colors in hex notation and named HTML colors.
|
77
80
|
#
|
78
81
|
# @param [Integer, String] The color value.
|
79
|
-
# @return [Integer] The color value, with the opacity applied if one was
|
82
|
+
# @return [Integer] The color value, with the opacity applied if one was
|
83
|
+
# given.
|
80
84
|
def parse(source)
|
81
85
|
return source if source.kind_of?(Integer)
|
82
86
|
case source.to_s
|
@@ -107,14 +111,16 @@ module ChunkyPNG
|
|
107
111
|
end
|
108
112
|
|
109
113
|
# Creates a new color using a grayscale teint.
|
110
|
-
# @param [Integer] teint The grayscale teint (0-255), will be used as r, g,
|
114
|
+
# @param [Integer] teint The grayscale teint (0-255), will be used as r, g,
|
115
|
+
# and b value.
|
111
116
|
# @return [Integer] The newly constructed color value.
|
112
117
|
def grayscale(teint)
|
113
118
|
teint << 24 | teint << 16 | teint << 8 | 0xff
|
114
119
|
end
|
115
120
|
|
116
121
|
# Creates a new color using a grayscale teint and alpha value.
|
117
|
-
# @param [Integer] teint The grayscale teint (0-255), will be used as r, g,
|
122
|
+
# @param [Integer] teint The grayscale teint (0-255), will be used as r, g,
|
123
|
+
# and b value.
|
118
124
|
# @param [Integer] a The opacity (0-255)
|
119
125
|
# @return [Integer] The newly constructed color value.
|
120
126
|
def grayscale_alpha(teint, a)
|
@@ -127,7 +133,7 @@ module ChunkyPNG
|
|
127
133
|
|
128
134
|
# Creates a color by unpacking an rgb triple from a string.
|
129
135
|
#
|
130
|
-
# @param [String] stream The string to load the color from. It should be
|
136
|
+
# @param [String] stream The string to load the color from. It should be
|
131
137
|
# at least 3 + pos bytes long.
|
132
138
|
# @param [Integer] pos The position in the string to load the triple from.
|
133
139
|
# @return [Integer] The newly constructed color value.
|
@@ -137,15 +143,15 @@ module ChunkyPNG
|
|
137
143
|
|
138
144
|
# Creates a color by unpacking an rgba triple from a string
|
139
145
|
#
|
140
|
-
# @param [String] stream The string to load the color from. It should be
|
146
|
+
# @param [String] stream The string to load the color from. It should be
|
141
147
|
# at least 4 + pos bytes long.
|
142
148
|
# @param [Integer] pos The position in the string to load the triple from.
|
143
149
|
# @return [Integer] The newly constructed color value.
|
144
150
|
def from_rgba_stream(stream, pos = 0)
|
145
151
|
rgba(*stream.unpack("@#{pos}C4"))
|
146
152
|
end
|
147
|
-
|
148
|
-
# Creates a color by converting it from a string in hex notation.
|
153
|
+
|
154
|
+
# Creates a color by converting it from a string in hex notation.
|
149
155
|
#
|
150
156
|
# It supports colors with (#rrggbbaa) or without (#rrggbb) alpha channel
|
151
157
|
# as well as the 3-digit short format (#rgb) for those without.
|
@@ -181,7 +187,7 @@ module ChunkyPNG
|
|
181
187
|
def r(value)
|
182
188
|
(value & 0xff000000) >> 24
|
183
189
|
end
|
184
|
-
|
190
|
+
|
185
191
|
# Returns the green-component from the color value.
|
186
192
|
#
|
187
193
|
# @param [Integer] value The color value.
|
@@ -189,7 +195,7 @@ module ChunkyPNG
|
|
189
195
|
def g(value)
|
190
196
|
(value & 0x00ff0000) >> 16
|
191
197
|
end
|
192
|
-
|
198
|
+
|
193
199
|
# Returns the blue-component from the color value.
|
194
200
|
#
|
195
201
|
# @param [Integer] value The color value.
|
@@ -197,7 +203,7 @@ module ChunkyPNG
|
|
197
203
|
def b(value)
|
198
204
|
(value & 0x0000ff00) >> 8
|
199
205
|
end
|
200
|
-
|
206
|
+
|
201
207
|
# Returns the alpha channel value for the color value.
|
202
208
|
#
|
203
209
|
# @param [Integer] value The color value.
|
@@ -205,7 +211,7 @@ module ChunkyPNG
|
|
205
211
|
def a(value)
|
206
212
|
value & 0x000000ff
|
207
213
|
end
|
208
|
-
|
214
|
+
|
209
215
|
# Returns true if this color is fully opaque.
|
210
216
|
#
|
211
217
|
# @param [Integer] value The color to test.
|
@@ -213,14 +219,14 @@ module ChunkyPNG
|
|
213
219
|
def opaque?(value)
|
214
220
|
a(value) == 0x000000ff
|
215
221
|
end
|
216
|
-
|
222
|
+
|
217
223
|
# Returns the opaque value of this color by removing the alpha channel.
|
218
224
|
# @param [Integer] value The color to transform.
|
219
225
|
# @return [Integer] The opaque color
|
220
226
|
def opaque!(value)
|
221
227
|
value | 0x000000ff
|
222
228
|
end
|
223
|
-
|
229
|
+
|
224
230
|
# Returns true if this color is fully transparent.
|
225
231
|
#
|
226
232
|
# @param [Integer] value The color to test.
|
@@ -228,7 +234,7 @@ module ChunkyPNG
|
|
228
234
|
def grayscale?(value)
|
229
235
|
r(value) == b(value) && b(value) == g(value)
|
230
236
|
end
|
231
|
-
|
237
|
+
|
232
238
|
# Returns true if this color is fully transparent.
|
233
239
|
#
|
234
240
|
# @param [Integer] value The color to test.
|
@@ -241,9 +247,9 @@ module ChunkyPNG
|
|
241
247
|
# ALPHA COMPOSITION
|
242
248
|
####################################################################
|
243
249
|
|
244
|
-
# Multiplies two fractions using integer math, where the fractions are
|
245
|
-
# integer between 0 and 255. This method is used as a
|
246
|
-
# colors using integer math.
|
250
|
+
# Multiplies two fractions using integer math, where the fractions are
|
251
|
+
# stored using an integer between 0 and 255. This method is used as a
|
252
|
+
# helper method for compositing colors using integer math.
|
247
253
|
#
|
248
254
|
# This is a quicker implementation of ((a * b) / 255.0).round.
|
249
255
|
#
|
@@ -257,8 +263,8 @@ module ChunkyPNG
|
|
257
263
|
|
258
264
|
# Composes two colors with an alpha channel using integer math.
|
259
265
|
#
|
260
|
-
# This version is faster than the version based on floating point math, so
|
261
|
-
# compositing function is used by default.
|
266
|
+
# This version is faster than the version based on floating point math, so
|
267
|
+
# this compositing function is used by default.
|
262
268
|
#
|
263
269
|
# @param [Integer] fg The foreground color.
|
264
270
|
# @param [Integer] bg The foreground color.
|
@@ -267,7 +273,7 @@ module ChunkyPNG
|
|
267
273
|
def compose_quick(fg, bg)
|
268
274
|
return fg if opaque?(fg) || fully_transparent?(bg)
|
269
275
|
return bg if fully_transparent?(fg)
|
270
|
-
|
276
|
+
|
271
277
|
a_com = int8_mult(0xff - a(fg), a(bg))
|
272
278
|
new_r = int8_mult(a(fg), r(fg)) + int8_mult(a_com, r(bg))
|
273
279
|
new_g = int8_mult(a(fg), g(fg)) + int8_mult(a_com, g(bg))
|
@@ -278,9 +284,9 @@ module ChunkyPNG
|
|
278
284
|
|
279
285
|
# Composes two colors with an alpha channel using floating point math.
|
280
286
|
#
|
281
|
-
# This method uses more precise floating point math, but this precision is
|
282
|
-
# when the result is converted back to an integer. Because it is
|
283
|
-
# the version based on integer math, that version is preferred.
|
287
|
+
# This method uses more precise floating point math, but this precision is
|
288
|
+
# lost when the result is converted back to an integer. Because it is
|
289
|
+
# slower than the version based on integer math, that version is preferred.
|
284
290
|
#
|
285
291
|
# @param [Integer] fg The foreground color.
|
286
292
|
# @param [Integer] bg The foreground color.
|
@@ -289,7 +295,7 @@ module ChunkyPNG
|
|
289
295
|
def compose_precise(fg, bg)
|
290
296
|
return fg if opaque?(fg) || fully_transparent?(bg)
|
291
297
|
return bg if fully_transparent?(fg)
|
292
|
-
|
298
|
+
|
293
299
|
fg_a = a(fg).to_f / MAX
|
294
300
|
bg_a = a(bg).to_f / MAX
|
295
301
|
a_com = (1.0 - fg_a) * bg_a
|
@@ -302,8 +308,8 @@ module ChunkyPNG
|
|
302
308
|
end
|
303
309
|
|
304
310
|
alias :compose :compose_quick
|
305
|
-
|
306
|
-
# Blends the foreground and background color by taking the average of
|
311
|
+
|
312
|
+
# Blends the foreground and background color by taking the average of
|
307
313
|
# the components.
|
308
314
|
#
|
309
315
|
# @param [Integer] fg The foreground color.
|
@@ -313,8 +319,8 @@ module ChunkyPNG
|
|
313
319
|
(fg + bg) >> 1
|
314
320
|
end
|
315
321
|
|
316
|
-
# Interpolates the foreground and background colors by the given alpha
|
317
|
-
# This also blends the alpha channels themselves.
|
322
|
+
# Interpolates the foreground and background colors by the given alpha
|
323
|
+
# value. This also blends the alpha channels themselves.
|
318
324
|
#
|
319
325
|
# A blending factor of 255 will give entirely the foreground,
|
320
326
|
# while a blending factor of 0 will give the background.
|
@@ -326,37 +332,39 @@ module ChunkyPNG
|
|
326
332
|
def interpolate_quick(fg, bg, alpha)
|
327
333
|
return fg if alpha >= 255
|
328
334
|
return bg if alpha <= 0
|
329
|
-
|
335
|
+
|
330
336
|
alpha_com = 255 - alpha
|
331
337
|
|
332
338
|
new_r = int8_mult(alpha, r(fg)) + int8_mult(alpha_com, r(bg))
|
333
339
|
new_g = int8_mult(alpha, g(fg)) + int8_mult(alpha_com, g(bg))
|
334
340
|
new_b = int8_mult(alpha, b(fg)) + int8_mult(alpha_com, b(bg))
|
335
341
|
new_a = int8_mult(alpha, a(fg)) + int8_mult(alpha_com, a(bg))
|
336
|
-
|
337
|
-
|
342
|
+
|
343
|
+
rgba(new_r, new_g, new_b, new_a)
|
338
344
|
end
|
339
345
|
|
340
346
|
# Calculates the grayscale teint of an RGB color.
|
341
347
|
#
|
342
|
-
# @param [Integer] color The color to convert.
|
348
|
+
# @param [Integer] color The color to convert.
|
343
349
|
# @return [Integer] The grayscale teint of the input color, 0-255.
|
344
350
|
def grayscale_teint(color)
|
345
351
|
(r(color) * 0.3 + g(color) * 0.59 + b(color) * 0.11).round
|
346
352
|
end
|
347
|
-
|
353
|
+
|
348
354
|
# Converts a color to a fiting grayscale value. It will conserve the alpha
|
349
355
|
# channel.
|
350
356
|
#
|
351
|
-
# This method will return a full color value, with the R, G, and B value
|
352
|
-
# to the grayscale teint calcuated from the input color's R, G and B
|
357
|
+
# This method will return a full color value, with the R, G, and B value
|
358
|
+
# set to the grayscale teint calcuated from the input color's R, G and B
|
359
|
+
# values.
|
353
360
|
#
|
354
361
|
# @param [Integer] color The color to convert.
|
355
|
-
# @return [Integer] The input color, converted to the best fitting
|
362
|
+
# @return [Integer] The input color, converted to the best fitting
|
363
|
+
# grayscale.
|
356
364
|
# @see #grayscale_teint
|
357
365
|
def to_grayscale(color)
|
358
366
|
grayscale_alpha(grayscale_teint(color), a(color))
|
359
|
-
end
|
367
|
+
end
|
360
368
|
|
361
369
|
# Lowers the intensity of a color, by lowering its alpha by a given factor.
|
362
370
|
# @param [Integer] color The color to adjust.
|
@@ -366,21 +374,23 @@ module ChunkyPNG
|
|
366
374
|
new_alpha = int8_mult(a(color), factor)
|
367
375
|
(color & 0xffffff00) | new_alpha
|
368
376
|
end
|
369
|
-
|
377
|
+
|
370
378
|
# Decomposes a color, given a color, a mask color and a background color.
|
371
379
|
# The returned color will be a variant of the mask color, with the alpha
|
372
|
-
# channel set to the best fitting value. This basically is the reverse
|
380
|
+
# channel set to the best fitting value. This basically is the reverse
|
373
381
|
# operation if alpha composition.
|
374
382
|
#
|
375
383
|
# If the color cannot be decomposed, this method will return the fully
|
376
384
|
# transparent variant of the mask color.
|
377
385
|
#
|
378
386
|
# @param [Integer] color The color that was the result of compositing.
|
379
|
-
# @param [Integer] mask The opaque variant of the color that was being
|
387
|
+
# @param [Integer] mask The opaque variant of the color that was being
|
388
|
+
# composed
|
380
389
|
# @param [Integer] bg The background color on which the color was composed.
|
381
|
-
# @param [Integer] tolerance The decomposition tolerance level, a value
|
382
|
-
#
|
383
|
-
#
|
390
|
+
# @param [Integer] tolerance The decomposition tolerance level, a value
|
391
|
+
# between 0 and 255.
|
392
|
+
# @return [Integer] The decomposed color, a variant of the masked color
|
393
|
+
# with the alpha channel set to an appropriate value.
|
384
394
|
def decompose_color(color, mask, bg, tolerance = 1)
|
385
395
|
if alpha_decomposable?(color, mask, bg, tolerance)
|
386
396
|
mask & 0xffffff00 | decompose_alpha(color, mask, bg)
|
@@ -388,62 +398,71 @@ module ChunkyPNG
|
|
388
398
|
mask & 0xffffff00
|
389
399
|
end
|
390
400
|
end
|
391
|
-
|
401
|
+
|
392
402
|
# Checks whether an alpha channel value can successfully be composed
|
393
403
|
# given the resulting color, the mask color and a background color,
|
394
|
-
# all of which should be opaque.
|
404
|
+
# all of which should be opaque.
|
395
405
|
#
|
396
406
|
# @param [Integer] color The color that was the result of compositing.
|
397
|
-
# @param [Integer] mask The opaque variant of the color that was being
|
407
|
+
# @param [Integer] mask The opaque variant of the color that was being
|
408
|
+
# composed
|
398
409
|
# @param [Integer] bg The background color on which the color was composed.
|
399
|
-
# @param [Integer] tolerance The decomposition tolerance level, a value
|
400
|
-
#
|
410
|
+
# @param [Integer] tolerance The decomposition tolerance level, a value
|
411
|
+
# between 0 and 255.
|
412
|
+
# @return [Boolean] True if the alpha component can be decomposed
|
413
|
+
# successfully.
|
401
414
|
# @see #decompose_alpha
|
402
415
|
def alpha_decomposable?(color, mask, bg, tolerance = 1)
|
403
416
|
components = decompose_alpha_components(color, mask, bg)
|
404
|
-
sum = components.inject(0) { |a,b| a + b }
|
417
|
+
sum = components.inject(0) { |a,b| a + b }
|
405
418
|
max = components.max * 3
|
406
|
-
|
419
|
+
components.max <= 255 && components.min >= 0 && (sum + tolerance * 3) >= max
|
407
420
|
end
|
408
|
-
|
409
|
-
# Decomposes the alpha channel value given the resulting color, the mask
|
410
|
-
# and a background color, all of which should be opaque.
|
421
|
+
|
422
|
+
# Decomposes the alpha channel value given the resulting color, the mask
|
423
|
+
# color and a background color, all of which should be opaque.
|
411
424
|
#
|
412
|
-
# Make sure to call {#alpha_decomposable?} first to see if the alpha
|
413
|
-
# value can successfully decomposed with a given tolerance,
|
414
|
-
# value of this method is undefined.
|
425
|
+
# Make sure to call {#alpha_decomposable?} first to see if the alpha
|
426
|
+
# channel value can successfully decomposed with a given tolerance,
|
427
|
+
# otherwise the return value of this method is undefined.
|
415
428
|
#
|
416
429
|
# @param [Integer] color The color that was the result of compositing.
|
417
|
-
# @param [Integer] mask The opaque variant of the color that was being
|
430
|
+
# @param [Integer] mask The opaque variant of the color that was being
|
431
|
+
# composed
|
418
432
|
# @param [Integer] bg The background color on which the color was composed.
|
419
|
-
# @return [Integer] The best fitting alpha channel, a value between 0 and
|
433
|
+
# @return [Integer] The best fitting alpha channel, a value between 0 and
|
434
|
+
# 255.
|
420
435
|
# @see #alpha_decomposable?
|
421
436
|
def decompose_alpha(color, mask, bg)
|
422
437
|
components = decompose_alpha_components(color, mask, bg)
|
423
438
|
(components.inject(0) { |a,b| a + b } / 3.0).round
|
424
439
|
end
|
425
|
-
|
440
|
+
|
426
441
|
# Decomposes an alpha channel for either the r, g or b color channel.
|
427
|
-
# @param [:r, :g, :b] channel The channel to decompose the alpha channel
|
442
|
+
# @param [:r, :g, :b] channel The channel to decompose the alpha channel
|
443
|
+
# from.
|
428
444
|
# @param [Integer] color The color that was the result of compositing.
|
429
|
-
# @param [Integer] mask The opaque variant of the color that was being
|
445
|
+
# @param [Integer] mask The opaque variant of the color that was being
|
446
|
+
# composed
|
430
447
|
# @param [Integer] bg The background color on which the color was composed.
|
431
448
|
# @return [Integer] The decomposed alpha value for the channel.
|
432
449
|
def decompose_alpha_component(channel, color, mask, bg)
|
433
450
|
cc, mc, bc = send(channel, color), send(channel, mask), send(channel, bg)
|
434
|
-
|
451
|
+
|
435
452
|
return 0x00 if bc == cc
|
436
453
|
return 0xff if bc == mc
|
437
454
|
return 0xff if cc == mc
|
438
|
-
|
455
|
+
|
439
456
|
(((bc - cc).to_f / (bc - mc).to_f) * MAX).round
|
440
457
|
end
|
441
|
-
|
458
|
+
|
442
459
|
# Decomposes the alpha channels for the r, g and b color channel.
|
443
460
|
# @param [Integer] color The color that was the result of compositing.
|
444
|
-
# @param [Integer] mask The opaque variant of the color that was being
|
445
|
-
#
|
446
|
-
# @
|
461
|
+
# @param [Integer] mask The opaque variant of the color that was being
|
462
|
+
# composed
|
463
|
+
# @param [Integer] bg The background color on which the color was composed.
|
464
|
+
# @return [Array<Integer>] The decomposed alpha values for the r, g and b
|
465
|
+
# channels.
|
447
466
|
def decompose_alpha_components(color, mask, bg)
|
448
467
|
[
|
449
468
|
decompose_alpha_component(:r, color, mask, bg),
|
@@ -456,7 +475,8 @@ module ChunkyPNG
|
|
456
475
|
# CONVERSIONS
|
457
476
|
####################################################################
|
458
477
|
|
459
|
-
# Returns a string representing this color using hex notation (i.e.
|
478
|
+
# Returns a string representing this color using hex notation (i.e.
|
479
|
+
# #rrggbbaa).
|
460
480
|
#
|
461
481
|
# @param [Integer] value The color to convert.
|
462
482
|
# @return [String] The color in hex notation, starting with a pound sign.
|
@@ -472,8 +492,8 @@ module ChunkyPNG
|
|
472
492
|
[r(color), g(color), b(color), a(color)]
|
473
493
|
end
|
474
494
|
|
475
|
-
# Returns an array with the separate RGB values for this color.
|
476
|
-
#
|
495
|
+
# Returns an array with the separate RGB values for this color. The alpha
|
496
|
+
# channel will be discarded.
|
477
497
|
#
|
478
498
|
# @param [Integer] color The color to convert.
|
479
499
|
# @return [Array<Integer>] An array with 3 Integer elements.
|
@@ -483,7 +503,7 @@ module ChunkyPNG
|
|
483
503
|
|
484
504
|
# Returns an array with the grayscale teint value for this color.
|
485
505
|
#
|
486
|
-
# This method expects the r,g and b value to be equal, and the alpha
|
506
|
+
# This method expects the r, g, and b value to be equal, and the alpha
|
487
507
|
# channel will be discarded.
|
488
508
|
#
|
489
509
|
# @param [Integer] color The grayscale color to convert.
|
@@ -492,189 +512,220 @@ module ChunkyPNG
|
|
492
512
|
[b(color)] # assumption r == g == b
|
493
513
|
end
|
494
514
|
|
495
|
-
# Returns an array with the grayscale teint and alpha channel values
|
496
|
-
#
|
515
|
+
# Returns an array with the grayscale teint and alpha channel values for
|
516
|
+
# this color.
|
497
517
|
#
|
498
|
-
# This method expects the color to be grayscale, i.e. r,g and b value
|
499
|
-
#
|
500
|
-
#
|
518
|
+
# This method expects the color to be grayscale, i.e. r, g, and b value to
|
519
|
+
# be equal and uses only the B channel. If you need to convert a color to
|
520
|
+
# grayscale first, see {#to_grayscale}.
|
501
521
|
#
|
502
522
|
# @param [Integer] color The grayscale color to convert.
|
503
523
|
# @return [Array<Integer>] An array with 2 Integer elements.
|
504
|
-
# @see #
|
524
|
+
# @see #to_grayscale
|
505
525
|
def to_grayscale_alpha_bytes(color)
|
506
526
|
[b(color), a(color)] # assumption r == g == b
|
507
527
|
end
|
508
528
|
|
529
|
+
####################################################################
|
530
|
+
# COMPARISON
|
531
|
+
####################################################################
|
532
|
+
|
533
|
+
# Compute the Euclidean distance between 2 colors in RGBA
|
534
|
+
#
|
535
|
+
# This method simply takes the Euclidean distance between the RGBA channels
|
536
|
+
# of 2 colors, which gives us a measure of how different the two colors
|
537
|
+
# are.
|
538
|
+
#
|
539
|
+
# Although it would be more perceptually accurate to calculate a proper
|
540
|
+
# Delta E in Lab colorspace, this method should serve many use-cases while
|
541
|
+
# avoiding the overhead of converting RGBA to Lab.
|
542
|
+
#
|
543
|
+
# @param color_a [Integer]
|
544
|
+
# @param color_b [Integer]
|
545
|
+
# @return [Float]
|
546
|
+
def euclidean_distance_rgba(pixel_after, pixel_before)
|
547
|
+
Math.sqrt(
|
548
|
+
(r(pixel_after) - r(pixel_before))**2 +
|
549
|
+
(g(pixel_after) - g(pixel_before))**2 +
|
550
|
+
(b(pixel_after) - b(pixel_before))**2 +
|
551
|
+
(a(pixel_after) - a(pixel_before))**2
|
552
|
+
)
|
553
|
+
end
|
554
|
+
|
555
|
+
# Could be simplified as MAX * 2, but this format mirrors the math in
|
556
|
+
# {#euclidean_distance_rgba}
|
557
|
+
# @return [Float] The maximum Euclidean distance of two RGBA colors.
|
558
|
+
MAX_EUCLIDEAN_DISTANCE_RGBA = Math.sqrt(MAX**2 * 4)
|
559
|
+
|
509
560
|
####################################################################
|
510
561
|
# COLOR CONSTANTS
|
511
562
|
####################################################################
|
512
563
|
|
513
564
|
# @return [Hash<Symbol, Integer>] All the predefined color names in HTML.
|
514
565
|
PREDEFINED_COLORS = {
|
515
|
-
:aliceblue
|
516
|
-
:antiquewhite
|
517
|
-
:aqua
|
518
|
-
:aquamarine
|
519
|
-
:azure
|
520
|
-
:beige
|
521
|
-
:bisque
|
522
|
-
:black
|
523
|
-
:blanchedalmond
|
524
|
-
:blue
|
525
|
-
:blueviolet
|
526
|
-
:brown
|
527
|
-
:burlywood
|
528
|
-
:cadetblue
|
529
|
-
:chartreuse
|
530
|
-
:chocolate
|
531
|
-
:coral
|
532
|
-
:cornflowerblue
|
533
|
-
:cornsilk
|
534
|
-
:crimson
|
535
|
-
:cyan
|
536
|
-
:darkblue
|
537
|
-
:darkcyan
|
538
|
-
:darkgoldenrod
|
539
|
-
:darkgray
|
540
|
-
:darkgrey
|
541
|
-
:darkgreen
|
542
|
-
:darkkhaki
|
543
|
-
:darkmagenta
|
544
|
-
:darkolivegreen
|
545
|
-
:darkorange
|
546
|
-
:darkorchid
|
547
|
-
:darkred
|
548
|
-
:darksalmon
|
549
|
-
:darkseagreen
|
550
|
-
:darkslateblue
|
551
|
-
:darkslategray
|
552
|
-
:darkslategrey
|
553
|
-
:darkturquoise
|
554
|
-
:darkviolet
|
555
|
-
:deeppink
|
556
|
-
:deepskyblue
|
557
|
-
:dimgray
|
558
|
-
:dimgrey
|
559
|
-
:dodgerblue
|
560
|
-
:firebrick
|
561
|
-
:floralwhite
|
562
|
-
:forestgreen
|
563
|
-
:fuchsia
|
564
|
-
:gainsboro
|
565
|
-
:ghostwhite
|
566
|
-
:gold
|
567
|
-
:goldenrod
|
568
|
-
:gray
|
569
|
-
:grey
|
570
|
-
:green
|
571
|
-
:greenyellow
|
572
|
-
:honeydew
|
573
|
-
:hotpink
|
574
|
-
:indianred
|
575
|
-
:indigo
|
576
|
-
:ivory
|
577
|
-
:khaki
|
578
|
-
:lavender
|
579
|
-
:lavenderblush
|
580
|
-
:lawngreen
|
581
|
-
:lemonchiffon
|
582
|
-
:lightblue
|
583
|
-
:lightcoral
|
584
|
-
:lightcyan
|
566
|
+
:aliceblue => 0xf0f8ff00,
|
567
|
+
:antiquewhite => 0xfaebd700,
|
568
|
+
:aqua => 0x00ffff00,
|
569
|
+
:aquamarine => 0x7fffd400,
|
570
|
+
:azure => 0xf0ffff00,
|
571
|
+
:beige => 0xf5f5dc00,
|
572
|
+
:bisque => 0xffe4c400,
|
573
|
+
:black => 0x00000000,
|
574
|
+
:blanchedalmond => 0xffebcd00,
|
575
|
+
:blue => 0x0000ff00,
|
576
|
+
:blueviolet => 0x8a2be200,
|
577
|
+
:brown => 0xa52a2a00,
|
578
|
+
:burlywood => 0xdeb88700,
|
579
|
+
:cadetblue => 0x5f9ea000,
|
580
|
+
:chartreuse => 0x7fff0000,
|
581
|
+
:chocolate => 0xd2691e00,
|
582
|
+
:coral => 0xff7f5000,
|
583
|
+
:cornflowerblue => 0x6495ed00,
|
584
|
+
:cornsilk => 0xfff8dc00,
|
585
|
+
:crimson => 0xdc143c00,
|
586
|
+
:cyan => 0x00ffff00,
|
587
|
+
:darkblue => 0x00008b00,
|
588
|
+
:darkcyan => 0x008b8b00,
|
589
|
+
:darkgoldenrod => 0xb8860b00,
|
590
|
+
:darkgray => 0xa9a9a900,
|
591
|
+
:darkgrey => 0xa9a9a900,
|
592
|
+
:darkgreen => 0x00640000,
|
593
|
+
:darkkhaki => 0xbdb76b00,
|
594
|
+
:darkmagenta => 0x8b008b00,
|
595
|
+
:darkolivegreen => 0x556b2f00,
|
596
|
+
:darkorange => 0xff8c0000,
|
597
|
+
:darkorchid => 0x9932cc00,
|
598
|
+
:darkred => 0x8b000000,
|
599
|
+
:darksalmon => 0xe9967a00,
|
600
|
+
:darkseagreen => 0x8fbc8f00,
|
601
|
+
:darkslateblue => 0x483d8b00,
|
602
|
+
:darkslategray => 0x2f4f4f00,
|
603
|
+
:darkslategrey => 0x2f4f4f00,
|
604
|
+
:darkturquoise => 0x00ced100,
|
605
|
+
:darkviolet => 0x9400d300,
|
606
|
+
:deeppink => 0xff149300,
|
607
|
+
:deepskyblue => 0x00bfff00,
|
608
|
+
:dimgray => 0x69696900,
|
609
|
+
:dimgrey => 0x69696900,
|
610
|
+
:dodgerblue => 0x1e90ff00,
|
611
|
+
:firebrick => 0xb2222200,
|
612
|
+
:floralwhite => 0xfffaf000,
|
613
|
+
:forestgreen => 0x228b2200,
|
614
|
+
:fuchsia => 0xff00ff00,
|
615
|
+
:gainsboro => 0xdcdcdc00,
|
616
|
+
:ghostwhite => 0xf8f8ff00,
|
617
|
+
:gold => 0xffd70000,
|
618
|
+
:goldenrod => 0xdaa52000,
|
619
|
+
:gray => 0x80808000,
|
620
|
+
:grey => 0x80808000,
|
621
|
+
:green => 0x00800000,
|
622
|
+
:greenyellow => 0xadff2f00,
|
623
|
+
:honeydew => 0xf0fff000,
|
624
|
+
:hotpink => 0xff69b400,
|
625
|
+
:indianred => 0xcd5c5c00,
|
626
|
+
:indigo => 0x4b008200,
|
627
|
+
:ivory => 0xfffff000,
|
628
|
+
:khaki => 0xf0e68c00,
|
629
|
+
:lavender => 0xe6e6fa00,
|
630
|
+
:lavenderblush => 0xfff0f500,
|
631
|
+
:lawngreen => 0x7cfc0000,
|
632
|
+
:lemonchiffon => 0xfffacd00,
|
633
|
+
:lightblue => 0xadd8e600,
|
634
|
+
:lightcoral => 0xf0808000,
|
635
|
+
:lightcyan => 0xe0ffff00,
|
585
636
|
:lightgoldenrodyellow => 0xfafad200,
|
586
|
-
:lightgray
|
587
|
-
:lightgrey
|
588
|
-
:lightgreen
|
589
|
-
:lightpink
|
590
|
-
:lightsalmon
|
591
|
-
:lightseagreen
|
592
|
-
:lightskyblue
|
593
|
-
:lightslategray
|
594
|
-
:lightslategrey
|
595
|
-
:lightsteelblue
|
596
|
-
:lightyellow
|
597
|
-
:lime
|
598
|
-
:limegreen
|
599
|
-
:linen
|
600
|
-
:magenta
|
601
|
-
:maroon
|
602
|
-
:mediumaquamarine
|
603
|
-
:mediumblue
|
604
|
-
:mediumorchid
|
605
|
-
:mediumpurple
|
606
|
-
:mediumseagreen
|
607
|
-
:mediumslateblue
|
608
|
-
:mediumspringgreen
|
609
|
-
:mediumturquoise
|
610
|
-
:mediumvioletred
|
611
|
-
:midnightblue
|
612
|
-
:mintcream
|
613
|
-
:mistyrose
|
614
|
-
:moccasin
|
615
|
-
:navajowhite
|
616
|
-
:navy
|
617
|
-
:oldlace
|
618
|
-
:olive
|
619
|
-
:olivedrab
|
620
|
-
:orange
|
621
|
-
:orangered
|
622
|
-
:orchid
|
623
|
-
:palegoldenrod
|
624
|
-
:palegreen
|
625
|
-
:paleturquoise
|
626
|
-
:palevioletred
|
627
|
-
:papayawhip
|
628
|
-
:peachpuff
|
629
|
-
:peru
|
630
|
-
:pink
|
631
|
-
:plum
|
632
|
-
:powderblue
|
633
|
-
:purple
|
634
|
-
:red
|
635
|
-
:rosybrown
|
636
|
-
:royalblue
|
637
|
-
:saddlebrown
|
638
|
-
:salmon
|
639
|
-
:sandybrown
|
640
|
-
:seagreen
|
641
|
-
:seashell
|
642
|
-
:sienna
|
643
|
-
:silver
|
644
|
-
:skyblue
|
645
|
-
:slateblue
|
646
|
-
:slategray
|
647
|
-
:slategrey
|
648
|
-
:snow
|
649
|
-
:springgreen
|
650
|
-
:steelblue
|
651
|
-
:tan
|
652
|
-
:teal
|
653
|
-
:thistle
|
654
|
-
:tomato
|
655
|
-
:turquoise
|
656
|
-
:violet
|
657
|
-
:wheat
|
658
|
-
:white
|
659
|
-
:whitesmoke
|
660
|
-
:yellow
|
661
|
-
:yellowgreen
|
637
|
+
:lightgray => 0xd3d3d300,
|
638
|
+
:lightgrey => 0xd3d3d300,
|
639
|
+
:lightgreen => 0x90ee9000,
|
640
|
+
:lightpink => 0xffb6c100,
|
641
|
+
:lightsalmon => 0xffa07a00,
|
642
|
+
:lightseagreen => 0x20b2aa00,
|
643
|
+
:lightskyblue => 0x87cefa00,
|
644
|
+
:lightslategray => 0x77889900,
|
645
|
+
:lightslategrey => 0x77889900,
|
646
|
+
:lightsteelblue => 0xb0c4de00,
|
647
|
+
:lightyellow => 0xffffe000,
|
648
|
+
:lime => 0x00ff0000,
|
649
|
+
:limegreen => 0x32cd3200,
|
650
|
+
:linen => 0xfaf0e600,
|
651
|
+
:magenta => 0xff00ff00,
|
652
|
+
:maroon => 0x80000000,
|
653
|
+
:mediumaquamarine => 0x66cdaa00,
|
654
|
+
:mediumblue => 0x0000cd00,
|
655
|
+
:mediumorchid => 0xba55d300,
|
656
|
+
:mediumpurple => 0x9370d800,
|
657
|
+
:mediumseagreen => 0x3cb37100,
|
658
|
+
:mediumslateblue => 0x7b68ee00,
|
659
|
+
:mediumspringgreen => 0x00fa9a00,
|
660
|
+
:mediumturquoise => 0x48d1cc00,
|
661
|
+
:mediumvioletred => 0xc7158500,
|
662
|
+
:midnightblue => 0x19197000,
|
663
|
+
:mintcream => 0xf5fffa00,
|
664
|
+
:mistyrose => 0xffe4e100,
|
665
|
+
:moccasin => 0xffe4b500,
|
666
|
+
:navajowhite => 0xffdead00,
|
667
|
+
:navy => 0x00008000,
|
668
|
+
:oldlace => 0xfdf5e600,
|
669
|
+
:olive => 0x80800000,
|
670
|
+
:olivedrab => 0x6b8e2300,
|
671
|
+
:orange => 0xffa50000,
|
672
|
+
:orangered => 0xff450000,
|
673
|
+
:orchid => 0xda70d600,
|
674
|
+
:palegoldenrod => 0xeee8aa00,
|
675
|
+
:palegreen => 0x98fb9800,
|
676
|
+
:paleturquoise => 0xafeeee00,
|
677
|
+
:palevioletred => 0xd8709300,
|
678
|
+
:papayawhip => 0xffefd500,
|
679
|
+
:peachpuff => 0xffdab900,
|
680
|
+
:peru => 0xcd853f00,
|
681
|
+
:pink => 0xffc0cb00,
|
682
|
+
:plum => 0xdda0dd00,
|
683
|
+
:powderblue => 0xb0e0e600,
|
684
|
+
:purple => 0x80008000,
|
685
|
+
:red => 0xff000000,
|
686
|
+
:rosybrown => 0xbc8f8f00,
|
687
|
+
:royalblue => 0x4169e100,
|
688
|
+
:saddlebrown => 0x8b451300,
|
689
|
+
:salmon => 0xfa807200,
|
690
|
+
:sandybrown => 0xf4a46000,
|
691
|
+
:seagreen => 0x2e8b5700,
|
692
|
+
:seashell => 0xfff5ee00,
|
693
|
+
:sienna => 0xa0522d00,
|
694
|
+
:silver => 0xc0c0c000,
|
695
|
+
:skyblue => 0x87ceeb00,
|
696
|
+
:slateblue => 0x6a5acd00,
|
697
|
+
:slategray => 0x70809000,
|
698
|
+
:slategrey => 0x70809000,
|
699
|
+
:snow => 0xfffafa00,
|
700
|
+
:springgreen => 0x00ff7f00,
|
701
|
+
:steelblue => 0x4682b400,
|
702
|
+
:tan => 0xd2b48c00,
|
703
|
+
:teal => 0x00808000,
|
704
|
+
:thistle => 0xd8bfd800,
|
705
|
+
:tomato => 0xff634700,
|
706
|
+
:turquoise => 0x40e0d000,
|
707
|
+
:violet => 0xee82ee00,
|
708
|
+
:wheat => 0xf5deb300,
|
709
|
+
:white => 0xffffff00,
|
710
|
+
:whitesmoke => 0xf5f5f500,
|
711
|
+
:yellow => 0xffff0000,
|
712
|
+
:yellowgreen => 0x9acd3200
|
662
713
|
}
|
663
|
-
|
714
|
+
|
664
715
|
# Gets a color value based on a HTML color name.
|
665
|
-
#
|
666
|
-
# The color name is flexible. E.g. <tt>'yellowgreen'</tt>, <tt>'Yellow
|
667
|
-
# <tt>'YellowGreen'</tt>, <tt>'YELLOW_GREEN'</tt> and
|
668
|
-
# all return the same color value.
|
669
|
-
#
|
670
|
-
# You can include a opacity level in the color name (e.g. <tt>'red @
|
671
|
-
# an explicit opacity value as second argument. If no
|
672
|
-
# will be fully opaque.
|
673
|
-
#
|
674
|
-
# @param [Symbol, String] color_name The color name. It may include an
|
675
|
-
# like <tt>@ 0.8</tt> to set the color's opacity.
|
676
|
-
# @param [Integer] opacity The opacity value for the color between 0 and
|
677
|
-
# any opacity value given in the color name.
|
716
|
+
#
|
717
|
+
# The color name is flexible. E.g. <tt>'yellowgreen'</tt>, <tt>'Yellow
|
718
|
+
# green'</tt>, <tt>'YellowGreen'</tt>, <tt>'YELLOW_GREEN'</tt> and
|
719
|
+
# <tt>:yellow_green</tt> will all return the same color value.
|
720
|
+
#
|
721
|
+
# You can include a opacity level in the color name (e.g. <tt>'red @
|
722
|
+
# 0.5'</tt>) or give an explicit opacity value as second argument. If no
|
723
|
+
# opacity value is given, the color will be fully opaque.
|
724
|
+
#
|
725
|
+
# @param [Symbol, String] color_name The color name. It may include an
|
726
|
+
# opacity specifier like <tt>@ 0.8</tt> to set the color's opacity.
|
727
|
+
# @param [Integer] opacity The opacity value for the color between 0 and
|
728
|
+
# 255. Overrides any opacity value given in the color name.
|
678
729
|
# @return [Integer] The color value.
|
679
730
|
# @raise [ChunkyPNG::Exception] If the color name was not recognized.
|
680
731
|
def html_color(color_name, opacity = nil)
|
@@ -709,37 +760,45 @@ module ChunkyPNG
|
|
709
760
|
when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; 4
|
710
761
|
when ChunkyPNG::COLOR_GRAYSCALE; 1
|
711
762
|
when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; 2
|
712
|
-
else raise ChunkyPNG::NotSupported, "Don't know the
|
763
|
+
else raise ChunkyPNG::NotSupported, "Don't know the number of samples for this colormode: #{color_mode}!"
|
713
764
|
end
|
714
765
|
end
|
715
766
|
|
716
|
-
# Returns the size in bytes of a pixel when it is stored using a given
|
717
|
-
#
|
767
|
+
# Returns the size in bytes of a pixel when it is stored using a given
|
768
|
+
# color mode.
|
769
|
+
#
|
770
|
+
# @param [Integer] color_mode The color mode in which the pixels are
|
771
|
+
# stored.
|
718
772
|
# @return [Integer] The number of bytes used per pixel in a datastream.
|
719
773
|
def pixel_bytesize(color_mode, depth = 8)
|
720
774
|
return 1 if depth < 8
|
721
775
|
(pixel_bitsize(color_mode, depth) + 7) >> 3
|
722
776
|
end
|
723
|
-
|
724
|
-
# Returns the size in bits of a pixel when it is stored using a given color
|
725
|
-
#
|
777
|
+
|
778
|
+
# Returns the size in bits of a pixel when it is stored using a given color
|
779
|
+
# mode.
|
780
|
+
#
|
781
|
+
# @param [Integer] color_mode The color mode in which the pixels are
|
782
|
+
# stored.
|
726
783
|
# @param [Integer] depth The color depth of the pixels.
|
727
784
|
# @return [Integer] The number of bytes used per pixel in a datastream.
|
728
785
|
def pixel_bitsize(color_mode, depth = 8)
|
729
786
|
samples_per_pixel(color_mode) * depth
|
730
787
|
end
|
731
|
-
|
788
|
+
|
732
789
|
# Returns the number of bytes used per scanline.
|
733
|
-
# @param [Integer] color_mode The color mode in which the pixels are
|
790
|
+
# @param [Integer] color_mode The color mode in which the pixels are
|
791
|
+
# stored.
|
734
792
|
# @param [Integer] depth The color depth of the pixels.
|
735
793
|
# @param [Integer] width The number of pixels per scanline.
|
736
794
|
# @return [Integer] The number of bytes used per scanline in a datastream.
|
737
795
|
def scanline_bytesize(color_mode, depth, width)
|
738
796
|
((pixel_bitsize(color_mode, depth) * width) + 7) >> 3
|
739
797
|
end
|
740
|
-
|
798
|
+
|
741
799
|
# Returns the number of bytes used for an image pass
|
742
|
-
# @param [Integer] color_mode The color mode in which the pixels are
|
800
|
+
# @param [Integer] color_mode The color mode in which the pixels are
|
801
|
+
# stored.
|
743
802
|
# @param [Integer] depth The color depth of the pixels.
|
744
803
|
# @param [Integer] width The width of the image pass.
|
745
804
|
# @param [Integer] width The height of the image pass.
|