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.
@@ -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(size=new_width, obj=nil)
43
- residues = Array.new(size=new_width, obj=nil)
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(size=new_width*new_height)
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(size=new_width*new_height)
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
@@ -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.read(8).unpack('Na4')
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 [RuntimeError] An exception is raised if the found CRC value is
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
@@ -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 color_a [Integer]
544
- # @param color_b [Integer]
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(
@@ -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
- when 2; ChunkyPNG::Dimension.new(*args)
34
- when 1; case source = args.first
35
- when ChunkyPNG::Dimension; source
36
- when ChunkyPNG::Point; ChunkyPNG::Dimension.new(source.x, source.y)
37
- when Array; ChunkyPNG::Dimension.new(source[0], source[1])
38
- when Hash; ChunkyPNG::Dimension.new(source[:width] || source['width'], source[:height] || source['height'])
39
- when ChunkyPNG::Dimension::DIMENSION_REGEXP; ChunkyPNG::Dimension.new($1, $2)
40
- else
41
- if source.respond_to?(:width) && source.respond_to?(:height)
42
- ChunkyPNG::Dimension.new(source.width, source.height)
43
- else
44
- raise ArgumentError, "Don't know how to construct a point from #{source.inspect}!"
45
- end
46
- end
47
- else raise ArgumentError, "Don't know how to construct a point from #{args.inspect}!"
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
  #
@@ -30,24 +30,38 @@ module ChunkyPNG
30
30
  # @see ChunkyPNG::Point
31
31
  def self.Point(*args)
32
32
  case args.length
33
- when 2; ChunkyPNG::Point.new(*args)
34
- when 1; case source = args.first
35
- when ChunkyPNG::Point; source
36
- when ChunkyPNG::Dimension; ChunkyPNG::Point.new(source.width, source.height)
37
- when Array; ChunkyPNG::Point.new(source[0], source[1])
38
- when Hash; ChunkyPNG::Point.new(source[:x] || source['x'], source[:y] || source['y'])
39
- when ChunkyPNG::Point::POINT_REGEXP; ChunkyPNG::Point.new($1.to_i, $2.to_i)
40
- else
41
- if source.respond_to?(:x) && source.respond_to?(:y)
42
- ChunkyPNG::Point.new(source.x, source.y)
43
- else
44
- raise ArgumentError, "Don't know how to construct a point from #{source.inspect}!"
45
- end
46
- end
47
- else raise ArgumentError, "Don't know how to construct a point from #{args.inspect}!"
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
@@ -1,5 +1,5 @@
1
1
  module ChunkyPNG
2
- # The current version of ChunkyPNG.
2
+ # The current version of ChunkyPNG.
3
3
  # Set it and commit the change this before running rake release.
4
- VERSION = "1.3.1"
4
+ VERSION = "1.3.2"
5
5
  end
@@ -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.1
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-04-28 00:00:00.000000000 Z
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: ! " This pure Ruby library can read and write PNG images without depending
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: 1.8.23
407
+ rubygems_version: 2.0.14
413
408
  signing_key:
414
- specification_version: 3
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