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
@@ -3,7 +3,7 @@ module ChunkyPNG
|
|
3
3
|
# The Datastream class represents a PNG formatted datastream. It supports
|
4
4
|
# both reading from and writing to strings, streams and files.
|
5
5
|
#
|
6
|
-
# A PNG datastream begins with the PNG signature, and
|
6
|
+
# A PNG datastream begins with the PNG signature, and then contains multiple
|
7
7
|
# chunks, starting with a header (IHDR) chunk and finishing with an end
|
8
8
|
# (IEND) chunk.
|
9
9
|
#
|
data/lib/chunky_png/palette.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
module ChunkyPNG
|
2
|
-
|
3
2
|
# A palette describes the set of colors that is being used for an image.
|
4
3
|
#
|
5
4
|
# A PNG image can contain an explicit palette which defines the colors of
|
6
|
-
# that image, but can also use an implicit palette, e.g. all truecolor
|
7
|
-
#
|
5
|
+
# that image, but can also use an implicit palette, e.g. all truecolor colors
|
6
|
+
# or all grayscale colors.
|
8
7
|
#
|
9
|
-
# This palette supports decoding colors from a palette if an explicit
|
10
|
-
#
|
11
|
-
#
|
8
|
+
# This palette supports decoding colors from a palette if an explicit palette
|
9
|
+
# is provided in a PNG datastream, and it supports encoding colors to an
|
10
|
+
# explicit palette (stores as PLTE & tRNS chunks in a PNG file).
|
12
11
|
#
|
13
12
|
# @see ChunkyPNG::Color
|
14
13
|
class Palette < SortedSet
|
15
|
-
|
16
14
|
# Builds a new palette given a set (Enumerable instance) of colors.
|
17
15
|
#
|
18
|
-
# @param [Enumerable<Integer>]
|
19
|
-
# This Enumerable can
|
20
|
-
# @param [Array]
|
21
|
-
# they appeared in the palette chunk, so that this array can be
|
16
|
+
# @param enum [Enumerable<Integer>] The set of colors to include in this
|
17
|
+
# palette.This Enumerable can contain duplicates.
|
18
|
+
# @param decoding_map [Array] An array of colors in the exact order at
|
19
|
+
# which they appeared in the palette chunk, so that this array can be
|
20
|
+
# used for decoding.
|
22
21
|
def initialize(enum, decoding_map = nil)
|
23
22
|
super(enum)
|
24
23
|
@decoding_map = decoding_map if decoding_map
|
@@ -29,8 +28,10 @@ module ChunkyPNG
|
|
29
28
|
#
|
30
29
|
# This method will cerate a palette that is suitable for decoding an image.
|
31
30
|
#
|
32
|
-
# @param [ChunkyPNG::Chunk::Palette] The palette chunk to
|
33
|
-
#
|
31
|
+
# @param palette_chunk [ChunkyPNG::Chunk::Palette] The palette chunk to
|
32
|
+
# load from
|
33
|
+
# @param transparency_chunk [ChunkyPNG::Chunk::Transparency, nil] The
|
34
|
+
# optional transparency chunk.
|
34
35
|
# @return [ChunkyPNG::Palette] The loaded palette instance.
|
35
36
|
# @see ChunkyPNG::Palette#can_decode?
|
36
37
|
def self.from_chunks(palette_chunk, transparency_chunk = nil)
|
@@ -53,25 +54,30 @@ module ChunkyPNG
|
|
53
54
|
index += 1
|
54
55
|
end
|
55
56
|
|
56
|
-
|
57
|
+
new(decoding_map, decoding_map)
|
57
58
|
end
|
58
59
|
|
59
60
|
# Builds a palette instance from a given canvas.
|
60
|
-
# @param [ChunkyPNG::Canvas]
|
61
|
+
# @param canvas [ChunkyPNG::Canvas] The canvas to create a palette for.
|
61
62
|
# @return [ChunkyPNG::Palette] The palette instance.
|
62
63
|
def self.from_canvas(canvas)
|
63
|
-
|
64
|
+
# Although we don't need to call .uniq.sort before initializing, because
|
65
|
+
# Palette subclasses SortedSet, we get significantly better performance
|
66
|
+
# by doing so.
|
67
|
+
new(canvas.pixels.uniq.sort)
|
64
68
|
end
|
65
69
|
|
66
70
|
# Builds a palette instance from a given set of pixels.
|
67
|
-
# @param [Enumerable<Integer>]
|
71
|
+
# @param pixels [Enumerable<Integer>] An enumeration of pixels to create a
|
72
|
+
# palette for
|
68
73
|
# @return [ChunkyPNG::Palette] The palette instance.
|
69
74
|
def self.from_pixels(pixels)
|
70
|
-
|
75
|
+
new(pixels)
|
71
76
|
end
|
72
77
|
|
73
78
|
# Checks whether the size of this palette is suitable for indexed storage.
|
74
|
-
# @return [true, false] True if the number of colors in this palette is at
|
79
|
+
# @return [true, false] True if the number of colors in this palette is at
|
80
|
+
# most 256.
|
75
81
|
def indexable?
|
76
82
|
size <= 256
|
77
83
|
end
|
@@ -84,56 +90,67 @@ module ChunkyPNG
|
|
84
90
|
end
|
85
91
|
|
86
92
|
# Check whether this palette only contains grayscale colors.
|
87
|
-
# @return [true, false] True if all colors in this palette are grayscale
|
93
|
+
# @return [true, false] True if all colors in this palette are grayscale
|
94
|
+
# teints.
|
88
95
|
# @see ChunkyPNG::Color#grayscale??
|
89
96
|
def grayscale?
|
90
97
|
all? { |color| Color.grayscale?(color) }
|
91
98
|
end
|
92
99
|
|
93
100
|
# Check whether this palette only contains bacl and white.
|
94
|
-
# @return [true, false] True if all colors in this palette are grayscale
|
101
|
+
# @return [true, false] True if all colors in this palette are grayscale
|
102
|
+
# teints.
|
95
103
|
# @see ChunkyPNG::Color#grayscale??
|
96
104
|
def black_and_white?
|
97
105
|
entries == [ChunkyPNG::Color::BLACK, ChunkyPNG::Color::WHITE]
|
98
106
|
end
|
99
|
-
|
100
|
-
# Returns a palette with all the opaque variants of the colors in this
|
101
|
-
#
|
107
|
+
|
108
|
+
# Returns a palette with all the opaque variants of the colors in this
|
109
|
+
# palette.
|
110
|
+
# @return [ChunkyPNG::Palette] A new Palette instance with only opaque
|
111
|
+
# colors.
|
102
112
|
# @see ChunkyPNG::Color#opaque!
|
103
113
|
def opaque_palette
|
104
114
|
self.class.new(map { |c| ChunkyPNG::Color.opaque!(c) })
|
105
115
|
end
|
106
116
|
|
107
|
-
# Checks whether this palette is suitable for decoding an image from a
|
117
|
+
# Checks whether this palette is suitable for decoding an image from a
|
118
|
+
# datastream.
|
108
119
|
#
|
109
|
-
# This requires that the positions of the colors in the original palette
|
110
|
-
# which is stored as an array in the +@decoding_map+
|
120
|
+
# This requires that the positions of the colors in the original palette
|
121
|
+
# chunk is known, which is stored as an array in the +@decoding_map+
|
122
|
+
# instance variable.
|
111
123
|
#
|
112
|
-
# @return [true, false] True if a decoding map was built when this palette
|
124
|
+
# @return [true, false] True if a decoding map was built when this palette
|
125
|
+
# was loaded.
|
113
126
|
def can_decode?
|
114
127
|
!@decoding_map.nil?
|
115
128
|
end
|
116
129
|
|
117
|
-
# Checks whether this palette is suitable for encoding an image from to
|
130
|
+
# Checks whether this palette is suitable for encoding an image from to
|
131
|
+
# datastream.
|
118
132
|
#
|
119
|
-
# This requires that the position of the color in the future palette chunk
|
120
|
-
# which is stored as a hash in the +@encoding_map+ instance
|
133
|
+
# This requires that the position of the color in the future palette chunk
|
134
|
+
# is known, which is stored as a hash in the +@encoding_map+ instance
|
135
|
+
# variable.
|
121
136
|
#
|
122
|
-
# @return [true, false] True if a encoding map was built when this palette
|
137
|
+
# @return [true, false] True if a encoding map was built when this palette
|
138
|
+
# was loaded.
|
123
139
|
def can_encode?
|
124
140
|
!@encoding_map.nil?
|
125
141
|
end
|
126
142
|
|
127
143
|
# Returns a color, given the position in the original palette chunk.
|
128
|
-
# @param [Integer]
|
129
|
-
# @return [ChunkyPNG::Color] The color that is stored in the palette under
|
144
|
+
# @param index [Integer] The 0-based position of the color in the palette.
|
145
|
+
# @return [ChunkyPNG::Color] The color that is stored in the palette under
|
146
|
+
# the given index
|
130
147
|
# @see ChunkyPNG::Palette#can_decode?
|
131
148
|
def [](index)
|
132
149
|
@decoding_map[index]
|
133
150
|
end
|
134
151
|
|
135
152
|
# Returns the position of a color in the palette
|
136
|
-
# @param [ChunkyPNG::Color]
|
153
|
+
# @param color [ChunkyPNG::Color] The color for which to look up the index.
|
137
154
|
# @return [Integer] The 0-based position of the color in the palette.
|
138
155
|
# @see ChunkyPNG::Palette#can_encode?
|
139
156
|
def index(color)
|
@@ -151,12 +168,12 @@ module ChunkyPNG
|
|
151
168
|
ChunkyPNG::Chunk::Transparency.new('tRNS', map { |c| ChunkyPNG::Color.a(c) }.pack('C*'))
|
152
169
|
end
|
153
170
|
|
154
|
-
# Creates a PLTE chunk that corresponds with this palette to store the
|
155
|
-
#
|
171
|
+
# Creates a PLTE chunk that corresponds with this palette to store the r,
|
172
|
+
# g, and b channels of all colors.
|
156
173
|
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
174
|
+
# @note A PLTE chunk should only be included if the image is encoded using
|
175
|
+
# index colors. After this chunk has been built, the palette becomes
|
176
|
+
# suitable for encoding an image.
|
160
177
|
#
|
161
178
|
# @return [ChunkyPNG::Chunk::Palette] The PLTE chunk.
|
162
179
|
# @see ChunkyPNG::Palette#can_encode?
|
@@ -174,7 +191,7 @@ module ChunkyPNG
|
|
174
191
|
|
175
192
|
# Determines the most suitable colormode for this palette.
|
176
193
|
# @return [Integer] The colormode which would create the smallest possible
|
177
|
-
#
|
194
|
+
# file for images that use this exact palette.
|
178
195
|
def best_color_settings
|
179
196
|
if black_and_white?
|
180
197
|
[ChunkyPNG::COLOR_GRAYSCALE, 1]
|
@@ -192,17 +209,17 @@ module ChunkyPNG
|
|
192
209
|
[ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 8]
|
193
210
|
end
|
194
211
|
end
|
195
|
-
|
212
|
+
|
196
213
|
# Determines the minimal bit depth required for an indexed image
|
197
|
-
# @return [Integer] Number of bits per pixel, i.e. 1, 2, 4 or 8, or nil if
|
198
|
-
#
|
214
|
+
# @return [Integer] Number of bits per pixel, i.e. 1, 2, 4 or 8, or nil if
|
215
|
+
# this image cannot be saved as an indexed image.
|
199
216
|
def determine_bit_depth
|
200
217
|
case size
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
218
|
+
when 1..2 then 1
|
219
|
+
when 3..4 then 2
|
220
|
+
when 5..16 then 4
|
221
|
+
when 17..256 then 8
|
222
|
+
else nil
|
206
223
|
end
|
207
224
|
end
|
208
225
|
end
|
data/lib/chunky_png/version.rb
CHANGED
@@ -11,6 +11,15 @@ describe ChunkyPNG::Canvas do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
describe '.from_bgr_stream' do
|
15
|
+
it "should load an image correctly from a datastream" do
|
16
|
+
File.open(resource_file('pixelstream.bgr')) do |stream|
|
17
|
+
matrix = ChunkyPNG::Canvas.from_bgr_stream(240, 180, stream)
|
18
|
+
matrix.should == reference_canvas('pixelstream_reference')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
14
23
|
describe '.from_rgba_stream' do
|
15
24
|
it "should load an image correctly from a datastream" do
|
16
25
|
File.open(resource_file('pixelstream.rgba')) do |stream|
|
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'ChunyPNG.Color' do
|
4
|
-
it
|
4
|
+
it 'should interpret 4 arguments as RGBA values' do
|
5
5
|
ChunkyPNG::Color(1, 2, 3, 4).should == ChunkyPNG::Color.rgba(1, 2, 3, 4)
|
6
6
|
end
|
7
|
-
|
8
|
-
it
|
7
|
+
|
8
|
+
it 'should interpret 3 arguments as RGBA values' do
|
9
9
|
ChunkyPNG::Color(1, 2, 3).should == ChunkyPNG::Color.rgb(1, 2, 3)
|
10
10
|
end
|
11
|
-
|
12
|
-
it
|
11
|
+
|
12
|
+
it 'should interpret 2 arguments as a color to parse and an opacity value' do
|
13
13
|
ChunkyPNG::Color('0x0a649664', 0xaa).should == 0x0a6496aa
|
14
14
|
ChunkyPNG::Color('spring green @ 0.6666', 0xff).should == 0x00ff7fff
|
15
15
|
end
|
16
|
-
|
17
|
-
it
|
16
|
+
|
17
|
+
it 'should interpret 1 argument as a color to parse' do
|
18
18
|
ChunkyPNG::Color.should_receive(:parse).with('0x0a649664')
|
19
19
|
ChunkyPNG::Color('0x0a649664')
|
20
20
|
end
|
@@ -30,55 +30,55 @@ describe ChunkyPNG::Color do
|
|
30
30
|
@non_opaque = 0x0a649664
|
31
31
|
@fully_transparent = 0x0a649600
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
describe '#parse' do
|
35
|
-
it
|
35
|
+
it 'should interpret a hex string correctly' do
|
36
36
|
parse('0x0a649664').should == ChunkyPNG::Color.from_hex('#0a649664')
|
37
37
|
end
|
38
38
|
|
39
|
-
it
|
39
|
+
it 'should interpret a color name correctly' do
|
40
40
|
parse(:spring_green).should == 0x00ff7fff
|
41
41
|
parse('spring green').should == 0x00ff7fff
|
42
42
|
parse('spring green @ 0.6666').should == 0x00ff7faa
|
43
43
|
end
|
44
|
-
|
45
|
-
it
|
44
|
+
|
45
|
+
it 'should return numbers as is' do
|
46
46
|
parse('12345').should == 12345
|
47
47
|
parse(12345).should == 12345
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
describe '#pixel_bytesize' do
|
52
|
-
it
|
52
|
+
it 'should return the normal amount of bytes with a bit depth of 8' do
|
53
53
|
pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8).should == 3
|
54
54
|
end
|
55
55
|
|
56
|
-
it
|
56
|
+
it 'should return a multiple of the normal amount of bytes with a bit depth greater than 8' do
|
57
57
|
pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 16).should == 6
|
58
58
|
pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 16).should == 8
|
59
59
|
pixel_bytesize(ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 16).should == 4
|
60
60
|
end
|
61
|
-
|
62
|
-
it
|
61
|
+
|
62
|
+
it 'should return 1 with a bit depth lower than 0' do
|
63
63
|
pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 4).should == 1
|
64
64
|
pixel_bytesize(ChunkyPNG::COLOR_INDEXED, 2).should == 1
|
65
65
|
pixel_bytesize(ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 1).should == 1
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
describe '#pass_bytesize' do
|
70
|
-
it
|
70
|
+
it 'should calculate a pass size correctly' do
|
71
71
|
pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 10, 10).should == 310
|
72
72
|
end
|
73
|
-
|
74
|
-
it
|
73
|
+
|
74
|
+
it 'should return 0 if one of the dimensions is zero' do
|
75
75
|
pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 0, 10).should == 0
|
76
76
|
pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 10, 0).should == 0
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
80
|
describe '#rgba' do
|
81
|
-
it
|
81
|
+
it 'should represent pixels as the correct number' do
|
82
82
|
rgba(255, 255, 255, 255).should == @white
|
83
83
|
rgba( 0, 0, 0, 255).should == @black
|
84
84
|
rgba( 10, 100, 150, 255).should == @opaque
|
@@ -86,9 +86,9 @@ describe ChunkyPNG::Color do
|
|
86
86
|
rgba( 10, 100, 150, 0).should == @fully_transparent
|
87
87
|
end
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
describe '#from_hex' do
|
91
|
-
it
|
91
|
+
it 'should load colors correctly from hex notation' do
|
92
92
|
from_hex('0a649664').should == @non_opaque
|
93
93
|
from_hex('#0a649664').should == @non_opaque
|
94
94
|
from_hex('0x0a649664').should == @non_opaque
|
@@ -99,17 +99,17 @@ describe ChunkyPNG::Color do
|
|
99
99
|
from_hex('#abc').should == 0xaabbccff
|
100
100
|
from_hex('0xabc').should == 0xaabbccff
|
101
101
|
end
|
102
|
-
|
103
|
-
it
|
102
|
+
|
103
|
+
it 'should allow setting opacity explicitly' do
|
104
104
|
from_hex('0x0a6496', 0x64).should == @non_opaque
|
105
105
|
from_hex('#0a6496', 0x64).should == @non_opaque
|
106
106
|
from_hex('0xabc', 0xdd).should == 0xaabbccdd
|
107
107
|
from_hex('#abc', 0xdd).should == 0xaabbccdd
|
108
108
|
end
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
describe '#html_color' do
|
112
|
-
it
|
112
|
+
it 'should find the correct color value' do
|
113
113
|
html_color(:springgreen).should == 0x00ff7fff
|
114
114
|
html_color(:spring_green).should == 0x00ff7fff
|
115
115
|
html_color('springgreen').should == 0x00ff7fff
|
@@ -117,26 +117,26 @@ describe ChunkyPNG::Color do
|
|
117
117
|
html_color('SpringGreen').should == 0x00ff7fff
|
118
118
|
html_color('SPRING_GREEN').should == 0x00ff7fff
|
119
119
|
end
|
120
|
-
|
121
|
-
it
|
120
|
+
|
121
|
+
it 'should set the opacity level explicitly' do
|
122
122
|
html_color(:springgreen, 0xff).should == 0x00ff7fff
|
123
123
|
html_color(:springgreen, 0xaa).should == 0x00ff7faa
|
124
124
|
html_color(:springgreen, 0x00).should == 0x00ff7f00
|
125
125
|
end
|
126
|
-
|
127
|
-
it
|
126
|
+
|
127
|
+
it 'should set opacity levels from the color name' do
|
128
128
|
html_color('Spring green @ 1.0').should == 0x00ff7fff
|
129
129
|
html_color('Spring green @ 0.666').should == 0x00ff7faa
|
130
130
|
html_color('Spring green @ 0.0').should == 0x00ff7f00
|
131
131
|
end
|
132
|
-
|
133
|
-
it
|
132
|
+
|
133
|
+
it 'should raise for an unkown color name' do
|
134
134
|
lambda { html_color(:nonsense) }.should raise_error(ArgumentError)
|
135
135
|
end
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
describe '#opaque?' do
|
139
|
-
it
|
139
|
+
it 'should correctly check for opaqueness' do
|
140
140
|
opaque?(@white).should be_true
|
141
141
|
opaque?(@black).should be_true
|
142
142
|
opaque?(@opaque).should be_true
|
@@ -144,47 +144,47 @@ describe ChunkyPNG::Color do
|
|
144
144
|
opaque?(@fully_transparent).should be_false
|
145
145
|
end
|
146
146
|
end
|
147
|
-
|
148
|
-
describe '
|
149
|
-
it
|
147
|
+
|
148
|
+
describe 'extraction of separate color channels' do
|
149
|
+
it 'should extract components from a color correctly' do
|
150
150
|
r(@opaque).should == 10
|
151
151
|
g(@opaque).should == 100
|
152
152
|
b(@opaque).should == 150
|
153
153
|
a(@opaque).should == 255
|
154
154
|
end
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
157
|
describe '#grayscale_teint' do
|
158
|
-
it
|
158
|
+
it 'should calculate the correct grayscale teint' do
|
159
159
|
grayscale_teint(@opaque).should == 79
|
160
160
|
grayscale_teint(@non_opaque).should == 79
|
161
161
|
end
|
162
162
|
end
|
163
|
-
|
163
|
+
|
164
164
|
describe '#to_grayscale' do
|
165
|
-
it
|
165
|
+
it 'should use the grayscale teint for r, g and b' do
|
166
166
|
gs = to_grayscale(@non_opaque)
|
167
167
|
r(gs).should == grayscale_teint(@non_opaque)
|
168
168
|
g(gs).should == grayscale_teint(@non_opaque)
|
169
169
|
b(gs).should == grayscale_teint(@non_opaque)
|
170
170
|
end
|
171
|
-
|
172
|
-
it
|
171
|
+
|
172
|
+
it 'should preserve the alpha channel' do
|
173
173
|
a(to_grayscale(@non_opaque)).should == a(@non_opaque)
|
174
174
|
a(to_grayscale(@opaque)).should == ChunkyPNG::Color::MAX
|
175
175
|
end
|
176
176
|
end
|
177
|
-
|
177
|
+
|
178
178
|
describe '#to_hex' do
|
179
|
-
it
|
179
|
+
it 'should represent colors correcly using hex notation' do
|
180
180
|
to_hex(@white).should == '#ffffffff'
|
181
181
|
to_hex(@black).should == '#000000ff'
|
182
182
|
to_hex(@opaque).should == '#0a6496ff'
|
183
183
|
to_hex(@non_opaque).should == '#0a649664'
|
184
184
|
to_hex(@fully_transparent).should == '#0a649600'
|
185
185
|
end
|
186
|
-
|
187
|
-
it
|
186
|
+
|
187
|
+
it 'should represent colors correcly using hex notation without alpha channel' do
|
188
188
|
to_hex(@white, false).should == '#ffffff'
|
189
189
|
to_hex(@black, false).should == '#000000'
|
190
190
|
to_hex(@opaque, false).should == '#0a6496'
|
@@ -194,58 +194,82 @@ describe ChunkyPNG::Color do
|
|
194
194
|
end
|
195
195
|
|
196
196
|
describe 'conversion to other formats' do
|
197
|
-
it
|
197
|
+
it 'should convert the individual color values back correctly' do
|
198
198
|
to_truecolor_bytes(@opaque).should == [10, 100, 150]
|
199
199
|
to_truecolor_alpha_bytes(@non_opaque).should == [10, 100, 150, 100]
|
200
200
|
end
|
201
201
|
end
|
202
|
-
|
202
|
+
|
203
203
|
describe '#compose' do
|
204
204
|
|
205
|
-
it
|
205
|
+
it 'should use the foregorund color as is when the background color is fully transparent' do
|
206
206
|
compose(@non_opaque, @fully_transparent).should == @non_opaque
|
207
207
|
end
|
208
208
|
|
209
|
-
it
|
209
|
+
it 'should use the foregorund color as is when an opaque color is given as foreground color' do
|
210
210
|
compose(@opaque, @white).should == @opaque
|
211
211
|
end
|
212
212
|
|
213
|
-
it
|
213
|
+
it 'should use the background color as is when a fully transparent pixel is given as foreground color' do
|
214
214
|
compose(@fully_transparent, @white).should == @white
|
215
215
|
end
|
216
216
|
|
217
|
-
it
|
217
|
+
it 'should compose pixels correctly with both algorithms' do
|
218
218
|
compose_quick(@non_opaque, @white).should == 0x9fc2d6ff
|
219
219
|
compose_precise(@non_opaque, @white).should == 0x9fc2d6ff
|
220
220
|
end
|
221
221
|
end
|
222
|
-
|
222
|
+
|
223
223
|
describe '#decompose_alpha' do
|
224
|
-
it
|
224
|
+
it 'should decompose the alpha channel correctly' do
|
225
225
|
decompose_alpha(0x9fc2d6ff, @opaque, @white).should == 0x00000064
|
226
226
|
end
|
227
|
-
|
228
|
-
it
|
227
|
+
|
228
|
+
it 'should return fully transparent if the background channel matches the resulting color' do
|
229
229
|
decompose_alpha(0xabcdefff, 0xff000000, 0xabcdefff).should == 0x00
|
230
230
|
end
|
231
|
-
|
232
|
-
it
|
231
|
+
|
232
|
+
it 'should return fully opaque if the background channel matches the mask color' do
|
233
233
|
decompose_alpha(0xff000000, 0xabcdefff, 0xabcdefff).should == 0xff
|
234
234
|
end
|
235
|
-
|
236
|
-
it
|
235
|
+
|
236
|
+
it 'should return fully opaque if the resulting color matches the mask color' do
|
237
237
|
decompose_alpha(0xabcdefff, 0xabcdefff, 0xffffffff).should == 255
|
238
|
-
end
|
238
|
+
end
|
239
239
|
end
|
240
|
-
|
240
|
+
|
241
241
|
describe '#blend' do
|
242
|
-
it
|
242
|
+
it 'should blend colors correctly' do
|
243
243
|
blend(@opaque, @black).should == 0x05324bff
|
244
244
|
end
|
245
|
-
|
246
|
-
it
|
245
|
+
|
246
|
+
it 'should not matter what color is used as foreground, and what as background' do
|
247
247
|
blend(@opaque, @black).should == blend(@black, @opaque)
|
248
248
|
end
|
249
249
|
end
|
250
|
-
end
|
251
250
|
|
251
|
+
describe '#euclidean_distance_rgba' do
|
252
|
+
subject { euclidean_distance_rgba(color_a, color_b) }
|
253
|
+
|
254
|
+
context 'with white and black' do
|
255
|
+
let(:color_a) { @white }
|
256
|
+
let(:color_b) { @black }
|
257
|
+
|
258
|
+
it { should == Math.sqrt(195_075) } # sqrt(255^2 * 3)
|
259
|
+
end
|
260
|
+
|
261
|
+
context 'with black and white' do
|
262
|
+
let(:color_a) { @black }
|
263
|
+
let(:color_b) { @white }
|
264
|
+
|
265
|
+
it { should == Math.sqrt(195_075) } # sqrt(255^2 * 3)
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'with the same colors' do
|
269
|
+
let(:color_a) { @white }
|
270
|
+
let(:color_b) { @white }
|
271
|
+
|
272
|
+
it { should == 0 }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|