color 1.4.2 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +2 -3
- data/History.rdoc +33 -0
- data/Manifest.txt +1 -1
- data/README.rdoc +14 -2
- data/Rakefile +20 -5
- data/lib/color.rb +104 -53
- data/lib/color/cmyk.rb +39 -43
- data/lib/color/css.rb +1 -10
- data/lib/color/grayscale.rb +33 -34
- data/lib/color/hsl.rb +96 -65
- data/lib/color/palette/monocontrast.rb +24 -28
- data/lib/color/rgb.rb +152 -67
- data/lib/color/rgb/colors.rb +166 -0
- data/lib/color/rgb/metallic.rb +26 -29
- data/lib/color/yiq.rb +13 -23
- data/test/test_adobecolor.rb +2 -2
- data/test/test_cmyk.rb +2 -2
- data/test/test_color.rb +2 -3
- data/test/test_css.rb +11 -9
- data/test/test_gimp.rb +2 -2
- data/test/test_grayscale.rb +2 -2
- data/test/test_hsl.rb +2 -2
- data/test/test_monocontrast.rb +2 -2
- data/test/test_rgb.rb +38 -4
- data/test/test_yiq.rb +2 -2
- metadata +57 -94
- metadata.gz.sig +0 -0
- data/lib/color/rgb-colors.rb +0 -343
data/lib/color/css.rb
CHANGED
@@ -1,16 +1,7 @@
|
|
1
|
-
require 'color'
|
2
|
-
|
3
1
|
# This namespace contains some CSS colour names.
|
4
2
|
module Color::CSS
|
5
3
|
# Returns the RGB colour for name or +nil+ if the name is not valid.
|
6
4
|
def self.[](name)
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
@colors = {}
|
11
|
-
Color::RGB.constants.each do |const|
|
12
|
-
next if const == "PDF_FORMAT_STR"
|
13
|
-
next if const == "Metallic"
|
14
|
-
@colors[const.downcase.to_sym] ||= Color::RGB.const_get(const)
|
5
|
+
Color::RGB.by_name(name) { nil }
|
15
6
|
end
|
16
7
|
end
|
data/lib/color/grayscale.rb
CHANGED
@@ -1,45 +1,40 @@
|
|
1
1
|
# A colour object representing shades of grey. Used primarily in PDF
|
2
2
|
# document creation.
|
3
3
|
class Color::GrayScale
|
4
|
+
include Color
|
5
|
+
|
4
6
|
# The format of a DeviceGrey colour for PDF. In color-tools 2.0 this will
|
5
7
|
# be removed from this package and added back as a modification by the
|
6
8
|
# PDF::Writer package.
|
7
9
|
PDF_FORMAT_STR = "%.3f %s"
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
11
|
+
class << self
|
12
|
+
# Creates a greyscale colour object from fractional values 0..1.
|
13
|
+
#
|
14
|
+
# Color::GreyScale.from_fraction(0.5)
|
15
|
+
def from_fraction(g = 0, &block)
|
16
|
+
new(g, 1.0, &block)
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
# Creates a greyscale colour object from percentages 0..100.
|
20
|
+
#
|
21
|
+
# Color::GrayScale.from_percent(50)
|
22
|
+
def from_percent(g = 0, &block)
|
23
|
+
new(g, &block)
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
27
|
# Creates a greyscale colour object from percentages 0..100.
|
26
28
|
#
|
27
29
|
# Color::GrayScale.new(50)
|
28
|
-
def initialize(g = 0)
|
29
|
-
@g = g /
|
30
|
+
def initialize(g = 0, radix = 100.0, &block) # :yields self:
|
31
|
+
@g = Color.normalize(g / radix)
|
32
|
+
block.call if block
|
30
33
|
end
|
31
34
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
# based on the other colour's #to_greyscale conversion. If there is no
|
36
|
-
# #to_greyscale conversion, this will raise an exception. This will report
|
37
|
-
# that two GreyScale values are equivalent if they are within
|
38
|
-
# COLOR_TOLERANCE of each other.
|
39
|
-
def ==(other)
|
40
|
-
other = other.to_grayscale
|
41
|
-
other.kind_of?(Color::GrayScale) and
|
42
|
-
((@g - other.g).abs <= Color::COLOR_TOLERANCE)
|
35
|
+
# Coerces the other Color object to grayscale.
|
36
|
+
def coerce(other)
|
37
|
+
other.to_grayscale
|
43
38
|
end
|
44
39
|
|
45
40
|
# Present the colour as a DeviceGrey fill colour string for PDF. This will
|
@@ -170,10 +165,7 @@ class Color::GrayScale
|
|
170
165
|
# The addition is done using the grayscale accessor methods to ensure a
|
171
166
|
# valid colour in the result.
|
172
167
|
def +(other)
|
173
|
-
|
174
|
-
ng = self.dup
|
175
|
-
ng.g += other.g
|
176
|
-
ng
|
168
|
+
self.class.from_fraction(g + other.to_grayscale.g)
|
177
169
|
end
|
178
170
|
|
179
171
|
# Subtracts another colour to the current colour. The other colour will be
|
@@ -183,15 +175,22 @@ class Color::GrayScale
|
|
183
175
|
# The subtraction is done using the grayscale accessor methods to ensure a
|
184
176
|
# valid colour in the result.
|
185
177
|
def -(other)
|
186
|
-
|
187
|
-
ng = self.dup
|
188
|
-
ng.g -= other.g
|
189
|
-
ng
|
178
|
+
self + (-other)
|
190
179
|
end
|
191
180
|
|
192
181
|
def inspect
|
193
182
|
"Gray [%.2f%%]" % [ gray ]
|
194
183
|
end
|
184
|
+
|
185
|
+
def to_a
|
186
|
+
[ g ]
|
187
|
+
end
|
188
|
+
|
189
|
+
def -@
|
190
|
+
gs = self.dup
|
191
|
+
gs.instance_variable_set(:@g, -g)
|
192
|
+
gs
|
193
|
+
end
|
195
194
|
end
|
196
195
|
|
197
196
|
# A synonym for Color::GrayScale.
|
data/lib/color/hsl.rb
CHANGED
@@ -1,39 +1,30 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
1
3
|
# An HSL colour object. Internally, the hue (#h), saturation (#s), and
|
2
4
|
# luminosity/lightness (#l) values are dealt with as fractional values in
|
3
5
|
# the range 0..1.
|
4
6
|
class Color::HSL
|
7
|
+
include Color
|
8
|
+
|
5
9
|
class << self
|
6
10
|
# Creates an HSL colour object from fractional values 0..1.
|
7
|
-
def from_fraction(h = 0.0, s = 0.0, l = 0.0)
|
8
|
-
|
9
|
-
colour.h = h
|
10
|
-
colour.s = s
|
11
|
-
colour.l = l
|
12
|
-
colour
|
11
|
+
def from_fraction(h = 0.0, s = 0.0, l = 0.0, &block)
|
12
|
+
new(h, s, l, 1.0, 1.0, &block)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
# colour's #to_hsl conversion. If there is no #to_hsl conversion, this
|
20
|
-
# will raise an exception. This will report that two HSL values are
|
21
|
-
# equivalent if all component values are within Color::COLOR_TOLERANCE of
|
22
|
-
# each other.
|
23
|
-
def ==(other)
|
24
|
-
other = other.to_hsl
|
25
|
-
other.kind_of?(Color::HSL) and
|
26
|
-
((@h - other.h).abs <= Color::COLOR_TOLERANCE) and
|
27
|
-
((@s - other.s).abs <= Color::COLOR_TOLERANCE) and
|
28
|
-
((@l - other.l).abs <= Color::COLOR_TOLERANCE)
|
16
|
+
# Coerces the other Color object into HSL.
|
17
|
+
def coerce(other)
|
18
|
+
other.to_hsl
|
29
19
|
end
|
30
20
|
|
31
21
|
# Creates an HSL colour object from the standard values of degrees and
|
32
22
|
# percentages (e.g., 145 deg, 30%, 50%).
|
33
|
-
def initialize(h = 0, s = 0, l = 0)
|
34
|
-
@h = h /
|
35
|
-
@s = s /
|
36
|
-
@l = l /
|
23
|
+
def initialize(h = 0, s = 0, l = 0, radix1 = 360.0, radix2 = 100.0, &block) # :yields self:
|
24
|
+
@h = Color.normalize(h / radix1)
|
25
|
+
@s = Color.normalize(s / radix2)
|
26
|
+
@l = Color.normalize(l / radix2)
|
27
|
+
block.call if block
|
37
28
|
end
|
38
29
|
|
39
30
|
# Present the colour as an HTML/CSS colour string.
|
@@ -67,46 +58,30 @@ class Color::HSL
|
|
67
58
|
"hsla(%3.2f, %3.2f%%, %3.2f%%, %3.2f)" % [ hue, saturation, luminosity, 1 ]
|
68
59
|
end
|
69
60
|
|
70
|
-
# Converting
|
71
|
-
#
|
61
|
+
# Converting from HSL to RGB. As with all colour conversions, this is
|
62
|
+
# approximate at best. The code here is adapted from fvd and van Dam,
|
63
|
+
# originally found at [1] (implemented similarly at [2]).
|
72
64
|
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
65
|
+
# This simplifies the calculations with the following assumptions:
|
66
|
+
# - Luminance values <= 0 always translate to Color::RGB::Black.
|
67
|
+
# - Luminance values >= 1 always translate to Color::RGB::White.
|
68
|
+
# - Saturation values <= 0 always translate to a shade of gray using
|
69
|
+
# luminance as a percentage of gray.
|
78
70
|
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
71
|
+
# [1] http://bobpowell.net/RGBHSB.aspx
|
72
|
+
# [2] http://support.microsoft.com/kb/29240
|
73
|
+
def to_rgb(*)
|
74
|
+
if Color.near_zero_or_less?(l)
|
75
|
+
Color::RGB::Black
|
76
|
+
elsif Color.near_one_or_more?(l)
|
77
|
+
Color::RGB::White
|
78
|
+
elsif Color.near_zero?(s)
|
79
|
+
Color::RGB.from_grayscale_fraction(l)
|
87
80
|
else
|
88
|
-
|
81
|
+
# Only needed for Ruby 1.8. For Ruby 1.9+, we can do:
|
82
|
+
# Color::RGB.new(*compute_fvd_rgb, 1.0)
|
83
|
+
Color::RGB.new(*(compute_fvd_rgb + [ 1.0 ]))
|
89
84
|
end
|
90
|
-
tmp1 = 2.0 * @l - tmp2
|
91
|
-
|
92
|
-
tmp3 = [ @h + (1.0 / 3.0), @h, @h - (1.0 / 3.0) ]
|
93
|
-
|
94
|
-
rgb = tmp3.map { |hue|
|
95
|
-
hue += 1.0 if Color.near_zero_or_less?(hue)
|
96
|
-
hue -= 1.0 if Color.near_one_or_more?(hue)
|
97
|
-
|
98
|
-
if Color.near_zero_or_less?((6.0 * hue) - 1.0)
|
99
|
-
tmp1 + ((tmp2 - tmp1) * hue * 6.0)
|
100
|
-
elsif Color.near_zero_or_less?((2.0 * hue) - 1.0)
|
101
|
-
tmp2
|
102
|
-
elsif Color.near_zero_or_less?((3.0 * hue) - 2.0)
|
103
|
-
tmp1 + (tmp2 - tmp1) * ((2 / 3.0) - hue) * 6.0
|
104
|
-
else
|
105
|
-
tmp1
|
106
|
-
end
|
107
|
-
}
|
108
|
-
|
109
|
-
Color::RGB.from_fraction(*rgb)
|
110
85
|
end
|
111
86
|
|
112
87
|
# Converts to RGB then YIQ.
|
@@ -197,13 +172,69 @@ class Color::HSL
|
|
197
172
|
# Mix the mask colour (which will be converted to an HSL colour) with the
|
198
173
|
# current colour at the stated mix percentage as a decimal value.
|
199
174
|
#
|
200
|
-
# NOTE::
|
175
|
+
# NOTE:: This differs from Color::RGB#mix_with.
|
201
176
|
def mix_with(color, mix_percent = 0.5)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
177
|
+
v = to_a.zip(coerce(color).to_a).map { |(x, y)|
|
178
|
+
((y - x) * mix_percent) + x
|
179
|
+
}
|
180
|
+
self.class.from_fraction(*v)
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_a
|
184
|
+
[ h, s, l ]
|
185
|
+
end
|
206
186
|
|
207
|
-
|
187
|
+
private
|
188
|
+
|
189
|
+
# This algorithm calculates based on a mixture of the saturation and
|
190
|
+
# luminance, and then takes the RGB values from the hue + 1/3, hue, and
|
191
|
+
# hue - 1/3 positions in a circular representation of colour divided into
|
192
|
+
# four parts (confusing, I know, but it's the way that it works). See
|
193
|
+
# #hue_to_rgb for more information.
|
194
|
+
def compute_fvd_rgb
|
195
|
+
t1, t2 = fvd_mix_sat_lum
|
196
|
+
[ h + (1 / 3.0), h, h - (1 / 3.0) ].map { |v|
|
197
|
+
hue_to_rgb(rotate_hue(v), t1, t2)
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
# Mix saturation and luminance for use in hue_to_rgb. The base value is
|
202
|
+
# different depending on whether luminance is <= 50% or > 50%.
|
203
|
+
def fvd_mix_sat_lum
|
204
|
+
t = if Color.near_zero_or_less?(l - 0.5)
|
205
|
+
l * (1.0 + s.to_f)
|
206
|
+
else
|
207
|
+
l + s - (l * s.to_f)
|
208
|
+
end
|
209
|
+
[ 2.0 * l - t, t ]
|
210
|
+
end
|
211
|
+
|
212
|
+
# In HSL, hues are referenced as degrees in a colour circle. The flow
|
213
|
+
# itself is endless; therefore, we can rotate around. The only thing our
|
214
|
+
# implementation restricts is that you should not be > 1.0.
|
215
|
+
def rotate_hue(h)
|
216
|
+
h += 1.0 if Color.near_zero_or_less?(h)
|
217
|
+
h -= 1.0 if Color.near_one_or_more?(h)
|
218
|
+
h
|
219
|
+
end
|
220
|
+
|
221
|
+
# We calculate the interaction of the saturation/luminance mix (calculated
|
222
|
+
# earlier) based on the position of the hue in the circular colour space
|
223
|
+
# divided into quadrants. Our hue range is [0, 1), not [0, 360º).
|
224
|
+
#
|
225
|
+
# - The first quadrant covers the first 60º [0, 60º].
|
226
|
+
# - The second quadrant covers the next 120º (60º, 180º].
|
227
|
+
# - The third quadrant covers the next 60º (180º, 240º].
|
228
|
+
# - The fourth quadrant covers the final 120º (240º, 360º).
|
229
|
+
def hue_to_rgb(h, t1, t2)
|
230
|
+
if Color.near_zero_or_less?((6.0 * h) - 1.0)
|
231
|
+
t1 + ((t2 - t1) * h * 6.0)
|
232
|
+
elsif Color.near_zero_or_less?((2.0 * h) - 1.0)
|
233
|
+
t2
|
234
|
+
elsif Color.near_zero_or_less?((3.0 * h) - 2.0)
|
235
|
+
t1 + (t2 - t1) * ((2 / 3.0) - h) * 6.0
|
236
|
+
else
|
237
|
+
t1
|
238
|
+
end
|
208
239
|
end
|
209
240
|
end
|
@@ -36,19 +36,17 @@ class Color::Palette::MonoContrast
|
|
36
36
|
# the palette based on the base colours. The default value for this is 125
|
37
37
|
# / 255.0. If this value is set to +nil+, it will be restored to the
|
38
38
|
# default.
|
39
|
-
|
40
|
-
remove_method :minimum_brightness_diff= ;
|
39
|
+
attr_reader :minimum_brightness_diff
|
41
40
|
def minimum_brightness_diff=(bd) #:nodoc:
|
42
|
-
if bd.nil?
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
@minimum_brightness_diff = if bd.nil?
|
42
|
+
DEFAULT_MINIMUM_BRIGHTNESS_DIFF
|
43
|
+
elsif bd > 1.0
|
44
|
+
1.0
|
45
|
+
elsif bd < 0.0
|
46
|
+
0.0
|
47
|
+
else
|
48
|
+
bd
|
49
|
+
end
|
52
50
|
regenerate(@background[0], @foreground[0])
|
53
51
|
end
|
54
52
|
|
@@ -57,18 +55,17 @@ class Color::Palette::MonoContrast
|
|
57
55
|
# The minimum colour difference between the background and the foreground,
|
58
56
|
# and must be between 0..3. Setting this value will regenerate the palette
|
59
57
|
# based on the base colours. The default value for this is 500 / 255.0.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
58
|
+
attr_reader :minimum_color_diff
|
59
|
+
def minimum_color_diff=(cd) #:nodoc:
|
60
|
+
@minimum_color_diff = if cd.nil?
|
61
|
+
DEFAULT_MINIMUM_COLOR_DIFF
|
62
|
+
elsif cd > 3.0
|
63
|
+
3.0
|
64
|
+
elsif cd < 0.0
|
65
|
+
0.0
|
66
|
+
else
|
67
|
+
cd
|
68
|
+
end
|
72
69
|
regenerate(@background[0], @foreground[0])
|
73
70
|
end
|
74
71
|
|
@@ -76,7 +73,6 @@ class Color::Palette::MonoContrast
|
|
76
73
|
def initialize(background, foreground = nil)
|
77
74
|
@minimum_brightness_diff = DEFAULT_MINIMUM_BRIGHTNESS_DIFF
|
78
75
|
@minimum_color_diff = DEFAULT_MINIMUM_COLOR_DIFF
|
79
|
-
|
80
76
|
regenerate(background, foreground)
|
81
77
|
end
|
82
78
|
|
@@ -148,7 +144,7 @@ class Color::Palette::MonoContrast
|
|
148
144
|
|
149
145
|
# Returns the absolute difference between the brightness levels of two
|
150
146
|
# colours. This will be a decimal value between 0 and 1. W3C accessibility
|
151
|
-
# guidelines for colour contrast[http://www.w3.org/TR/AERT#color-contrast]
|
147
|
+
# guidelines for {colour contrast}[http://www.w3.org/TR/AERT#color-contrast]
|
152
148
|
# suggest that this value be at least approximately 0.49 (125 / 255.0) for
|
153
149
|
# proper contrast.
|
154
150
|
def brightness_diff(c1, c2)
|
@@ -156,8 +152,8 @@ class Color::Palette::MonoContrast
|
|
156
152
|
end
|
157
153
|
|
158
154
|
# Returns the contrast between to colours, a decimal value between 0 and
|
159
|
-
# 3. W3C accessibility guidelines for colour
|
160
|
-
# contrast[http://www.w3.org/TR/AERT#color-contrast] suggest that this
|
155
|
+
# 3. W3C accessibility guidelines for {colour
|
156
|
+
# contrast}[http://www.w3.org/TR/AERT#color-contrast] suggest that this
|
161
157
|
# value be at least approximately 1.96 (500 / 255.0) for proper contrast.
|
162
158
|
def color_diff(c1, c2)
|
163
159
|
r = (c1.r - c2.r).abs
|
data/lib/color/rgb.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# An RGB colour object.
|
2
2
|
class Color::RGB
|
3
|
+
include Color
|
4
|
+
|
3
5
|
# The format of a DeviceRGB colour for PDF. In color-tools 2.0 this will
|
4
6
|
# be removed from this package and added back as a modification by the
|
5
7
|
# PDF::Writer package.
|
@@ -9,20 +11,22 @@ class Color::RGB
|
|
9
11
|
# Creates an RGB colour object from percentages 0..100.
|
10
12
|
#
|
11
13
|
# Color::RGB.from_percentage(10, 20 30)
|
12
|
-
def from_percentage(r = 0, g = 0, b = 0)
|
13
|
-
|
14
|
+
def from_percentage(r = 0, g = 0, b = 0, &block)
|
15
|
+
new(r, g, b, 100.0, &block)
|
14
16
|
end
|
15
17
|
|
16
18
|
# Creates an RGB colour object from fractional values 0..1.
|
17
19
|
#
|
18
20
|
# Color::RGB.from_fraction(.3, .2, .1)
|
19
|
-
def from_fraction(r = 0.0, g = 0.0, b = 0.0)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def from_fraction(r = 0.0, g = 0.0, b = 0.0, &block)
|
22
|
+
new(r, g, b, 1.0, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates an RGB colour object from a grayscale fractional value 0..1.
|
26
|
+
def from_grayscale_fraction(l = 0.0, &block)
|
27
|
+
new(l, l, l, 1.0, &block)
|
25
28
|
end
|
29
|
+
alias_method :from_greyscale_fraction, :from_grayscale_fraction
|
26
30
|
|
27
31
|
# Creates an RGB colour object from an HTML colour descriptor (e.g.,
|
28
32
|
# <tt>"fed"</tt> or <tt>"#cabbed;"</tt>.
|
@@ -31,44 +35,86 @@ class Color::RGB
|
|
31
35
|
# Color::RGB.from_html("#fed")
|
32
36
|
# Color::RGB.from_html("#cabbed")
|
33
37
|
# Color::RGB.from_html("cabbed")
|
34
|
-
def from_html(html_colour)
|
35
|
-
|
36
|
-
|
38
|
+
def from_html(html_colour, &block)
|
39
|
+
# When we can move to 1.9+ only, this will be \h
|
40
|
+
h = html_colour.scan(/[0-9a-f]/i)
|
41
|
+
case h.size
|
37
42
|
when 3
|
38
|
-
|
43
|
+
new(*h.map { |v| (v * 2).to_i(16) }, &block)
|
39
44
|
when 6
|
40
|
-
|
45
|
+
new(*h.each_slice(2).map { |v| v.join.to_i(16) }, &block)
|
41
46
|
else
|
42
|
-
raise ArgumentError
|
47
|
+
raise ArgumentError, "Not a supported HTML colour type."
|
43
48
|
end
|
49
|
+
end
|
44
50
|
|
45
|
-
|
51
|
+
# Find or create a colour by an HTML hex code. This differs from the
|
52
|
+
# #from_html method in that if the colour code matches a named colour,
|
53
|
+
# the existing colour will be returned.
|
54
|
+
#
|
55
|
+
# Color::RGB.by_hex('ff0000').name # => 'red'
|
56
|
+
# Color::RGB.by_hex('ff0001').name # => nil
|
57
|
+
#
|
58
|
+
# If a block is provided, the value that is returned by the block will
|
59
|
+
# be returned instead of the exception caused by an error in providing a
|
60
|
+
# correct hex format.
|
61
|
+
def by_hex(hex, &block)
|
62
|
+
__by_hex.fetch(html_hexify(hex)) { from_html(hex) }
|
63
|
+
rescue
|
64
|
+
if block
|
65
|
+
block.call
|
66
|
+
else
|
67
|
+
raise
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return a colour as identified by the colour name.
|
72
|
+
def by_name(name, &block)
|
73
|
+
__by_name.fetch(name.to_s.downcase, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return a colour as identified by the colour name, or by hex.
|
77
|
+
def by_css(name_or_hex, &block)
|
78
|
+
by_name(name_or_hex) { by_hex(name_or_hex, &block) }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Extract named or hex colours from the provided text.
|
82
|
+
def extract_colors(text, mode = :both)
|
83
|
+
text = text.downcase
|
84
|
+
regex = case mode
|
85
|
+
when :name
|
86
|
+
Regexp.union(__by_name.keys)
|
87
|
+
when :hex
|
88
|
+
Regexp.union(__by_hex.keys)
|
89
|
+
when :both
|
90
|
+
Regexp.union(__by_hex.keys + __by_name.keys)
|
91
|
+
end
|
92
|
+
|
93
|
+
text.scan(regex).map { |match|
|
94
|
+
case mode
|
95
|
+
when :name
|
96
|
+
by_name(match)
|
97
|
+
when :hex
|
98
|
+
by_hex(match)
|
99
|
+
when :both
|
100
|
+
by_css(match)
|
101
|
+
end
|
102
|
+
}
|
46
103
|
end
|
47
104
|
end
|
48
105
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
# colour's default #to_rgb conversion. If there is no #to_rgb conversion,
|
53
|
-
# this will raise an exception. This will report that two RGB colours are
|
54
|
-
# equivalent if all component values are within COLOR_TOLERANCE of each
|
55
|
-
# other.
|
56
|
-
def ==(other)
|
57
|
-
other = other.to_rgb
|
58
|
-
other.kind_of?(Color::RGB) and
|
59
|
-
((@r - other.r).abs <= Color::COLOR_TOLERANCE) and
|
60
|
-
((@g - other.g).abs <= Color::COLOR_TOLERANCE) and
|
61
|
-
((@b - other.b).abs <= Color::COLOR_TOLERANCE)
|
106
|
+
# Coerces the other Color object into RGB.
|
107
|
+
def coerce(other)
|
108
|
+
other.to_rgb
|
62
109
|
end
|
63
110
|
|
64
111
|
# Creates an RGB colour object from the standard range 0..255.
|
65
112
|
#
|
66
113
|
# Color::RGB.new(32, 64, 128)
|
67
114
|
# Color::RGB.new(0x20, 0x40, 0x80)
|
68
|
-
def initialize(r = 0, g = 0, b = 0)
|
69
|
-
@r = r /
|
70
|
-
|
71
|
-
@b = b / 255.0
|
115
|
+
def initialize(r = 0, g = 0, b = 0, radix = 255.0, &block) # :yields self:
|
116
|
+
@r, @g, @b = [ r, g, b ].map { |v| Color.normalize(v / radix) }
|
117
|
+
block.call(self) if block
|
72
118
|
end
|
73
119
|
|
74
120
|
# Present the colour as a DeviceRGB fill colour string for PDF. This will
|
@@ -83,8 +129,8 @@ class Color::RGB
|
|
83
129
|
PDF_FORMAT_STR % [ @r, @g, @b, "RG" ]
|
84
130
|
end
|
85
131
|
|
86
|
-
# Present the colour as an
|
87
|
-
def
|
132
|
+
# Present the colour as an RGB hex triplet.
|
133
|
+
def hex
|
88
134
|
r = (@r * 255).round
|
89
135
|
r = 255 if r > 255
|
90
136
|
|
@@ -94,7 +140,12 @@ class Color::RGB
|
|
94
140
|
b = (@b * 255).round
|
95
141
|
b = 255 if b > 255
|
96
142
|
|
97
|
-
"
|
143
|
+
"%02x%02x%02x" % [ r, g, b ]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Present the colour as an HTML/CSS colour string.
|
147
|
+
def html
|
148
|
+
"##{hex}"
|
98
149
|
end
|
99
150
|
|
100
151
|
# Present the colour as an RGB HTML/CSS colour string (e.g., "rgb(0%, 50%,
|
@@ -267,11 +318,7 @@ class Color::RGB
|
|
267
318
|
# Color::RGB::DarkBlue.adjust_brightness(10)
|
268
319
|
# Color::RGB::DarkBlue.adjust_brightness(-10)
|
269
320
|
def adjust_brightness(percent)
|
270
|
-
percent
|
271
|
-
percent += 1.0
|
272
|
-
percent = [ percent, 2.0 ].min
|
273
|
-
percent = [ 0.0, percent ].max
|
274
|
-
|
321
|
+
percent = normalize_percent(percent)
|
275
322
|
hsl = to_hsl
|
276
323
|
hsl.l *= percent
|
277
324
|
hsl.to_rgb
|
@@ -284,11 +331,7 @@ class Color::RGB
|
|
284
331
|
# Color::RGB::DarkBlue.adjust_saturation(10)
|
285
332
|
# Color::RGB::DarkBlue.adjust_saturation(-10)
|
286
333
|
def adjust_saturation(percent)
|
287
|
-
percent
|
288
|
-
percent += 1.0
|
289
|
-
percent = [ percent, 2.0 ].min
|
290
|
-
percent = [ 0.0, percent ].max
|
291
|
-
|
334
|
+
percent = normalize_percent(percent)
|
292
335
|
hsl = to_hsl
|
293
336
|
hsl.s *= percent
|
294
337
|
hsl.to_rgb
|
@@ -301,11 +344,7 @@ class Color::RGB
|
|
301
344
|
# Color::RGB::DarkBlue.adjust_hue(10)
|
302
345
|
# Color::RGB::DarkBlue.adjust_hue(-10)
|
303
346
|
def adjust_hue(percent)
|
304
|
-
percent
|
305
|
-
percent += 1.0
|
306
|
-
percent = [ percent, 2.0 ].min
|
307
|
-
percent = [ 0.0, percent ].max
|
308
|
-
|
347
|
+
percent = normalize_percent(percent)
|
309
348
|
hsl = to_hsl
|
310
349
|
hsl.h *= percent
|
311
350
|
hsl.to_rgb
|
@@ -399,14 +438,7 @@ class Color::RGB
|
|
399
438
|
# The addition is done using the RGB Accessor methods to ensure a valid
|
400
439
|
# colour in the result.
|
401
440
|
def +(other)
|
402
|
-
other
|
403
|
-
rgb = self.dup
|
404
|
-
|
405
|
-
rgb.r += other.r
|
406
|
-
rgb.g += other.g
|
407
|
-
rgb.b += other.b
|
408
|
-
|
409
|
-
rgb
|
441
|
+
self.class.from_fraction(r + other.r, g + other.g, b + other.b)
|
410
442
|
end
|
411
443
|
|
412
444
|
# Subtracts another colour to the current colour. The other colour will be
|
@@ -416,14 +448,7 @@ class Color::RGB
|
|
416
448
|
# The subtraction is done using the RGB Accessor methods to ensure a valid
|
417
449
|
# colour in the result.
|
418
450
|
def -(other)
|
419
|
-
|
420
|
-
rgb = self.dup
|
421
|
-
|
422
|
-
rgb.r -= other.r
|
423
|
-
rgb.g -= other.g
|
424
|
-
rgb.b -= other.b
|
425
|
-
|
426
|
-
rgb
|
451
|
+
self + (-other)
|
427
452
|
end
|
428
453
|
|
429
454
|
# Retrieve the maxmum RGB value from the current colour as a GrayScale
|
@@ -436,6 +461,66 @@ class Color::RGB
|
|
436
461
|
def inspect
|
437
462
|
"RGB [#{html}]"
|
438
463
|
end
|
464
|
+
|
465
|
+
def to_a
|
466
|
+
[ r, g, b ]
|
467
|
+
end
|
468
|
+
|
469
|
+
# Numerically negate the color. This results in a color that is only
|
470
|
+
# usable for subtraction.
|
471
|
+
def -@
|
472
|
+
rgb = self.dup
|
473
|
+
rgb.instance_variable_set(:@r, -rgb.r)
|
474
|
+
rgb.instance_variable_set(:@g, -rgb.g)
|
475
|
+
rgb.instance_variable_set(:@b, -rgb.b)
|
476
|
+
rgb
|
477
|
+
end
|
478
|
+
|
479
|
+
private
|
480
|
+
def normalize_percent(percent)
|
481
|
+
percent /= 100.0
|
482
|
+
percent += 1.0
|
483
|
+
percent = [ percent, 2.0 ].min
|
484
|
+
percent = [ 0.0, percent ].max
|
485
|
+
percent
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
class << Color::RGB
|
490
|
+
private
|
491
|
+
def __named_color(mod, rgb, *names)
|
492
|
+
if names.any? { |n| mod.const_defined? n }
|
493
|
+
raise ArgumentError, "#{names.join(', ')} already defined in #{mod}"
|
494
|
+
end
|
495
|
+
|
496
|
+
names.each { |n| mod.const_set(n, rgb) }
|
497
|
+
|
498
|
+
rgb.names = names
|
499
|
+
rgb.names.each { |n| __by_name[n] = rgb }
|
500
|
+
__by_hex[rgb.hex] = rgb
|
501
|
+
rgb.freeze
|
502
|
+
end
|
503
|
+
|
504
|
+
def __by_hex
|
505
|
+
@__by_hex ||= {}
|
506
|
+
end
|
507
|
+
|
508
|
+
def __by_name
|
509
|
+
@__by_name ||= {}
|
510
|
+
end
|
511
|
+
|
512
|
+
def html_hexify(hex)
|
513
|
+
# When we can move to 1.9+ only, this will be \h
|
514
|
+
h = hex.to_s.downcase.scan(/[0-9a-f]/)
|
515
|
+
case h.size
|
516
|
+
when 3
|
517
|
+
h.map { |v| (v * 2) }.join
|
518
|
+
when 6
|
519
|
+
h.join
|
520
|
+
else
|
521
|
+
raise ArgumentError, "Not a supported HTML colour type."
|
522
|
+
end
|
523
|
+
end
|
439
524
|
end
|
440
525
|
|
441
|
-
require 'color/rgb
|
526
|
+
require 'color/rgb/colors'
|