chunky_png 1.3.1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|