color 1.7.1 → 2.0.0.pre.0
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.
- checksums.yaml +5 -13
- data/CHANGELOG.md +298 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +70 -0
- data/CONTRIBUTORS.md +10 -0
- data/LICENCE.md +27 -0
- data/Manifest.txt +11 -21
- data/README.md +54 -0
- data/Rakefile +74 -53
- data/SECURITY.md +34 -0
- data/lib/color/cielab.rb +348 -0
- data/lib/color/cmyk.rb +279 -213
- data/lib/color/grayscale.rb +128 -160
- data/lib/color/hsl.rb +205 -173
- data/lib/color/rgb/colors.rb +177 -163
- data/lib/color/rgb.rb +534 -537
- data/lib/color/version.rb +5 -0
- data/lib/color/xyz.rb +214 -0
- data/lib/color/yiq.rb +91 -46
- data/lib/color.rb +208 -141
- data/test/fixtures/cielab.json +444 -0
- data/test/minitest_helper.rb +20 -4
- data/test/test_cmyk.rb +49 -71
- data/test/test_color.rb +58 -106
- data/test/test_grayscale.rb +35 -56
- data/test/test_hsl.rb +72 -76
- data/test/test_rgb.rb +195 -267
- data/test/test_yiq.rb +12 -30
- metadata +165 -150
- checksums.yaml.gz.sig +0 -0
- data/.autotest +0 -5
- data/.gemtest +0 -0
- data/.hoerc +0 -2
- data/.minitest.rb +0 -2
- data/.travis.yml +0 -35
- data/Contributing.rdoc +0 -60
- data/Gemfile +0 -9
- data/History.rdoc +0 -172
- data/Licence.rdoc +0 -27
- data/README.rdoc +0 -50
- data/lib/color/css.rb +0 -7
- data/lib/color/palette/adobecolor.rb +0 -260
- data/lib/color/palette/gimp.rb +0 -104
- data/lib/color/palette/monocontrast.rb +0 -164
- data/lib/color/palette.rb +0 -4
- data/lib/color/rgb/contrast.rb +0 -57
- data/lib/color/rgb/metallic.rb +0 -28
- data/test/test_adobecolor.rb +0 -405
- data/test/test_css.rb +0 -19
- data/test/test_gimp.rb +0 -87
- data/test/test_monocontrast.rb +0 -130
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
data/lib/color/xyz.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# A \Color object for the CIE 1931 \XYZ color space derived from the original CIE \RGB
|
5
|
+
# color space as linear transformation functions x̅(λ), y̅(λ), and z̅(λ) that describe the
|
6
|
+
# device-independent \CIE standard observer. It underpins most other CIE color systems
|
7
|
+
# (such as \CIELAB), but is directly used mostly for color instrument readings and color
|
8
|
+
# space transformations particularly in color profiles.
|
9
|
+
#
|
10
|
+
# The \XYZ color space ranges describe the mixture of wavelengths of light required to
|
11
|
+
# stimulate cone cells in the human eye, as well as the luminance (brightness) required.
|
12
|
+
# The `Y` component describes the luminance while the `X` and `Z` components describe two
|
13
|
+
# axes of chromaticity. Definitionally, the minimum value for any \XYZ color component is
|
14
|
+
# 0.
|
15
|
+
#
|
16
|
+
# As \XYZ describes imaginary colors, the color gamut is usually expressed in relation to
|
17
|
+
# a reference white of an illuminant (frequently often D65 or D50) and expressed as the
|
18
|
+
# `xyY` color space, computed as:
|
19
|
+
#
|
20
|
+
# ```
|
21
|
+
# x = X / (X + Y + Z)
|
22
|
+
# y = Y / (X + Y + Z)
|
23
|
+
# Y = Y
|
24
|
+
# ```
|
25
|
+
#
|
26
|
+
# The range of `Y` values is conventionally clamped to 0..100, whereas the `X` and `Z`
|
27
|
+
# values must be no lower than 0 and on the same scale.
|
28
|
+
#
|
29
|
+
# For more details, see [CIE XYZ color space][ciexyz].
|
30
|
+
#
|
31
|
+
# [ciexyz]: https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_XYZ_color_space
|
32
|
+
#
|
33
|
+
# \XYZ colors are immutable Data class instances. Array deconstruction is `[x * 100,
|
34
|
+
# y * 100, z * 100]` and hash deconstruction is `{x:, y:, z:}` (see #x, #y, #z).
|
35
|
+
class Color::XYZ
|
36
|
+
include Color
|
37
|
+
|
38
|
+
##
|
39
|
+
# :attr_reader: x
|
40
|
+
# The X attribute of this \XYZ color object expressed as a value scaled to #y.
|
41
|
+
|
42
|
+
##
|
43
|
+
# :attr_reader: y
|
44
|
+
# The Y attribute of this \XYZ color object expressed as a value 0..1.
|
45
|
+
|
46
|
+
##
|
47
|
+
# :attr_reader: z
|
48
|
+
# The Z attribute of this \XYZ color object expressed as a value scaled to #y.
|
49
|
+
|
50
|
+
##
|
51
|
+
# Creates a \XYZ color representation from native values. `y` must be between 0 and 100
|
52
|
+
# and `x` and `z` values must be scaled to `y`.
|
53
|
+
#
|
54
|
+
# ```ruby
|
55
|
+
# Color::XYZ.from_values(95.047, 100.00, 108.883)
|
56
|
+
# Color::XYZ.from_values(x: 95.047, y: 100.00, z: 108.883)
|
57
|
+
# ```
|
58
|
+
#
|
59
|
+
# call-seq:
|
60
|
+
# Color::XYZ.from_values(x, y, z)
|
61
|
+
# Color::XYZ.from_values(x:, y:, z:)
|
62
|
+
def self.from_values(*args, **kwargs)
|
63
|
+
x, y, z =
|
64
|
+
case [args, kwargs]
|
65
|
+
in [[_, _, _], {}]
|
66
|
+
args
|
67
|
+
in [[], {x:, y:, z:}]
|
68
|
+
[x, y, z]
|
69
|
+
else
|
70
|
+
new(*args, **kwargs)
|
71
|
+
end
|
72
|
+
|
73
|
+
new(x: x / 100.0, y: y / 100.0, z: z / 100.0)
|
74
|
+
end
|
75
|
+
|
76
|
+
class << self
|
77
|
+
alias_method :from_fraction, :new
|
78
|
+
alias_method :from_internal, :new # :nodoc:
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Creates a \XYZ color representation from native values. The `y` value must be between
|
83
|
+
# 0 and 1 and `x` and `z` must be fractional valiues greater than or equal to 0.
|
84
|
+
#
|
85
|
+
# ```ruby
|
86
|
+
# Color::XYZ.from_fraction(0.95047, 1.0, 1.0.883)
|
87
|
+
# Color::XYZ.new(0.95047, 1.0, 1.08883)
|
88
|
+
# Color::XYZ[x: 0.95047, y: 1.0, z: 1.08883]
|
89
|
+
# ```
|
90
|
+
def initialize(x:, y:, z:)
|
91
|
+
super(
|
92
|
+
x: normalize(x, 0.0..1.0),
|
93
|
+
y: normalize(y),
|
94
|
+
z: normalize(z, 0.0..1.0)
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
# :stopdoc:
|
99
|
+
# NOTE: This should be using Rational instead of floating point values,
|
100
|
+
# otherwise there will be discontinuities.
|
101
|
+
# http://www.brucelindbloom.com/LContinuity.html
|
102
|
+
# :startdoc:
|
103
|
+
|
104
|
+
E = 216r/24389r # :nodoc:
|
105
|
+
K = 24389r/27r # :nodoc:
|
106
|
+
EK = E * K # :nodoc:
|
107
|
+
|
108
|
+
# The D65 standard illuminant white point
|
109
|
+
D65 = new(0.95047, 1.0, 1.08883)
|
110
|
+
|
111
|
+
# The D50 standard illuminant white point
|
112
|
+
D50 = new(0.96421, 1.0, 0.82521)
|
113
|
+
|
114
|
+
##
|
115
|
+
# Coerces the other Color object into \XYZ.
|
116
|
+
def coerce(other) = other.to_xyz
|
117
|
+
|
118
|
+
##
|
119
|
+
def to_xyz(...) = self
|
120
|
+
|
121
|
+
##
|
122
|
+
# Converts \XYZ to Color::CMYK via Color::RGB.
|
123
|
+
#
|
124
|
+
# See #to_rgb and Color::RGB#to_cmyk.
|
125
|
+
def to_cmyk(...) = to_rgb(...).to_cmyk(...)
|
126
|
+
|
127
|
+
##
|
128
|
+
# Converts \XYZ to Color::Grayscale using the #y value
|
129
|
+
def to_grayscale(...) = Color::Grayscale.from_fraction(y)
|
130
|
+
|
131
|
+
##
|
132
|
+
# Converts \XYZ to Color::HSL via Color::RGB.
|
133
|
+
#
|
134
|
+
# See #to_rgb and Color::RGB#to_hsl.
|
135
|
+
def to_hsl(...) = to_rgb(...).to_hsl(...)
|
136
|
+
|
137
|
+
##
|
138
|
+
# Converts \XYZ to Color::YIQ via Color::RGB.
|
139
|
+
#
|
140
|
+
# See #to_rgb and Color::RGB#to_yiq.
|
141
|
+
def to_yiq(...) = to_rgb(...).to_yiq(...)
|
142
|
+
|
143
|
+
##
|
144
|
+
# Converts \XYZ to Color::CIELAB.
|
145
|
+
#
|
146
|
+
# :call-seq:
|
147
|
+
# to_lab(white: Color::XYZ::D65)
|
148
|
+
def to_lab(*args, **kwargs)
|
149
|
+
ref = kwargs[:white] || args.first || Color::XYZ::D65
|
150
|
+
# Calculate the ratio of the XYZ values to the reference white.
|
151
|
+
# http://www.brucelindbloom.com/index.html?Equations.html
|
152
|
+
rel = scale(1.0 / ref.x, 1.0 / ref.y, 1.0 / ref.z)
|
153
|
+
|
154
|
+
# And now transform
|
155
|
+
# http://en.wikipedia.org/wiki/Lab_color_space#Forward_transformation
|
156
|
+
# There is a brief explanation there as far as the nature of the calculations,
|
157
|
+
# as well as a much nicer looking modeling of the algebra.
|
158
|
+
f = rel.map { |t|
|
159
|
+
if t > E
|
160
|
+
t**(1.0 / 3)
|
161
|
+
else # t <= E
|
162
|
+
((K * t) + 16) / 116.0
|
163
|
+
# The 4/29 here is for when t = 0 (black). 4/29 * 116 = 16, and 16 -
|
164
|
+
# 16 = 0, which is the correct value for L* with black.
|
165
|
+
# ((1.0/3)*((29.0/6)**2) * t) + (4.0/29)
|
166
|
+
end
|
167
|
+
}
|
168
|
+
Color::CIELAB.from_values(
|
169
|
+
(116 * f.y) - 16,
|
170
|
+
500 * (f.x - f.y),
|
171
|
+
200 * (f.y - f.z)
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Converts \XYZ to Color::RGB.
|
177
|
+
#
|
178
|
+
# This always assumes an sRGB target color space and a D65 white point.
|
179
|
+
def to_rgb(...)
|
180
|
+
# sRGB companding from linear values
|
181
|
+
linear = [
|
182
|
+
x * 3.2406255 + y * -1.5372080 + z * -0.4986286,
|
183
|
+
x * -0.9689307 + y * 1.8757561 + z * 0.0415175,
|
184
|
+
x * 0.0557101 + y * -0.2040211 + z * 1.0569959
|
185
|
+
].map {
|
186
|
+
if _1.abs <= 0.0031308
|
187
|
+
_1 * 12.92
|
188
|
+
else
|
189
|
+
1.055 * (_1**(1 / 2.4)) - 0.055
|
190
|
+
end
|
191
|
+
}
|
192
|
+
|
193
|
+
Color::RGB.from_fraction(*linear)
|
194
|
+
end
|
195
|
+
|
196
|
+
def deconstruct = [x * 100.0, y * 100.0, z * 100.0] # :nodoc:
|
197
|
+
alias_method :to_a, :deconstruct # :nodoc:
|
198
|
+
|
199
|
+
def to_internal = [x, y, z] # :nodoc:
|
200
|
+
|
201
|
+
def inspect = "XYZ [#{x} #{y} #{z}]" # :nodoc:
|
202
|
+
|
203
|
+
def pretty_print(q) # :nodoc:
|
204
|
+
q.text "XYZ"
|
205
|
+
q.breakable
|
206
|
+
q.group 2, "[", "]" do
|
207
|
+
q.text "%.4f" % x
|
208
|
+
q.fill_breakable
|
209
|
+
q.text "%.4f" % y
|
210
|
+
q.fill_breakable
|
211
|
+
q.text "%.4f" % z
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
data/lib/color/yiq.rb
CHANGED
@@ -1,62 +1,107 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A \Color object representing YIQ (NTSC) color encoding, where Y is the luma
|
4
|
+
# (brightness) value, and I (orange-blue) and Q (purple-green) are chrominance.
|
5
|
+
#
|
6
|
+
# All values are clamped between 0 and 1 inclusive.
|
7
|
+
#
|
8
|
+
# More more details, see [YIQ][wikiyiq].
|
9
|
+
#
|
10
|
+
# [wikiyiq]: https://en.wikipedia.org/wiki/YIQ
|
11
|
+
#
|
12
|
+
# \YIQ colors are immutable Data class instances. Array deconstruction is `[y, i, q]` and
|
13
|
+
# hash deconstruction is `{y:, i:, q:}` (see #y, #i, #q).
|
14
|
+
#
|
15
|
+
# \YIQ is only partially implemented: other \Color objects can only be converted _to_
|
16
|
+
# \YIQ, but it has few conversion functions for converting _from_ \YIQ.
|
2
17
|
class Color::YIQ
|
3
18
|
include Color
|
4
19
|
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
20
|
+
##
|
21
|
+
# :attr_reader: y
|
22
|
+
# The `y` (luma) attribute of this \YIQ color expressed as a value 0..1.
|
23
|
+
|
24
|
+
##
|
25
|
+
# :attr_reader: i
|
26
|
+
# The `i` (orange-blue chrominance) attribute of this \YIQ color expressed as a value
|
27
|
+
# 0..1.
|
28
|
+
|
29
|
+
##
|
30
|
+
# :attr_reader: q
|
31
|
+
# The `q` (purple-green chrominance) attribute of this \YIQ color expressed as a value
|
32
|
+
# 0..1.
|
11
33
|
|
12
|
-
|
34
|
+
##
|
35
|
+
# Creates a YIQ color object from percentage values 0 .. 1.
|
13
36
|
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
37
|
+
# ```ruby
|
38
|
+
# Color::YIQ.from_percentage(30, 20, 10) # => YIQ [30% 20% 10%]
|
39
|
+
# Color::YIQ.from_percentage(y: 30, i: 20, q: 10) # => YIQ [30% 20% 10%]
|
40
|
+
# Color::YIQ.from_values(30, 20, 10) # => YIQ [30% 20% 10%]
|
41
|
+
# Color::YIQ.from_values(y: 30, i: 20, q: 10) # => YIQ [30% 20% 10%]
|
42
|
+
# ```
|
43
|
+
def self.from_percentage(*args, **kwargs)
|
44
|
+
y, i, q =
|
45
|
+
case [args, kwargs]
|
46
|
+
in [[_, _, _], {}]
|
47
|
+
args
|
48
|
+
in [[], {y:, i:, q:}]
|
49
|
+
[y, i, q]
|
50
|
+
else
|
51
|
+
new(*args, **kwargs)
|
52
|
+
end
|
19
53
|
|
20
|
-
|
21
|
-
other.to_yiq
|
54
|
+
new(y: y / 100.0, i: i / 100.0, q: q / 100.0)
|
22
55
|
end
|
23
56
|
|
24
|
-
|
25
|
-
|
57
|
+
class << self
|
58
|
+
alias_method :from_values, :from_percentage
|
59
|
+
alias_method :from_fraction, :new # :nodoc:
|
60
|
+
alias_method :from_internal, :new
|
26
61
|
end
|
27
62
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
63
|
+
##
|
64
|
+
# Creates a YIQ color object from fractional values 0 .. 1.
|
65
|
+
#
|
66
|
+
# ```ruby
|
67
|
+
# Color::YIQ.from_fraction(0.3, 0.2, 0.1) # => YIQ [30% 20% 10%]
|
68
|
+
# Color::YIQ.new(0.3, 0.2, 0.1) # => YIQ [30% 20% 10%]
|
69
|
+
# Color::YIQ[y: 0.3, i: 0.2, q: 0.1] # => YIQ [30% 20% 10%]
|
70
|
+
# ```
|
71
|
+
def initialize(y:, i:, q:) # :nodoc:
|
72
|
+
super(y: normalize(y), i: normalize(i), q: normalize(q))
|
33
73
|
end
|
34
|
-
alias to_greyscale to_grayscale
|
35
74
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
def y=(yy)
|
40
|
-
@y = Color.normalize(yy)
|
41
|
-
end
|
42
|
-
def i
|
43
|
-
@i
|
44
|
-
end
|
45
|
-
def i=(ii)
|
46
|
-
@i = Color.normalize(ii)
|
47
|
-
end
|
48
|
-
def q
|
49
|
-
@q
|
50
|
-
end
|
51
|
-
def q=(qq)
|
52
|
-
@q = Color.normalize(qq)
|
53
|
-
end
|
75
|
+
##
|
76
|
+
# Coerces the other Color object into \YIQ.
|
77
|
+
def coerce(other) = other.to_yiq
|
54
78
|
|
55
|
-
|
56
|
-
|
57
|
-
end
|
79
|
+
##
|
80
|
+
def to_yiq = self
|
58
81
|
|
59
|
-
|
60
|
-
|
82
|
+
##
|
83
|
+
# Convert \YIQ to Color::Grayscale using the luma (#y) value.
|
84
|
+
def to_grayscale = Color::Grayscale.from_fraction(y)
|
85
|
+
|
86
|
+
##
|
87
|
+
alias_method :brightness, :y
|
88
|
+
|
89
|
+
def inspect = "YIQ [%.2f%% %.2f%% %.2f%%]" % [y * 100, i * 100, q * 100] # :nodoc:
|
90
|
+
|
91
|
+
def pretty_print(q) # :nodoc:
|
92
|
+
q.text "YIQ"
|
93
|
+
q.breakable
|
94
|
+
q.group 2, "[", "]" do
|
95
|
+
q.text "%.2f%%" % y
|
96
|
+
q.fill_breakable
|
97
|
+
q.text "%.2f%%" % i
|
98
|
+
q.fill_breakable
|
99
|
+
q.text "%.2f%%" % q
|
100
|
+
end
|
61
101
|
end
|
102
|
+
|
103
|
+
alias_method :to_a, :deconstruct # :nodoc:
|
104
|
+
alias_method :to_internal, :deconstruct # :nodoc:
|
105
|
+
|
106
|
+
def deconstruct_keys(_keys) = {y:, i:, q:} # :nodoc:
|
62
107
|
end
|