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.
Files changed (53) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +298 -0
  3. data/CODE_OF_CONDUCT.md +128 -0
  4. data/CONTRIBUTING.md +70 -0
  5. data/CONTRIBUTORS.md +10 -0
  6. data/LICENCE.md +27 -0
  7. data/Manifest.txt +11 -21
  8. data/README.md +54 -0
  9. data/Rakefile +74 -53
  10. data/SECURITY.md +34 -0
  11. data/lib/color/cielab.rb +348 -0
  12. data/lib/color/cmyk.rb +279 -213
  13. data/lib/color/grayscale.rb +128 -160
  14. data/lib/color/hsl.rb +205 -173
  15. data/lib/color/rgb/colors.rb +177 -163
  16. data/lib/color/rgb.rb +534 -537
  17. data/lib/color/version.rb +5 -0
  18. data/lib/color/xyz.rb +214 -0
  19. data/lib/color/yiq.rb +91 -46
  20. data/lib/color.rb +208 -141
  21. data/test/fixtures/cielab.json +444 -0
  22. data/test/minitest_helper.rb +20 -4
  23. data/test/test_cmyk.rb +49 -71
  24. data/test/test_color.rb +58 -106
  25. data/test/test_grayscale.rb +35 -56
  26. data/test/test_hsl.rb +72 -76
  27. data/test/test_rgb.rb +195 -267
  28. data/test/test_yiq.rb +12 -30
  29. metadata +165 -150
  30. checksums.yaml.gz.sig +0 -0
  31. data/.autotest +0 -5
  32. data/.gemtest +0 -0
  33. data/.hoerc +0 -2
  34. data/.minitest.rb +0 -2
  35. data/.travis.yml +0 -35
  36. data/Contributing.rdoc +0 -60
  37. data/Gemfile +0 -9
  38. data/History.rdoc +0 -172
  39. data/Licence.rdoc +0 -27
  40. data/README.rdoc +0 -50
  41. data/lib/color/css.rb +0 -7
  42. data/lib/color/palette/adobecolor.rb +0 -260
  43. data/lib/color/palette/gimp.rb +0 -104
  44. data/lib/color/palette/monocontrast.rb +0 -164
  45. data/lib/color/palette.rb +0 -4
  46. data/lib/color/rgb/contrast.rb +0 -57
  47. data/lib/color/rgb/metallic.rb +0 -28
  48. data/test/test_adobecolor.rb +0 -405
  49. data/test/test_css.rb +0 -19
  50. data/test/test_gimp.rb +0 -87
  51. data/test/test_monocontrast.rb +0 -130
  52. data.tar.gz.sig +0 -0
  53. metadata.gz.sig +0 -0
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Color
4
+ VERSION = "2.0.0.pre.0" # :nodoc:
5
+ end
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
- # A colour object representing YIQ (NTSC) colour encoding.
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
- # Creates a YIQ colour object from fractional values 0 .. 1.
6
- #
7
- # Color::YIQ.new(0.3, 0.2, 0.1)
8
- def self.from_fraction(y = 0, i = 0, q = 0, &block)
9
- new(y, i, q, 1.0, &block)
10
- end
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
- # Creates a YIQ colour object from percentages 0 .. 100.
34
+ ##
35
+ # Creates a YIQ color object from percentage values 0 .. 1.
13
36
  #
14
- # Color::YIQ.new(10, 20, 30)
15
- def initialize(y = 0, i = 0, q = 0, radix = 100.0, &block) # :yields self:
16
- @y, @i, @q = [ y, i, q ].map { |v| Color.normalize(v / radix) }
17
- block.call if block
18
- end
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
- def coerce(other)
21
- other.to_yiq
54
+ new(y: y / 100.0, i: i / 100.0, q: q / 100.0)
22
55
  end
23
56
 
24
- def to_yiq
25
- self
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
- def brightness
29
- @y
30
- end
31
- def to_grayscale
32
- Color::GrayScale.new(@y)
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
- def y
37
- @y
38
- end
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
- def inspect
56
- "YIQ [%.2f%%, %.2f%%, %.2f%%]" % [ @y * 100, @i * 100, @q * 100 ]
57
- end
79
+ ##
80
+ def to_yiq = self
58
81
 
59
- def to_a
60
- [ y, i, q ]
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