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