chunky_png 1.3.1 → 1.3.2
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 +7 -0
- data/lib/chunky_png/canvas/resampling.rb +4 -4
- data/lib/chunky_png/chunk.rb +17 -10
- data/lib/chunky_png/color.rb +172 -2
- data/lib/chunky_png/dimension.rb +28 -16
- data/lib/chunky_png/point.rb +30 -16
- data/lib/chunky_png/version.rb +2 -2
- data/spec/chunky_png/color_spec.rb +108 -0
- data/spec/chunky_png/image_spec.rb +11 -5
- data/spec/chunky_png/point_spec.rb +6 -0
- data/spec/resources/empty.png +0 -0
- metadata +12 -16
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 323e9e7a7ff35bce40522c1f9f26763168fef3e0
|
4
|
+
data.tar.gz: a82e301e630948b3ecdc34bc7edfc3dc89bbdb93
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2a62b78538767364a849f3c555e1cc3d8204a9dfb58638cf264033bb65d265ff7858c0c873858699906b627a0bcdbf7fbe46ab481a502663a01086470ae70420
|
7
|
+
data.tar.gz: 8be957364e9c0e2aae3b1a4789f370e24db4a5fc62e2458dab4d3e416b93bffd099a43f0bc3934177dfa0d22dfb222ed91a6db279d304655560da41f17a8131f
|
@@ -39,8 +39,8 @@ module ChunkyPNG
|
|
39
39
|
# @param [Integer] new_width The width of the destination
|
40
40
|
# @return [Array<Integer>, Array<Integer>] Two arrays of indicies and residues
|
41
41
|
def steps_residues(width, new_width)
|
42
|
-
indicies = Array.new(
|
43
|
-
residues = Array.new(
|
42
|
+
indicies = Array.new(new_width, obj=nil)
|
43
|
+
residues = Array.new(new_width, obj=nil)
|
44
44
|
|
45
45
|
# This works by accumulating the fractional error and
|
46
46
|
# overflowing when necessary.
|
@@ -80,7 +80,7 @@ module ChunkyPNG
|
|
80
80
|
steps_y = steps(height, new_height)
|
81
81
|
|
82
82
|
|
83
|
-
pixels = Array(
|
83
|
+
pixels = Array.new(new_width*new_height)
|
84
84
|
i = 0
|
85
85
|
for y in steps_y
|
86
86
|
for x in steps_x
|
@@ -104,7 +104,7 @@ module ChunkyPNG
|
|
104
104
|
index_x, interp_x = steps_residues(width, new_width)
|
105
105
|
index_y, interp_y = steps_residues(height, new_height)
|
106
106
|
|
107
|
-
pixels = Array(
|
107
|
+
pixels = Array.new(new_width*new_height)
|
108
108
|
i = 0
|
109
109
|
for y in 1..new_height
|
110
110
|
# Clamp the indicies to the edges of the image
|
data/lib/chunky_png/chunk.rb
CHANGED
@@ -16,21 +16,33 @@ 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 = io
|
20
|
-
content = io.read(length)
|
21
|
-
crc = io.read(4).unpack('N').first
|
19
|
+
length, type = read_bytes(io, 8).unpack('Na4')
|
22
20
|
|
21
|
+
content = read_bytes(io, length)
|
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)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Reads an exact number of bytes from an IO stream.
|
29
|
+
# @param io [IO, #read] The IO stream to read from.
|
30
|
+
# @param length [Integer] The IO exact number of bytes to read.
|
31
|
+
# @return [String] A binary string of exactly length bytes.
|
32
|
+
# @raise [ChunkyPNG::ExpectationFailed] If not exactly length
|
33
|
+
# bytes could be read from the IO stream.
|
34
|
+
def self.read_bytes(io, length)
|
35
|
+
data = io.read(length)
|
36
|
+
raise ExpectationFailed, "Couldn't read #{length} bytes from IO stream." if data.nil? || data.bytesize != length
|
37
|
+
data
|
38
|
+
end
|
39
|
+
|
28
40
|
# Verifies the CRC of a chunk.
|
29
41
|
# @param type [String] The chunk's type.
|
30
42
|
# @param content [String] The chunk's content.
|
31
43
|
# @param found_crc [Integer] The chunk's found CRC value.
|
32
|
-
# @raise [
|
33
|
-
# not equal to the expected CRC value.
|
44
|
+
# @raise [ChunkyPNG::CRCMismatch] An exception is raised if
|
45
|
+
# the found CRC value is not equal to the expected CRC value.
|
34
46
|
def self.verify_crc!(type, content, found_crc)
|
35
47
|
expected_crc = Zlib.crc32(content, Zlib.crc32(type))
|
36
48
|
raise ChunkyPNG::CRCMismatch, "Chuck CRC mismatch!" if found_crc != expected_crc
|
@@ -232,11 +244,6 @@ module ChunkyPNG
|
|
232
244
|
end
|
233
245
|
|
234
246
|
class ImageData < Generic
|
235
|
-
def self.read(type, content)
|
236
|
-
raise ExpectationFailed, 'The IDAT chunk should not be empty!' if content.bytesize == 0
|
237
|
-
super
|
238
|
-
end
|
239
|
-
|
240
247
|
def self.combine_chunks(data_chunks)
|
241
248
|
Zlib::Inflate.inflate(data_chunks.map { |c| c.content }.join(''))
|
242
249
|
end
|
data/lib/chunky_png/color.rb
CHANGED
@@ -176,6 +176,91 @@ module ChunkyPNG
|
|
176
176
|
base_color | opacity
|
177
177
|
end
|
178
178
|
|
179
|
+
# Creates a new color from an HSV triple.
|
180
|
+
#
|
181
|
+
# Create a new color using an HSV (sometimes also called HSB) triple. The
|
182
|
+
# words `value` and `brightness` are used interchangeably and synonymously
|
183
|
+
# in descriptions of this colorspace. This implementation follows the modern
|
184
|
+
# convention of 0 degrees hue indicating red.
|
185
|
+
#
|
186
|
+
# @param [Fixnum] hue The hue component (0-360)
|
187
|
+
# @param [Fixnum] saturation The saturation component (0-1)
|
188
|
+
# @param [Fixnum] value The value (brightness) component (0-1)
|
189
|
+
# @param [Fixnum] alpha Defaults to opaque (255).
|
190
|
+
# @return [Integer] The newly constructed color value.
|
191
|
+
# @raise [ArgumentError] if the hsv triple is invalid.
|
192
|
+
# @see http://en.wikipedia.org/wiki/HSL_and_HSV
|
193
|
+
def from_hsv(hue, saturation, value, alpha = 255)
|
194
|
+
raise ArgumentError, "Hue must be between 0 and 360" unless (0..360).include?(hue)
|
195
|
+
raise ArgumentError, "Saturation must be between 0 and 1" unless (0..1).include?(saturation)
|
196
|
+
raise ArgumentError, "Value/brightness must be between 0 and 1" unless (0..1).include?(value)
|
197
|
+
chroma = value * saturation
|
198
|
+
rgb = cylindrical_to_cubic(hue, saturation, value, chroma)
|
199
|
+
rgb.map! { |component| ((component + value - chroma) * 255).to_i }
|
200
|
+
rgb << alpha
|
201
|
+
self.rgba(*rgb)
|
202
|
+
end
|
203
|
+
alias_method :from_hsb, :from_hsv
|
204
|
+
|
205
|
+
# Creates a new color from an HSL triple.
|
206
|
+
#
|
207
|
+
# This implementation follows the modern convention of 0 degrees hue
|
208
|
+
# indicating red.
|
209
|
+
#
|
210
|
+
# @param [Fixnum] hue The hue component (0-360)
|
211
|
+
# @param [Fixnum] saturation The saturation component (0-1)
|
212
|
+
# @param [Fixnum] lightness The lightness component (0-1)
|
213
|
+
# @param [Fixnum] alpha Defaults to opaque (255).
|
214
|
+
# @return [Integer] The newly constructed color value.
|
215
|
+
# @raise [ArgumentError] if the hsl triple is invalid.
|
216
|
+
# @see http://en.wikipedia.org/wiki/HSL_and_HSV
|
217
|
+
def from_hsl(hue, saturation, lightness, alpha = 255)
|
218
|
+
raise ArgumentError, "Hue #{hue} was not between 0 and 360" unless (0..360).include?(hue)
|
219
|
+
raise ArgumentError, "Saturation #{saturation} was not between 0 and 1" unless (0..1).include?(saturation)
|
220
|
+
raise ArgumentError, "Lightness #{lightness} was not between 0 and 1" unless (0..1).include?(lightness)
|
221
|
+
chroma = (1 - (2 * lightness - 1).abs) * saturation
|
222
|
+
rgb = cylindrical_to_cubic(hue, saturation, lightness, chroma)
|
223
|
+
rgb.map! { |component| ((component + lightness - 0.5 * chroma) * 255).to_i }
|
224
|
+
rgb << alpha
|
225
|
+
self.rgba(*rgb)
|
226
|
+
end
|
227
|
+
|
228
|
+
# Convert one HSL or HSV triple and associated chroma to a scaled rgb triple
|
229
|
+
#
|
230
|
+
# This method encapsulates the shared mathematical operations needed to
|
231
|
+
# convert coordinates from a cylindrical colorspace such as HSL or HSV into
|
232
|
+
# coordinates of the RGB colorspace.
|
233
|
+
#
|
234
|
+
# Even though chroma values are derived from the other three coordinates,
|
235
|
+
# the formula for calculating chroma differs for each colorspace. Since it
|
236
|
+
# is calculated differently for each colorspace, it must be passed in as
|
237
|
+
# a parameter.
|
238
|
+
#
|
239
|
+
# @param [Fixnum] hue The hue-component (0-360)
|
240
|
+
# @param [Fixnum] saturation The saturation-component (0-1)
|
241
|
+
# @param [Fixnum] y_component The y_component can represent either lightness
|
242
|
+
# or brightness/value (0-1) depending on which scheme (HSV/HSL) is being used.
|
243
|
+
# @param [Fixnum] chroma The associated chroma value.
|
244
|
+
# @return [Array<Fixnum>] A scaled r,g,b triple. Scheme-dependent
|
245
|
+
# adjustments are still needed to reach the true r,g,b values.
|
246
|
+
# @see http://en.wikipedia.org/wiki/HSL_and_HSV
|
247
|
+
# @see http://www.tomjewett.com/colors/hsb.html
|
248
|
+
# @private
|
249
|
+
def cylindrical_to_cubic(hue, saturation, y_component, chroma)
|
250
|
+
hue_prime = hue.fdiv(60)
|
251
|
+
x = chroma * (1 - (hue_prime % 2 - 1).abs)
|
252
|
+
|
253
|
+
case hue_prime
|
254
|
+
when (0...1); [chroma, x, 0]
|
255
|
+
when (1...2); [x, chroma, 0]
|
256
|
+
when (2...3); [0, chroma, x]
|
257
|
+
when (3...4); [0, x, chroma]
|
258
|
+
when (4...5); [x, 0, chroma]
|
259
|
+
when (5...6); [chroma, 0, x]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
private :cylindrical_to_cubic
|
263
|
+
|
179
264
|
####################################################################
|
180
265
|
# PROPERTIES
|
181
266
|
####################################################################
|
@@ -484,6 +569,91 @@ module ChunkyPNG
|
|
484
569
|
include_alpha ? ('#%08x' % color) : ('#%06x' % [color >> 8])
|
485
570
|
end
|
486
571
|
|
572
|
+
# Returns an array with the separate HSV components of a color.
|
573
|
+
#
|
574
|
+
# Because ChunkyPNG internally handles colors as Integers for performance
|
575
|
+
# reasons, some rounding occurs when importing or exporting HSV colors
|
576
|
+
# whose coordinates are float-based. Because of this rounding, #to_hsv and
|
577
|
+
# #from_hsv may not be perfect inverses.
|
578
|
+
#
|
579
|
+
# This implementation follows the modern convention of 0 degrees hue
|
580
|
+
# indicating red.
|
581
|
+
#
|
582
|
+
# @param [Integer] color The ChunkyPNG color to convert.
|
583
|
+
# @param [Boolean] include_alpha Flag indicates whether a fourth element
|
584
|
+
# representing alpha channel should be included in the returned array.
|
585
|
+
# @return [Array<Fixnum>[0]] The hue of the color (0-360)
|
586
|
+
# @return [Array<Fixnum>[1]] The saturation of the color (0-1)
|
587
|
+
# @return [Array<Fixnum>[2]] The value of the color (0-1)
|
588
|
+
# @return [Array<Fixnum>[3]] Optional fourth element for alpha, included if
|
589
|
+
# include_alpha=true (0-255)
|
590
|
+
# @see http://en.wikipedia.org/wiki/HSL_and_HSV
|
591
|
+
def to_hsv(color, include_alpha = false)
|
592
|
+
hue, chroma, max, min = hue_and_chroma(color)
|
593
|
+
value = max
|
594
|
+
saturation = chroma.zero? ? 0 : chroma.fdiv(value)
|
595
|
+
|
596
|
+
include_alpha ? [hue, saturation, value, a(color)] :
|
597
|
+
[hue, saturation, value]
|
598
|
+
end
|
599
|
+
alias_method :to_hsb, :to_hsv
|
600
|
+
|
601
|
+
# Returns an array with the separate HSL components of a color.
|
602
|
+
#
|
603
|
+
# Because ChunkyPNG internally handles colors as Integers for performance
|
604
|
+
# reasons, some rounding occurs when importing or exporting HSL colors
|
605
|
+
# whose coordinates are float-based. Because of this rounding, #to_hsl and
|
606
|
+
# #from_hsl may not be perfect inverses.
|
607
|
+
#
|
608
|
+
# This implementation follows the modern convention of 0 degrees hue indicating red.
|
609
|
+
#
|
610
|
+
# @param [Integer] color The ChunkyPNG color to convert.
|
611
|
+
# @param [Boolean] include_alpha Flag indicates whether a fourth element
|
612
|
+
# representing alpha channel should be included in the returned array.
|
613
|
+
# @return [Array<Fixnum>[0]] The hue of the color (0-360)
|
614
|
+
# @return [Array<Fixnum>[1]] The saturation of the color (0-1)
|
615
|
+
# @return [Array<Fixnum>[2]] The lightness of the color (0-1)
|
616
|
+
# @return [Array<Fixnum>[3]] Optional fourth element for alpha, included if
|
617
|
+
# include_alpha=true (0-255)
|
618
|
+
# @see http://en.wikipedia.org/wiki/HSL_and_HSV
|
619
|
+
def to_hsl(color, include_alpha = false)
|
620
|
+
hue, chroma, max, min = hue_and_chroma(color)
|
621
|
+
lightness = 0.5 * (max + min)
|
622
|
+
saturation = chroma.zero? ? 0 : chroma.fdiv(1 - (2*lightness-1).abs)
|
623
|
+
|
624
|
+
include_alpha ? [hue, saturation, lightness, a(color)] :
|
625
|
+
[hue, saturation, lightness]
|
626
|
+
end
|
627
|
+
|
628
|
+
# This method encapsulates the logic needed to extract hue and chroma from
|
629
|
+
# a ChunkPNG color. This logic is shared by the cylindrical HSV/HSB and HSL
|
630
|
+
# color space models.
|
631
|
+
#
|
632
|
+
# @param [Integer] A ChunkyPNG color.
|
633
|
+
# @return [Fixnum] hue The hue of the color (0-360)
|
634
|
+
# @return [Fixnum] chroma The chroma of the color (0-1)
|
635
|
+
# @return [Fixnum] max The magnitude of the largest scaled rgb component (0-1)
|
636
|
+
# @return [Fixnum] min The magnitude of the smallest scaled rgb component (0-1)
|
637
|
+
# @private
|
638
|
+
def hue_and_chroma(color)
|
639
|
+
scaled_rgb = to_truecolor_bytes(color)
|
640
|
+
scaled_rgb.map! { |component| component.fdiv(255) }
|
641
|
+
min, max = scaled_rgb.minmax
|
642
|
+
chroma = max - min
|
643
|
+
|
644
|
+
r, g, b = scaled_rgb
|
645
|
+
hue_prime = chroma.zero? ? 0 : case max
|
646
|
+
when r; (g - b).fdiv(chroma)
|
647
|
+
when g; (b - r).fdiv(chroma) + 2
|
648
|
+
when b; (r - g).fdiv(chroma) + 4
|
649
|
+
else 0
|
650
|
+
end
|
651
|
+
hue = 60 * hue_prime
|
652
|
+
|
653
|
+
return hue, chroma, max, min
|
654
|
+
end
|
655
|
+
private :hue_and_chroma
|
656
|
+
|
487
657
|
# Returns an array with the separate RGBA values for this color.
|
488
658
|
#
|
489
659
|
# @param [Integer] color The color to convert.
|
@@ -540,8 +710,8 @@ module ChunkyPNG
|
|
540
710
|
# Delta E in Lab colorspace, this method should serve many use-cases while
|
541
711
|
# avoiding the overhead of converting RGBA to Lab.
|
542
712
|
#
|
543
|
-
# @param
|
544
|
-
# @param
|
713
|
+
# @param pixel_after [Integer]
|
714
|
+
# @param pixel_before [Integer]
|
545
715
|
# @return [Float]
|
546
716
|
def euclidean_distance_rgba(pixel_after, pixel_before)
|
547
717
|
Math.sqrt(
|
data/lib/chunky_png/dimension.rb
CHANGED
@@ -28,25 +28,37 @@ module ChunkyPNG
|
|
28
28
|
# @raise [ArgumentError] If the argument(s) given where not understood as a dimension.
|
29
29
|
# @see ChunkyPNG::Dimension
|
30
30
|
def self.Dimension(*args)
|
31
|
-
|
32
31
|
case args.length
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
32
|
+
when 2; ChunkyPNG::Dimension.new(*args)
|
33
|
+
when 1; build_dimension_from_object(args.first)
|
34
|
+
else raise ArgumentError,
|
35
|
+
"Don't know how to construct a dimension from #{args.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.build_dimension_from_object(source)
|
40
|
+
case source
|
41
|
+
when ChunkyPNG::Dimension
|
42
|
+
source
|
43
|
+
when ChunkyPNG::Point
|
44
|
+
ChunkyPNG::Dimension.new(source.x, source.y)
|
45
|
+
when Array
|
46
|
+
ChunkyPNG::Dimension.new(source[0], source[1])
|
47
|
+
when Hash
|
48
|
+
width = source[:width] || source['width']
|
49
|
+
height = source[:height] || source['height']
|
50
|
+
ChunkyPNG::Dimension.new(width, height)
|
51
|
+
when ChunkyPNG::Dimension::DIMENSION_REGEXP
|
52
|
+
ChunkyPNG::Dimension.new($1, $2)
|
53
|
+
else
|
54
|
+
if source.respond_to?(:width) && source.respond_to?(:height)
|
55
|
+
ChunkyPNG::Dimension.new(source.width, source.height)
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Don't know how to construct a dimension from #{source.inspect}!"
|
58
|
+
end
|
48
59
|
end
|
49
60
|
end
|
61
|
+
private_class_method :build_dimension_from_object
|
50
62
|
|
51
63
|
# Class that represents the dimension of something, e.g. a {ChunkyPNG::Canvas}.
|
52
64
|
#
|
data/lib/chunky_png/point.rb
CHANGED
@@ -30,24 +30,38 @@ module ChunkyPNG
|
|
30
30
|
# @see ChunkyPNG::Point
|
31
31
|
def self.Point(*args)
|
32
32
|
case args.length
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
33
|
+
when 2; ChunkyPNG::Point.new(*args)
|
34
|
+
when 1; build_point_from_object(args.first)
|
35
|
+
else raise ArgumentError,
|
36
|
+
"Don't know how to construct a point from #{args.inspect}!"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build_point_from_object(source)
|
41
|
+
case source
|
42
|
+
when ChunkyPNG::Point
|
43
|
+
source
|
44
|
+
when ChunkyPNG::Dimension
|
45
|
+
ChunkyPNG::Point.new(source.width, source.height)
|
46
|
+
when Array
|
47
|
+
ChunkyPNG::Point.new(source[0], source[1])
|
48
|
+
when Hash
|
49
|
+
x = source[:x] || source['x']
|
50
|
+
y = source[:y] || source['y']
|
51
|
+
ChunkyPNG::Point.new(x, y)
|
52
|
+
when ChunkyPNG::Point::POINT_REGEXP
|
53
|
+
ChunkyPNG::Point.new($1.to_i, $2.to_i)
|
54
|
+
else
|
55
|
+
if source.respond_to?(:x) && source.respond_to?(:y)
|
56
|
+
ChunkyPNG::Point.new(source.x, source.y)
|
57
|
+
else
|
58
|
+
raise ArgumentError,
|
59
|
+
"Don't know how to construct a point from #{source.inspect}!"
|
60
|
+
end
|
48
61
|
end
|
49
62
|
end
|
50
|
-
|
63
|
+
private_class_method :build_point_from_object
|
64
|
+
|
51
65
|
# Simple class that represents a point on a canvas using an x and y coordinate.
|
52
66
|
#
|
53
67
|
# This class implements some basic methods to handle comparison, the splat operator and
|
data/lib/chunky_png/version.rb
CHANGED
@@ -29,6 +29,9 @@ describe ChunkyPNG::Color do
|
|
29
29
|
@opaque = 0x0a6496ff
|
30
30
|
@non_opaque = 0x0a649664
|
31
31
|
@fully_transparent = 0x0a649600
|
32
|
+
@red = 0xff0000ff
|
33
|
+
@green = 0x00ff00ff
|
34
|
+
@blue = 0x0000ffff
|
32
35
|
end
|
33
36
|
|
34
37
|
describe '#parse' do
|
@@ -108,6 +111,69 @@ describe ChunkyPNG::Color do
|
|
108
111
|
end
|
109
112
|
end
|
110
113
|
|
114
|
+
describe '#from_hsv' do
|
115
|
+
it 'should load colors correctly from an HSV triple' do
|
116
|
+
# At 0 brightness, should be @black independent of hue or sat
|
117
|
+
from_hsv(0, 0, 0).should == @black
|
118
|
+
from_hsv(100, 1, 0).should == @black
|
119
|
+
from_hsv(100, 0.5, 0).should == @black
|
120
|
+
|
121
|
+
# At brightness 1 and sat 0, should be @white regardless of hue
|
122
|
+
from_hsv(0, 0, 1).should == @white
|
123
|
+
from_hsv(100, 0, 1).should == @white
|
124
|
+
|
125
|
+
# Converting the "pure" colors should work
|
126
|
+
from_hsv(0, 1, 1).should == @red
|
127
|
+
from_hsv(120, 1, 1).should == @green
|
128
|
+
from_hsv(240, 1, 1).should == @blue
|
129
|
+
|
130
|
+
# And, finally, one random color
|
131
|
+
from_hsv(120, 0.5, 0.80).should == 0x66cc66ff
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should optionally accept a fourth param for alpha' do
|
135
|
+
from_hsv(0, 1, 1, 255).should == @red
|
136
|
+
from_hsv(120, 1, 1, 255).should == @green
|
137
|
+
from_hsv(240, 1, 1, 255).should == @blue
|
138
|
+
from_hsv(0, 1, 1, 0).should == 0xff000000 # transparent red
|
139
|
+
from_hsv(120, 1, 1, 0).should == 0x00ff0000 # transparent green
|
140
|
+
from_hsv(240, 1, 1, 0).should == 0x0000ff00 # transparent blue
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#from_hsl' do
|
145
|
+
it 'should load colors correctly from an HSL triple' do
|
146
|
+
# At 0 lightness, should always be black
|
147
|
+
from_hsl(0, 0, 0).should == @black
|
148
|
+
from_hsl(100, 0, 0).should == @black
|
149
|
+
from_hsl(54, 0.5, 0).should == @black
|
150
|
+
|
151
|
+
# At 1 lightness, should always be white
|
152
|
+
from_hsl(0, 0, 1).should == @white
|
153
|
+
from_hsl(0, 0.5, 1).should == @white
|
154
|
+
from_hsl(110, 0, 1).should == @white
|
155
|
+
|
156
|
+
# 'Pure' colors should work
|
157
|
+
from_hsl(0, 1, 0.5).should == @red
|
158
|
+
from_hsl(120, 1, 0.5).should == @green
|
159
|
+
from_hsl(240, 1, 0.5).should == @blue
|
160
|
+
|
161
|
+
# Random colors
|
162
|
+
from_hsl(87.27, 0.5, 0.5686) == 0x96c85aff
|
163
|
+
from_hsl(271.83, 0.5399, 0.4176) == 0x6e31a4ff
|
164
|
+
from_hsl(63.6, 0.5984, 0.4882) == 0xbec732ff
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should optionally accept a fourth param for alpha' do
|
168
|
+
from_hsl(0, 1, 0.5, 255).should == @red
|
169
|
+
from_hsl(120, 1, 0.5, 255).should == @green
|
170
|
+
from_hsl(240, 1, 0.5, 255).should == @blue
|
171
|
+
from_hsl(0, 1, 0.5, 0).should == 0xff000000 # transparent red
|
172
|
+
from_hsl(120, 1, 0.5, 0).should == 0x00ff0000 # transparent green
|
173
|
+
from_hsl(240, 1, 0.5, 0).should == 0x0000ff00 # transparent blue
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
111
177
|
describe '#html_color' do
|
112
178
|
it 'should find the correct color value' do
|
113
179
|
html_color(:springgreen).should == 0x00ff7fff
|
@@ -193,6 +259,48 @@ describe ChunkyPNG::Color do
|
|
193
259
|
end
|
194
260
|
end
|
195
261
|
|
262
|
+
describe '#to_hsv' do
|
263
|
+
it 'should return a [hue, saturation, value] array' do
|
264
|
+
to_hsv(@white).should == [0, 0, 1]
|
265
|
+
to_hsv(@black).should == [0, 0, 0]
|
266
|
+
to_hsv(@red).should == [0, 1, 1]
|
267
|
+
to_hsv(@blue).should == [240, 1, 1]
|
268
|
+
to_hsv(@green).should == [120, 1, 1]
|
269
|
+
to_hsv(0x805440ff)[0].should be_within(1).of(19)
|
270
|
+
to_hsv(0x805440ff)[1].should be_within(0.01).of(0.5)
|
271
|
+
to_hsv(0x805440ff)[2].should be_within(0.01).of(0.5)
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'should optionally include the alpha channel' do
|
275
|
+
to_hsv(@white, true).should == [0, 0, 1, 255]
|
276
|
+
to_hsv(@red, true).should == [0, 1, 1, 255]
|
277
|
+
to_hsv(@blue, true).should == [240, 1, 1, 255]
|
278
|
+
to_hsv(@green, true).should == [120, 1, 1, 255]
|
279
|
+
to_hsv(@opaque, true)[3].should == 255
|
280
|
+
to_hsv(@fully_transparent, true)[3].should == 0
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe '#to_hsl' do
|
285
|
+
it 'should return a [hue, saturation, lightness] array' do
|
286
|
+
to_hsl(@white).should == [0, 0, 1]
|
287
|
+
to_hsl(@black).should == [0, 0, 0]
|
288
|
+
to_hsl(@red).should == [0, 1, 0.5]
|
289
|
+
to_hsl(@blue).should == [240, 1, 0.5]
|
290
|
+
to_hsl(@green).should == [120, 1, 0.5]
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'should optionally include the alpha channel in the returned array' do
|
294
|
+
to_hsl(@white, true).should == [0, 0, 1, 255]
|
295
|
+
to_hsl(@black, true).should == [0, 0, 0, 255]
|
296
|
+
to_hsl(@red, true).should == [0, 1, 0.5, 255]
|
297
|
+
to_hsl(@blue, true).should == [240, 1, 0.5, 255]
|
298
|
+
to_hsl(@green, true).should == [120, 1, 0.5, 255]
|
299
|
+
to_hsl(@opaque, true)[3].should == 255
|
300
|
+
to_hsl(@fully_transparent, true)[3].should == 0
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
196
304
|
describe 'conversion to other formats' do
|
197
305
|
it 'should convert the individual color values back correctly' do
|
198
306
|
to_truecolor_bytes(@opaque).should == [10, 100, 150]
|
@@ -2,24 +2,30 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe ChunkyPNG::Image do
|
4
4
|
describe '#metadata' do
|
5
|
-
|
5
|
+
|
6
6
|
it "should load metadata from an existing file" do
|
7
7
|
image = ChunkyPNG::Image.from_file(resource_file('text_chunk.png'))
|
8
8
|
image.metadata['Title'].should == 'My amazing icon!'
|
9
9
|
image.metadata['Author'].should == 'Willem van Bergen'
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
it "should write metadata to the file correctly" do
|
13
13
|
filename = resource_file('_metadata.png')
|
14
|
-
|
14
|
+
|
15
15
|
image = ChunkyPNG::Image.new(10, 10)
|
16
16
|
image.metadata['Title'] = 'My amazing icon!'
|
17
17
|
image.metadata['Author'] = 'Willem van Bergen'
|
18
18
|
image.save(filename)
|
19
|
-
|
19
|
+
|
20
20
|
metadata = ChunkyPNG::Datastream.from_file(filename).metadata
|
21
21
|
metadata['Title'].should == 'My amazing icon!'
|
22
22
|
metadata['Author'].should == 'Willem van Bergen'
|
23
23
|
end
|
24
|
+
|
25
|
+
it "should load empty images correctly" do
|
26
|
+
expect do
|
27
|
+
ChunkyPNG::Image.from_file(resource_file('empty.png'))
|
28
|
+
end.to_not raise_error
|
29
|
+
end
|
24
30
|
end
|
25
|
-
end
|
31
|
+
end
|
@@ -43,6 +43,7 @@ end
|
|
43
43
|
|
44
44
|
describe 'ChunkyPNG.Point' do
|
45
45
|
subject { ChunkyPNG::Point.new(1, 2) }
|
46
|
+
|
46
47
|
|
47
48
|
it "should create a point from a 2-item array" do
|
48
49
|
ChunkyPNG::Point([1, 2]).should == subject
|
@@ -53,6 +54,11 @@ describe 'ChunkyPNG.Point' do
|
|
53
54
|
ChunkyPNG::Point(:x => 1, :y => 2).should == subject
|
54
55
|
ChunkyPNG::Point('x' => '1', 'y' => '2').should == subject
|
55
56
|
end
|
57
|
+
|
58
|
+
it "should create a point from a ChunkyPNG::Dimension object" do
|
59
|
+
dimension = ChunkyPNG::Dimension.new(1, 2)
|
60
|
+
ChunkyPNG::Point(dimension) == subject
|
61
|
+
end
|
56
62
|
|
57
63
|
it "should create a point from a point-like string" do
|
58
64
|
[
|
Binary file
|
metadata
CHANGED
@@ -1,36 +1,32 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chunky_png
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
5
|
-
prerelease:
|
4
|
+
version: 1.3.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Willem van Bergen
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-
|
11
|
+
date: 2014-10-18 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rake
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rspec
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,12 +34,11 @@ dependencies:
|
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '2.2'
|
46
|
-
description:
|
41
|
+
description: " This pure Ruby library can read and write PNG images without depending
|
47
42
|
on an external \n image library, like RMagick. It tries to be memory efficient
|
48
43
|
and reasonably fast.\n \n It supports reading and writing all PNG variants
|
49
44
|
that are defined in the specification, \n with one limitation: only 8-bit color
|
@@ -361,6 +356,7 @@ files:
|
|
361
356
|
- spec/resources/cropped.png
|
362
357
|
- spec/resources/damaged_chunk.png
|
363
358
|
- spec/resources/damaged_signature.png
|
359
|
+
- spec/resources/empty.png
|
364
360
|
- spec/resources/lines.png
|
365
361
|
- spec/resources/operations.png
|
366
362
|
- spec/resources/operations_border.png
|
@@ -385,6 +381,7 @@ files:
|
|
385
381
|
homepage: http://wiki.github.com/wvanbergen/chunky_png
|
386
382
|
licenses:
|
387
383
|
- MIT
|
384
|
+
metadata: {}
|
388
385
|
post_install_message:
|
389
386
|
rdoc_options:
|
390
387
|
- --title
|
@@ -396,22 +393,20 @@ rdoc_options:
|
|
396
393
|
require_paths:
|
397
394
|
- lib
|
398
395
|
required_ruby_version: !ruby/object:Gem::Requirement
|
399
|
-
none: false
|
400
396
|
requirements:
|
401
|
-
- -
|
397
|
+
- - '>='
|
402
398
|
- !ruby/object:Gem::Version
|
403
399
|
version: '0'
|
404
400
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
405
|
-
none: false
|
406
401
|
requirements:
|
407
|
-
- -
|
402
|
+
- - '>='
|
408
403
|
- !ruby/object:Gem::Version
|
409
404
|
version: '0'
|
410
405
|
requirements: []
|
411
406
|
rubyforge_project:
|
412
|
-
rubygems_version:
|
407
|
+
rubygems_version: 2.0.14
|
413
408
|
signing_key:
|
414
|
-
specification_version:
|
409
|
+
specification_version: 4
|
415
410
|
summary: Pure ruby library for read/write, chunk-level access to PNG files
|
416
411
|
test_files:
|
417
412
|
- spec/chunky_png/canvas/adam7_interlacing_spec.rb
|
@@ -675,6 +670,7 @@ test_files:
|
|
675
670
|
- spec/resources/cropped.png
|
676
671
|
- spec/resources/damaged_chunk.png
|
677
672
|
- spec/resources/damaged_signature.png
|
673
|
+
- spec/resources/empty.png
|
678
674
|
- spec/resources/lines.png
|
679
675
|
- spec/resources/operations.png
|
680
676
|
- spec/resources/operations_border.png
|