color 2.0.1 → 2.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de0f595f559249d4eed1b1a71514d9b31caf458544b10d791b954b68d42ae169
4
- data.tar.gz: 1d6680d9805b0556a7b865b5bf9bced7b089ed72c8889a7187ac30a082826efb
3
+ metadata.gz: 2d4c9c7de02c29ef865bd03c3e91309c2880726e8919e2a0c8916b9e935fe1c6
4
+ data.tar.gz: acfd302f192306092af688898d9186d7460c59646dce0df698cf32e29fb6212c
5
5
  SHA512:
6
- metadata.gz: 6bb3c94216b72f936f2252de8b698ff28e8fbc240bee72fa5cd4d016f3a911de3755b8ba892cbe857a8f3f4a9aeaef4bd79132e14c4230eff2f047690a8940d2
7
- data.tar.gz: 275c0cdaed2c2474eedc59a915910dca1573a95f35f7cf41a5758f60284dbb3402454b56c17fb0d833de0b57047dddde25df09f2211585fe5957be68ee1791f2
6
+ metadata.gz: d7bf088dca85deeb798f0c68542fc1f1fac08e7f629e41b38c91d95537d963ce603768a290af699c5e3b71bcb3183f165c438bf8769d12fd6c84a6e0167b127b
7
+ data.tar.gz: 185273ad56535d55992ab8f78aec618f9b65ce6407ee68c2c5c56475660c87d536941bea440852393030369906bc55ed5daaf1da97340f8879c743b79b583edf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.1.1 / 2025-08-08
4
+
5
+ Color 2.1.1 fixes a bug where `Color::RGB::Black` and `Color::RGB::White` are no
6
+ longer defined automatically because they are part of `color/rgb/colors`.
7
+ Internally, this defines `Color::RGB::Black000` and `Color::RGB::WhiteFFF`.
8
+
9
+ ## 2.1.0 / 2025-07-20
10
+
11
+ Color 2.1.0 fixes a computation bug where CIE XYZ values were improperly clamped
12
+ and adds more Color::XYZ white points for standard illuminants.
13
+
14
+ - Fixes a bug where standard illuminant white points were improperly clamped and
15
+ was seen in `Color::RGB#to_lab` since CIELAB conversions must go through the
16
+ XYZ color model. Even though we were using the D65 white point, the Z value
17
+ was being clamped to 1.0 instead of the correct value of ≅1.08. Reported by
18
+ @r-plus in [#45][issue-45] and fixed in [#45][pr-46].
19
+
20
+ The resulting Color::LAB values are not _exactly_ the same values under Color
21
+ 1.8, but they are within fractional differences deemed acceptable.
22
+
23
+ - Added more white points for standard illuminants in the Color::XYZ::WP2
24
+ constant. The values here were derived from the
25
+ [White points of standard illuminants][wp-std-illuminant] using the `xyY` to
26
+ `XYZ` conversion formula where `X = (x * Y) / y` and
27
+ `Z = ((1 - x - y) * Y) / y`. Only the values for CIE 1931 2° were computed.
28
+ The values for Color::XYZ::D50 and Color::XYZ::D65 were replaced with these
29
+ computed values.
30
+
3
31
  ## 2.0.1 / 2025-07-05
4
32
 
5
33
  Color 2.0.1 is a minor documentation update.
@@ -309,6 +337,9 @@ ownership to contribute it to this project under the licence terms.
309
337
  [css-device-cmyk]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/device-cmyk
310
338
  [issue-10]: https://github.com/halostatue/color/issues/10
311
339
  [issue-30]: https://github.com/halostatue/color/issues/30
340
+ [issue-45]: https://github.com/halostatue/color/issues/45
312
341
  [pr-11]: https://github.com/halostatue/color/pull/11
313
- [pr-8]: https://github.com/halostatue/color/pulls/8
314
342
  [pr-36]: https://github.com/halostatue/color/pull/36
343
+ [pr-46]: https://github.com/halostatue/pull/46
344
+ [pr-8]: https://github.com/halostatue/color/pulls/8
345
+ [wp-std-illuminant]: https://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants
data/CONTRIBUTORS.md CHANGED
@@ -7,5 +7,6 @@
7
7
  - Aaron Hill (CIE94 color matching)
8
8
  - Luke Bennellick
9
9
  - @stiff (CIELAB color support)
10
+ - @r-plus
10
11
  - Matthew Draper
11
12
  - Charles Nutter
data/README.md CHANGED
@@ -22,9 +22,11 @@ reliably converted to relative color spaces (like RGB) without color profiles.
22
22
  When necessary for conversions, Color provides D65 and D50 reference white
23
23
  values in Color::XYZ.
24
24
 
25
- Color 2.0 is a major release, dropping support for all versions of Ruby prior to
26
- 3.2 as well as removing or renaming a number of features. The main breaking
27
- changes are:
25
+ Color 2.1 fixes a Color::XYZ bug where the values were improperly clamped and
26
+ adds more Color::XYZ white points for standard illuminants. It builds on the
27
+ Color 2.0 major release, dropping support for all versions of Ruby prior to 3.2
28
+ as well as removing or renaming a number of features. The main breaking changes
29
+ are:
28
30
 
29
31
  - Color classes are immutable Data objects; they are no longer mutable.
30
32
  - RGB named colors are no longer loaded on gem startup, but must be required
data/lib/color/hsl.rb CHANGED
@@ -93,8 +93,8 @@ class Color::HSL
93
93
  # fvd and van Dam, originally found at [1] (implemented similarly at [2]). This
94
94
  # simplifies the calculations with the following assumptions:
95
95
  #
96
- # - Luminance values <= 0 always translate to Color::RGB::Black.
97
- # - Luminance values >= 1 always translate to Color::RGB::White.
96
+ # - Luminance values <= 0 always translate to a black Color::RGB value.
97
+ # - Luminance values >= 1 always translate to a white Color::RGB value.
98
98
  # - Saturation values <= 0 always translate to a shade of gray using luminance as
99
99
  # a percentage of gray.
100
100
  #
@@ -102,9 +102,9 @@ class Color::HSL
102
102
  # [2] http://support.microsoft.com/kb/29240
103
103
  def to_rgb(...)
104
104
  if near_zero_or_less?(l)
105
- Color::RGB::Black
105
+ Color::RGB::Black000
106
106
  elsif near_one_or_more?(l)
107
- Color::RGB::White
107
+ Color::RGB::WhiteFFF
108
108
  elsif near_zero?(s)
109
109
  Color::RGB.from_fraction(l, l, l)
110
110
  else
data/lib/color/rgb.rb CHANGED
@@ -63,6 +63,9 @@ class Color::RGB
63
63
  super(r: normalize(r), g: normalize(g), b: normalize(b), names: names)
64
64
  end
65
65
 
66
+ Black000 = new(r: 0x00, g: 0x00, b: 0x00) # :nodoc:
67
+ WhiteFFF = new(r: 0xff, g: 0xff, b: 0xff) # :nodoc:
68
+
66
69
  ##
67
70
  # :attr_reader: name
68
71
  # The primary name for this \RGB color.
@@ -263,18 +266,18 @@ class Color::RGB
263
266
  def delta_e2000(other) = to_lab.delta_e2000(coerce(other).to_lab)
264
267
 
265
268
  ##
266
- # Mix the \RGB hue with White so that the \RGB hue is the specified percentage of the
269
+ # Mix the \RGB hue with white so that the \RGB hue is the specified percentage of the
267
270
  # resulting color.
268
271
  #
269
272
  # Strictly speaking, this isn't a `lighten_by` operation, but it mostly works.
270
- def lighten_by(percent) = mix_with(Color::RGB::White, percent)
273
+ def lighten_by(percent) = mix_with(Color::RGB::WhiteFFF, percent)
271
274
 
272
275
  ##
273
- # Mix the \RGB hue with Black so that the \RGB hue is the specified percentage of the
276
+ # Mix the \RGB hue with black so that the \RGB hue is the specified percentage of the
274
277
  # resulting color.
275
278
  #
276
279
  # Strictly speaking, this isn't a `darken_by` operation, but it mostly works.
277
- def darken_by(percent) = mix_with(Color::RGB::Black, percent)
280
+ def darken_by(percent) = mix_with(Color::RGB::Black000, percent)
278
281
 
279
282
  ##
280
283
  # Mix the mask color with the current color at the stated opacity percentage (0..100).
data/lib/color/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Color
4
- VERSION = "2.0.1" # :nodoc:
4
+ VERSION = "2.1.1" # :nodoc:
5
5
  end
data/lib/color/xyz.rb CHANGED
@@ -83,15 +83,18 @@ class Color::XYZ
83
83
  # 0 and 1 and `x` and `z` must be fractional valiues greater than or equal to 0.
84
84
  #
85
85
  # ```ruby
86
- # Color::XYZ.from_fraction(0.95047, 1.0, 1.0.883)
86
+ # Color::XYZ.from_fraction(0.95047, 1.0, 1.0883)
87
87
  # Color::XYZ.new(0.95047, 1.0, 1.08883)
88
88
  # Color::XYZ[x: 0.95047, y: 1.0, z: 1.08883]
89
89
  # ```
90
90
  def initialize(x:, y:, z:)
91
+ # The X and Z values in the XYZ color model are technically unbounded. With Y scaled
92
+ # to 1.0, we will clamp X to 0.0..2.2 and Z to 0.0..2.8.
93
+
91
94
  super(
92
- x: normalize(x, 0.0..1.0),
95
+ x: normalize(x, 0.0..2.2),
93
96
  y: normalize(y),
94
- z: normalize(z, 0.0..1.0)
97
+ z: normalize(z, 0.0..2.8)
95
98
  )
96
99
  end
97
100
 
@@ -105,11 +108,70 @@ class Color::XYZ
105
108
  K = 24389r/27r # :nodoc:
106
109
  EK = E * K # :nodoc:
107
110
 
108
- # The D65 standard illuminant white point
109
- D65 = new(0.95047, 1.0, 1.08883)
111
+ ##
112
+ # White points for standard illuminants at 2° (CIE 1931).
113
+ WP2 = {
114
+ A: new(1.09849161234507, 1.0, 0.355798257454902),
115
+ B: new(0.9909274480248, 1.0, 0.853132732288615),
116
+ C: new(0.980705971659919, 1.0, 1.18224949392713),
117
+ D50: new(0.964211994421199, 1.0, 0.825188284518828),
118
+ D55: new(0.956797052643698, 1.0, 0.921480586017327),
119
+ D65: new(0.950430051970945, 1.0, 1.08880649180926),
120
+ D75: new(0.949722089884072, 1.0, 1.22639352072415),
121
+ D93: new(0.953014035205816, 1.0, 1.41274275520851),
122
+ E: new(1, 1.0, 1.0000300003),
123
+ F1: new(0.92833634773327, 1.0, 1.03664719660806),
124
+ F2: new(0.991446614618029, 1.0, 0.673159423379253),
125
+ F3: new(1.03753487192493, 1.0, 0.49860512300279),
126
+ F4: new(1.0914726375561, 1.0, 0.388132609288601),
127
+ F5: new(0.908719701138108, 1.0, 0.987228866815325),
128
+ F6: new(0.973091283635895, 1.0, 0.601905497618128),
129
+ F7: new(0.950171560440895, 1.0, 1.08629642000425),
130
+ F8: new(0.96412543554007, 1.0, 0.823331010452962),
131
+ F9: new(1.00364797081623, 1.0, 0.678683511708377),
132
+ F10: new(0.961735119213027, 1.0, 0.817123325737787),
133
+ F11: new(1.00898894280487, 1.0, 0.642616604353936),
134
+ F12: new(1.08046289656537, 1.0, 0.392275166291635),
135
+ "FL3.0": new(1.09273493677163, 1.0, 0.3868088271758),
136
+ "FL3.1": new(1.01981788966256, 1.0, 0.658275307980718),
137
+ "FL3.2": new(0.916836289619075, 1.0, 0.990985751671998),
138
+ "FL3.3": new(1.09547365817462, 1.0, 0.377937175364828),
139
+ "FL3.4": new(1.02096949891068, 1.0, 0.702342047930283),
140
+ "FL3.5": new(0.968888888888889, 1.0, 0.808888888888889),
141
+ "FL3.6": new(1.08380716934487, 1.0, 0.388380716934487),
142
+ "FL3.7": new(0.996868475991649, 1.0, 0.612734864300626),
143
+ "FL3.8": new(0.974380395433027, 1.0, 0.810359231411863),
144
+ "FL3.9": new(0.970505617977528, 1.0, 0.838483146067416),
145
+ "FL3.10": new(0.944962143273151, 1.0, 0.967093768200349),
146
+ "FL3.11": new(1.08422095615556, 1.0, 0.392865989596235),
147
+ "FL3.12": new(1.02846401718582, 1.0, 0.656820622986037),
148
+ "FL3.13": new(0.955112219451372, 1.0, 0.815738431698531),
149
+ "FL3.14": new(0.951034063260341, 1.0, 1.09032846715328),
150
+ HP1: new(1.28433734939759, 1.0, 0.125301204819277),
151
+ HP2: new(1.14911014911015, 1.0, 0.255892255892256),
152
+ HP3: new(1.05570552147239, 1.0, 0.398282208588957),
153
+ HP4: new(1.00395048722676, 1.0, 0.62970766394522),
154
+ HP5: new(1.01696741179639, 1.0, 0.676272555884729),
155
+ "LED-B1": new(1.11819519372241, 1.0, 0.3339872486513),
156
+ "LED-B2": new(1.08599202392822, 1.0, 0.406530408773679),
157
+ "LED-B3": new(1.0088638195004, 1.0, 0.677142089712597),
158
+ "LED-B4": new(0.977155910908053, 1.0, 0.87835522558538),
159
+ "LED-B5": new(0.963535228677379, 1.0, 1.12669962917182),
160
+ "LED-BH1": new(1.10034431874078, 1.0, 0.359075258239056),
161
+ "LED-RGB1": new(1.08216575635241, 1.0, 0.292567086202802),
162
+ "LED-V1": new(1.12462908011869, 1.0, 0.348170128585559),
163
+ "LED-V2": new(1.00158940397351, 1.0, 0.647417218543046),
164
+ ID50: new(0.952803997779012, 1.0, 0.823431426985008),
165
+ ID65: new(0.939522225582099, 1.0, 1.08436649531297)
166
+ }.freeze
110
167
 
111
- # The D50 standard illuminant white point
112
- D50 = new(0.96421, 1.0, 0.82521)
168
+ ##
169
+ # The D50 standard illuminant white point at 2° (CIE 1931).
170
+ D50 = WP2[:D50]
171
+
172
+ ##
173
+ # The D65 standard illuminant white point at 2° (CIE 1931).
174
+ D65 = WP2[:D65]
113
175
 
114
176
  ##
115
177
  # Coerces the other Color object into \XYZ.
data/lib/color.rb CHANGED
@@ -17,8 +17,10 @@
17
17
  # spaces (like RGB) without color profiles. When necessary for conversions, \Color
18
18
  # provides \D65 and \D50 reference white values in Color::XYZ.
19
19
  #
20
- # Color 2.0 is a major release, dropping support for all versions of Ruby prior to 3.2 as
21
- # well as removing or renaming a number of features. The main breaking changes are:
20
+ # Color 2.1 fixes a Color::XYZ bug where the values were improperly clamped and adds more
21
+ # Color::XYZ white points for standard illuminants. It builds on the Color 2.0 major
22
+ # release, dropping support for all versions of Ruby prior to 3.2 as well as removing or
23
+ # renaming a number of features. The main breaking changes are:
22
24
  #
23
25
  # - Color classes are immutable Data objects; they are no longer mutable.
24
26
  # - RGB named colors are no longer loaded on gem startup, but must be required explicitly
data/test/test_rgb.rb CHANGED
@@ -317,6 +317,15 @@ module TestColor
317
317
  end
318
318
  end
319
319
 
320
+ def test_fix_45_invalid_rgb_to_lab
321
+ assert_equal(
322
+ Color::CIELAB[56.2562, 88.1033, -18.8203],
323
+ Color::RGB.by_hex("#ff00aa").to_lab
324
+ )
325
+
326
+ assert_equal("#ff00aa", Color::RGB.by_hex("#ff00aa").to_lab.to_rgb.html)
327
+ end
328
+
320
329
  # # An RGB color round-tripped through CIELAB should still have more or less the same
321
330
  # # RGB values, but this doesn't really work because the color math here is slightly
322
331
  # # wrong.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: color
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Ziegler
@@ -223,9 +223,11 @@ description: |-
223
223
  When necessary for conversions, Color provides D65 and D50 reference white
224
224
  values in Color::XYZ.
225
225
 
226
- Color 2.0 is a major release, dropping support for all versions of Ruby prior to
227
- 3.2 as well as removing or renaming a number of features. The main breaking
228
- changes are:
226
+ Color 2.1 fixes a Color::XYZ bug where the values were improperly clamped and
227
+ adds more Color::XYZ white points for standard illuminants. It builds on the
228
+ Color 2.0 major release, dropping support for all versions of Ruby prior to 3.2
229
+ as well as removing or renaming a number of features. The main breaking changes
230
+ are:
229
231
 
230
232
  - Color classes are immutable Data objects; they are no longer mutable.
231
233
  - RGB named colors are no longer loaded on gem startup, but must be required