color_converters 0.1.1 → 0.1.3

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: c97c6fdaf99305ee413b3648f6c16b8fe9a83124696af3e3e161fae19d71fe19
4
- data.tar.gz: b0c6e84ebde615a1b7d044aaa85556e16e1ca9bfa0181232c238b74a1d80d25f
3
+ metadata.gz: 5cff79bc695fab4e8041206a798421948d03eb04933b77274a802f90a0010999
4
+ data.tar.gz: 1edc8b8811b32ca6ac4d7b9f8347151ab772fa58d52d90ba4cd8f48ab60d889e
5
5
  SHA512:
6
- metadata.gz: 1c42e5f3097a878896c29b317856cfd3b9d3458b24ad66c69ac1cf550a50c7bd72e04c135157ef582d8f8ce0408500624f7776339c8949442ea41c1cfec77956
7
- data.tar.gz: 8a6310fd2eaa1b323236111ddb96732ad84fca43edfd4e39805afd614499c2afee09b481e81fdcb454187e45637c57770a3e4676c346238641aaf23e686220d4
6
+ metadata.gz: a5fe55d89679198c41e0e166dbdae882b11f9396d58710b004707358652c6187e53889d88dd21efbeeee99b7738484a7cd3d71f7cc35496445881a9558fa9543
7
+ data.tar.gz: 507da4fe02639e97285b5df4ed0e6e5e69fd78f793aa8bc772b2702d72e029f227ce50abf2987ac087fe1b050a06562262ed9660ca8395d6833e528412900852
data/README.md CHANGED
@@ -18,7 +18,9 @@ Colors can be converted between the following spaces:
18
18
  - cmyk
19
19
  - xyz
20
20
  - cielab
21
- - oklch
21
+ - cielch
22
+ - oklab (not always reliable)
23
+ - oklch (not always reliable)
22
24
  - name
23
25
 
24
26
  ## Installation
@@ -63,10 +65,16 @@ color = ColorConverters::Color.new(c: 74, m: 58, y: 22, k: 3)
63
65
  color = ColorConverters::Color.new(x: 16, y: 44, z: 32)
64
66
 
65
67
  # from cielab
66
- color = ColorConverters::Color.new(l: 16, a: 44, b: 32)
68
+ color = ColorConverters::Color.new(l: 16, a: 44, b: 32, space: :cie)
69
+
70
+ # from cielch
71
+ color = ColorConverters::Color.new(l: 16, c: 44, h: 32, space: :cie)
72
+
73
+ # from oklab
74
+ color = ColorConverters::Color.new(l: 16, a: 44, b: 32, space: :ok)
67
75
 
68
76
  # from oklch
69
- color = ColorConverters::Color.new(l: 16, c: 44, h: 32)
77
+ color = ColorConverters::Color.new(l: 16, c: 44, h: 32, space: :ok)
70
78
 
71
79
  # from textual color
72
80
  color = ColorConverters::Color.new("blue")
@@ -109,7 +117,13 @@ color.xyz
109
117
  color.cielab
110
118
  => {:l=>52.47, :a=>-4.08, :b=>-32.19}
111
119
 
112
- color.oklch
120
+ color.cielch
121
+ => {:l=>52.47, :c=>32.45, :h=>262.78}
122
+
123
+ color.oklab # not always accurate
124
+ => {:l=>52.47, :a=>-4.08, :b=>-32.19}
125
+
126
+ color.oklch # not always accurate
113
127
  => {:l=>52.47, :c=>32.45, :h=>262.78}
114
128
 
115
129
  color.hex
@@ -121,6 +135,16 @@ color.name
121
135
 
122
136
  ## Options
123
137
 
138
+ ### space
139
+
140
+ As there are certain color spaces that use the same letter keys, there needed to be a way to different between those space.
141
+ The space parameter allows that, with examples in the usage code above
142
+
143
+ ```ruby
144
+ ColorConverters::Color.new(l: 64, a: 28, b: -15, space: :cie)
145
+ ColorConverters::Color.new(l: 64, a: 28, b: -15, space: :ok)
146
+ ```
147
+
124
148
  ### limit_override
125
149
 
126
150
  By default all values are checked to be within the expected number ranges, i.e.; rgb between 0-255 each.
@@ -132,7 +156,7 @@ ColorConverters::Color.new(r: 270, g: 1300, b: 380, a: 0.5, limit_override: true
132
156
 
133
157
  ## Development
134
158
 
135
- [Converting Colors](https://convertingcolors.com/) can be usef to help verify results. Different calculators use different exponents and standards so there can be discrepency across them (like this calculator for LCH).
159
+ [Converting Colors](https://convertingcolors.com/) and [Colorffy](https://colorffy.com/) can be usef to help verify results. Different calculators use different exponents and standards so there can be discrepency across them (like this calculator for LCH).
136
160
 
137
161
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
138
162
 
@@ -1,4 +1,6 @@
1
1
  require 'active_support/core_ext/object/blank'
2
+ require 'bigdecimal'
3
+ require 'bigdecimal/util'
2
4
 
3
5
  module ColorConverters
4
6
  class BaseConverter
@@ -26,15 +28,19 @@ module ColorConverters
26
28
  def initialize(color_input, limit_override = false)
27
29
  @original_value = color_input
28
30
 
31
+ # self.clamp_input(color_input) if limit_clamp == true
32
+
29
33
  if limit_override == false && !self.validate_input(color_input)
30
34
  raise InvalidColorError # validation method is defined in each convertor
31
35
  end
32
36
 
33
- @rgba = self.input_to_rgba(color_input) # conversion method is defined in each convertor
37
+ r, g, b, a = self.input_to_rgba(color_input) # conversion method is defined in each convertor
38
+
39
+ @rgba = { r: r.to_f.round(IMPORT_DP), g: g.to_f.round(IMPORT_DP), b: b.to_f.round(IMPORT_DP), a: a.to_f.round(IMPORT_DP) }
34
40
  end
35
41
 
36
42
  def rgb
37
- { r: @rgba[:r].round(OUTPUT_DP), g: @rgba[:g].round(OUTPUT_DP), b: @rgba[:b].round(OUTPUT_DP) }
43
+ { r: @rgba[:r].to_f.round(OUTPUT_DP), g: @rgba[:g].to_f.round(OUTPUT_DP), b: @rgba[:b].to_f.round(OUTPUT_DP) }
38
44
  end
39
45
 
40
46
  def hex
@@ -44,13 +50,13 @@ module ColorConverters
44
50
  def hsl
45
51
  @r, @g, @b = self.rgb_array_frac
46
52
 
47
- { h: self.hue.round(OUTPUT_DP), s: self.hsl_saturation.round(OUTPUT_DP), l: self.hsl_lightness.round(OUTPUT_DP) }
53
+ { h: self.hue.to_f.round(OUTPUT_DP), s: self.hsl_saturation.to_f.round(OUTPUT_DP), l: self.hsl_lightness.to_f.round(OUTPUT_DP) }
48
54
  end
49
55
 
50
56
  def hsv
51
57
  @r, @g, @b = self.rgb_array
52
58
 
53
- { h: self.hue.round(OUTPUT_DP), s: self.hsv_saturation.round(OUTPUT_DP), v: self.hsv_value.round(OUTPUT_DP) }
59
+ { h: self.hue.to_f.round(OUTPUT_DP), s: self.hsv_saturation.to_f.round(OUTPUT_DP), v: self.hsv_value.to_f.round(OUTPUT_DP) }
54
60
  end
55
61
 
56
62
  def hsb
@@ -62,25 +68,37 @@ module ColorConverters
62
68
  def cmyk
63
69
  c, m, y, k = CmykConverter.rgb_to_cmyk(self.rgb_array_frac)
64
70
 
65
- { c: c.round(OUTPUT_DP), m: m.round(OUTPUT_DP), y: y.round(OUTPUT_DP), k: k.round(OUTPUT_DP) }
71
+ { c: c.to_f.round(OUTPUT_DP), m: m.to_f.round(OUTPUT_DP), y: y.to_f.round(OUTPUT_DP), k: k.to_f.round(OUTPUT_DP) }
66
72
  end
67
73
 
68
74
  def xyz
69
75
  x, y, z = XyzConverter.rgb_to_xyz(self.rgb_array_frac)
70
76
 
71
- { x: x.round(OUTPUT_DP), y: y.round(OUTPUT_DP), z: z.round(OUTPUT_DP) }
77
+ { x: x.to_f.round(OUTPUT_DP), y: y.to_f.round(OUTPUT_DP), z: z.to_f.round(OUTPUT_DP) }
72
78
  end
73
79
 
74
80
  def cielab
75
- l, a, b = CielabConverter.xyz_to_lab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
81
+ l, a, b = CielabConverter.xyz_to_cielab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
82
+
83
+ { l: l.to_f.round(OUTPUT_DP), a: a.to_f.round(OUTPUT_DP), b: b.to_f.round(OUTPUT_DP) }
84
+ end
85
+
86
+ def cielch
87
+ l, c, h = CielchConverter.cielab_to_cielch(CielabConverter.xyz_to_cielab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
88
+
89
+ { l: l.to_f.round(OUTPUT_DP), c: c.to_f.round(OUTPUT_DP), h: h.to_f.round(OUTPUT_DP) }
90
+ end
91
+
92
+ def oklab
93
+ l, a, b = OklabConverter.xyz_to_oklab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
76
94
 
77
- { l: l.round(OUTPUT_DP), a: a.round(OUTPUT_DP), b: b.round(OUTPUT_DP) }
95
+ { l: l.to_f.round(OUTPUT_DP), a: a.to_f.round(OUTPUT_DP), b: b.to_f.round(OUTPUT_DP) }
78
96
  end
79
97
 
80
98
  def oklch
81
- l, c, h = OklchConverter.lab_to_lch(CielabConverter.xyz_to_lab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
99
+ l, c, h = OklchConverter.oklab_to_oklch(OklabConverter.xyz_to_oklab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
82
100
 
83
- { l: l.round(OUTPUT_DP), c: c.round(OUTPUT_DP), h: h.round(OUTPUT_DP) }
101
+ { l: l.to_f.round(OUTPUT_DP), c: c.to_f.round(OUTPUT_DP), h: h.to_f.round(OUTPUT_DP) }
84
102
  end
85
103
 
86
104
  def alpha
@@ -88,7 +106,7 @@ module ColorConverters
88
106
  end
89
107
 
90
108
  def name
91
- NameConverter.rgb_to_name(self.rgb_array_frac)
109
+ NameConverter.rgb_to_name(self.rgb_array)
92
110
  end
93
111
 
94
112
  protected
@@ -1,7 +1,7 @@
1
1
  module ColorConverters
2
2
  class Color
3
3
  extend Forwardable
4
- def_delegators :@converter, :rgb, :hex, :hsl, :hsv, :hsb, :cmyk, :xyz, :cielab, :oklch, :name, :alpha
4
+ def_delegators :@converter, :rgb, :hex, :hsl, :hsv, :hsb, :cmyk, :xyz, :cielab, :cielch, :oklab, :oklch, :name, :alpha
5
5
 
6
6
  def initialize(color)
7
7
  @converter = BaseConverter.factory(color)
@@ -3,7 +3,7 @@ module ColorConverters
3
3
  def self.matches?(color_input)
4
4
  return false unless color_input.is_a?(Hash)
5
5
 
6
- color_input.keys - [:l, :a, :b] == []
6
+ color_input.keys - [:l, :a, :b, :space] == [] && color_input[:space].to_s == 'cie'
7
7
  end
8
8
 
9
9
  def self.bounds
@@ -18,69 +18,73 @@ module ColorConverters
18
18
  end
19
19
 
20
20
  def input_to_rgba(color_input)
21
- xyz_hash = CielabConverter.lab_to_xyz(color_input)
22
- XyzConverter.new(xyz_hash, limit_override: true).rgba
21
+ x, y, z = CielabConverter.cielab_to_xyz(color_input)
22
+ r, g, b = XyzConverter.xyz_to_rgb({ x: x, y: y, z: z })
23
+
24
+ [r, g, b, 1.0]
23
25
  end
24
26
 
25
- def self.lab_to_xyz(color_input)
26
- l = color_input[:l].to_f
27
- a = color_input[:a].to_f
28
- b = color_input[:b].to_f
27
+ def self.cielab_to_xyz(color_input)
28
+ l = color_input[:l].to_d
29
+ a = color_input[:a].to_d
30
+ b = color_input[:b].to_d
29
31
 
30
- yy = (l + 16.0) / 116.0
31
- xx = (a / 500.0) + yy
32
- zz = yy - (b / 200.0)
32
+ yy = (l + 16.0.to_d) / 116.0.to_d
33
+ xx = (a / 500.0.to_d) + yy
34
+ zz = yy - (b / 200.0.to_d)
33
35
 
34
- ratio = 6.0 / 29.0
36
+ e = 216.0.to_d / 24_389.0.to_d
35
37
 
36
38
  x, y, z = [xx, yy, zz].map do
37
- if _1 <= ratio
38
- (3.0 * (6.0 / 29.0) * (6.0 / 29.0) * (_1 - (4.0 / 29.0)))
39
+ if _1**3.to_d <= e
40
+ (3.0.to_d * (6.0.to_d / 29.0.to_d) * (6.0.to_d / 29.0.to_d) * (_1 - (4.0.to_d / 29.0.to_d)))
39
41
  else
40
- _1**3
42
+ _1**3.to_d
41
43
  end
42
44
  end
43
45
 
44
- x *= 95.047
45
- y *= 100.0
46
- z *= 108.883
46
+ x *= 95.047.to_d
47
+ y *= 100.0.to_d
48
+ z *= 108.883.to_d
47
49
 
48
- { x: x, y: y, z: z }
50
+ [x, y, z]
49
51
  end
50
52
 
51
- def self.xyz_to_lab(xyz_array)
52
- x, y, z = xyz_array
53
+ def self.xyz_to_cielab(xyz_array)
54
+ x, y, z = xyz_array.map(&:to_d)
55
+
56
+ # https://www.w3.org/TR/css-color-4/#color-conversion-code
57
+ # # The D50 & D65 standard illuminant white point
58
+ # wp_rel = [0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585]
59
+ wp_rel = [0.3127.to_d / 0.3290.to_d, 1.0.to_d, (1.0.to_d - 0.3127.to_d - 0.3290.to_d) / 0.3290.to_d].map { _1 * 100.0.to_d }
53
60
 
54
- # The D65 standard illuminant white point
55
- xr = 95.047
56
- yr = 100.0
57
- zr = 108.883
61
+ xr, yr, zr = wp_rel
58
62
 
59
- # Calculate the ratio of the XYZ values to the reference white.
60
- # http://www.brucelindbloom.com/index.html?Equations.html
63
+ # # Calculate the ratio of the XYZ values to the reference white.
64
+ # # http://www.brucelindbloom.com/index.html?Equations.html
61
65
  rel = [x / xr, y / yr, z / zr]
62
66
 
63
- e = 216.0 / 24_389.0
64
- k = 841.0 / 108.0
67
+ e = 216.0.to_d / 24_389.0.to_d
68
+ k = 841.0.to_d / 108.0.to_d
65
69
 
66
70
  # And now transform
67
- # http://en.wikipedia.org/wiki/Lab_color_space#Forward_transformation
71
+ # http:#en.wikipedia.org/wiki/Lab_color_space#Forward_transformation
68
72
  # There is a brief explanation there as far as the nature of the calculations,
69
73
  # as well as a much nicer looking modeling of the algebra.
70
74
  xx, yy, zz = rel.map do
71
75
  if _1 > e
72
- _1**(1.0 / 3.0)
76
+ _1**(1.0.to_d / 3.0.to_d)
73
77
  else
74
- (k * _1) + (4.0 / 29.0)
78
+ (k * _1) + (4.0.to_d / 29.0.to_d)
75
79
  # The 4/29 here is for when t = 0 (black). 4/29 * 116 = 16, and 16 -
76
80
  # 16 = 0, which is the correct value for L* with black.
77
81
  # ((1.0/3)*((29.0/6)**2) * t) + (4.0/29)
78
82
  end
79
83
  end
80
84
 
81
- l = ((116.0 * yy) - 16.0)
82
- a = (500.0 * (xx - yy))
83
- b = (200.0 * (yy - zz))
85
+ l = ((116.0.to_d * yy) - 16.0.to_d)
86
+ a = (500.0.to_d * (xx - yy))
87
+ b = (200.0.to_d * (yy - zz))
84
88
 
85
89
  [l, a, b]
86
90
  end
@@ -0,0 +1,58 @@
1
+ module ColorConverters
2
+ class CielchConverter < BaseConverter
3
+ def self.matches?(color_input)
4
+ return false unless color_input.is_a?(Hash)
5
+
6
+ color_input.keys - [:l, :c, :h, :space] == [] && color_input[:space].to_s == 'cie'
7
+ end
8
+
9
+ def self.bounds
10
+ { l: [0.0, 100.0], c: [0.0, 150.0], h: [0.0, 360.0] }
11
+ end
12
+
13
+ private
14
+
15
+ def validate_input(color_input)
16
+ bounds = CielchConverter.bounds
17
+ color_input[:l].to_f.between?(*bounds[:l]) && color_input[:c].to_f.between?(*bounds[:c]) && color_input[:h].to_f.between?(*bounds[:h])
18
+ end
19
+
20
+ def input_to_rgba(color_input)
21
+ l, a, b = CielchConverter.cielch_to_cielab(color_input)
22
+ x, y, z = CielabConverter.cielab_to_xyz({ l: l, a: a, b: b })
23
+ r, g, b = XyzConverter.xyz_to_rgb({ x: x, y: y, z: z })
24
+
25
+ [r, g, b, 1.0]
26
+ end
27
+
28
+ def self.cielch_to_cielab(color_input)
29
+ l = color_input[:l].to_d
30
+ c = color_input[:c].to_d
31
+ h = color_input[:h].to_d
32
+
33
+ h_rad = h * (Math::PI.to_d / 180.0.to_d)
34
+
35
+ a = c * Math.cos(h_rad).to_d
36
+ b = c * Math.sin(h_rad).to_d
37
+
38
+ [l, a, b]
39
+ end
40
+
41
+ def self.cielab_to_cielch(lab_array)
42
+ l, aa, bb = lab_array.map(&:to_d)
43
+
44
+ e = 0.0015.to_d; # if chroma is smaller than this, set hue to 0 [https://www.w3.org/TR/css-color-4/#color-conversion-code]
45
+
46
+ c = ((aa**2.to_d) + (bb**2.to_d))**0.5.to_d
47
+
48
+ h_rad = Math.atan2(bb, aa).to_d
49
+ h = h_rad * (180.0.to_d / Math::PI.to_d)
50
+
51
+ h %= 360
52
+
53
+ h = 0 if c < e
54
+
55
+ [l, c, h]
56
+ end
57
+ end
58
+ end
@@ -28,11 +28,11 @@ module ColorConverters
28
28
  y /= 100.0
29
29
  k /= 100.0
30
30
 
31
- r = (255 * (1.0 - [1.0, c * (1.0 - k) + k].min)).round(IMPORT_DP)
32
- g = (255 * (1.0 - [1.0, m * (1.0 - k) + k].min)).round(IMPORT_DP)
33
- b = (255 * (1.0 - [1.0, y * (1.0 - k) + k].min)).round(IMPORT_DP)
31
+ r = (255 * (1.0 - [1.0, c * (1.0 - k) + k].min))
32
+ g = (255 * (1.0 - [1.0, m * (1.0 - k) + k].min))
33
+ b = (255 * (1.0 - [1.0, y * (1.0 - k) + k].min))
34
34
 
35
- { r: r, g: g, b: b, a: 1.0 }
35
+ [r, g, b, 1.0]
36
36
  end
37
37
 
38
38
  def self.rgb_to_cmyk(rgb_array_frac)
@@ -8,7 +8,7 @@ module ColorConverters
8
8
 
9
9
  private
10
10
 
11
- def validate_input(color_input)
11
+ def validate_input(_color_input)
12
12
  # TODO
13
13
  true
14
14
  end
@@ -21,7 +21,7 @@ module ColorConverters
21
21
  b = color_input[4, 2].hex
22
22
  a = color_input.length == 8 ? hex[6, 2].hex : 1.0
23
23
 
24
- { r: r, g: g, b: b, a: a }
24
+ [r, g, b, a]
25
25
  end
26
26
 
27
27
  def normalize_hex(hex_input)
@@ -50,15 +50,16 @@ module ColorConverters
50
50
  t1
51
51
  end
52
52
 
53
- rgb[i] = (val * 255).round(IMPORT_DP)
53
+ rgb[i] = (val * 255)
54
54
  end
55
55
 
56
- { r: rgb[0], g: rgb[1], b: rgb[2], a: a }
56
+ [rgb[0], rgb[1], rgb[2], a]
57
57
  end
58
58
 
59
59
  def greyscale(luminosity, alpha)
60
60
  rgb_equal_value = (luminosity * 255).round(IMPORT_DP)
61
- { r: rgb_equal_value, g: rgb_equal_value, b: rgb_equal_value, a: alpha }
61
+
62
+ [rgb_equal_value, rgb_equal_value, rgb_equal_value, alpha]
62
63
  end
63
64
  end
64
65
  end
@@ -8,7 +8,7 @@ module ColorConverters
8
8
 
9
9
  private
10
10
 
11
- def validate_input(color_input)
11
+ def validate_input(_color_input)
12
12
  true
13
13
  end
14
14
 
@@ -19,7 +19,9 @@ module ColorConverters
19
19
  h, s, l, a = matches[1].split(',').map(&:strip)
20
20
  raise InvalidColorError unless h.present? && s.present? && l.present?
21
21
 
22
- HslConverter.new(h: h, s: s, l: l, a: a).rgba
22
+ rgba = HslConverter.new(h: h, s: s, l: l, a: a).rgba
23
+
24
+ [rgba[:r], rgba[:g], rgba[:b], rgba[:a]]
23
25
  end
24
26
  end
25
27
  end
@@ -41,17 +41,17 @@ module ColorConverters
41
41
 
42
42
  case i % 6
43
43
  when 0
44
- { r: v, g: t, b: p, a: 1.0 }
44
+ [v, t, p, 1.0]
45
45
  when 1
46
- { r: q, g: v, b: p, a: 1.0 }
46
+ [q, v, p, 1.0]
47
47
  when 2
48
- { r: p, g: v, b: t, a: 1.0 }
48
+ [p, v, t, 1.0]
49
49
  when 3
50
- { r: p, g: q, b: v, a: 1.0 }
50
+ [p, q, v, 1.0]
51
51
  when 4
52
- { r: t, g: p, b: v, a: 1.0 }
52
+ [t, p, v, 1.0]
53
53
  when 5
54
- { r: v, g: p, b: q, a: 1.0 }
54
+ [v, p, q, 1.0]
55
55
  end
56
56
  end
57
57
  end
@@ -6,16 +6,16 @@ module ColorConverters
6
6
  self.color_names.include?(color_input.downcase.to_sym)
7
7
  end
8
8
 
9
- def self.rgb_to_name(rgb_array_frac)
10
- r, g, b = rgb_array_frac
9
+ def self.rgb_to_name(rgb_array)
10
+ r, g, b = rgb_array
11
11
 
12
- name = self.color_names.find { |_k, v| v == [r, g, b] }
12
+ name = self.color_names.find { |_k, v| v == [r.round, g.round, b.round] }
13
13
  name.present? ? name[0].to_s : nil
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def validate_input(color_input) #
18
+ def validate_input(_color_input)
19
19
  # TODO: validate against list of keys?
20
20
  true
21
21
  end
@@ -25,9 +25,11 @@ module ColorConverters
25
25
  raise InvalidColorError unless found_colour.present?
26
26
 
27
27
  r, g, b = found_colour
28
- { r: r, g: g, b: b, a: 1.0 }
28
+
29
+ [r, g, b, 1.0]
29
30
  end
30
31
 
32
+ # TODO: use color_namer_ruby gem instead, but that gem also uses this gem
31
33
  def self.color_names
32
34
  {
33
35
  aliceblue: [240, 248, 255],
@@ -0,0 +1,98 @@
1
+ module ColorConverters
2
+ class OklabConverter < BaseConverter
3
+ def self.matches?(color_input)
4
+ return false unless color_input.is_a?(Hash)
5
+
6
+ color_input.keys - [:l, :a, :b, :space] == [] && color_input[:space].to_s == 'ok'
7
+ end
8
+
9
+ def self.bounds
10
+ { l: [0.0, 100.0], a: [-0.5, 0.5], b: [-0.5, 0.5] }
11
+ end
12
+
13
+ private
14
+
15
+ def validate_input(color_input)
16
+ bounds = OklabConverter.bounds
17
+ color_input[:l].to_f.between?(*bounds[:l]) && color_input[:a].to_f.between?(*bounds[:a]) && color_input[:b].to_f.between?(*bounds[:b])
18
+ end
19
+
20
+ def input_to_rgba(color_input)
21
+ x, y, z = OklabConverter.oklab_to_xyz(color_input)
22
+ r, g, b = XyzConverter.xyz_to_rgb({ x: x, y: y, z: z })
23
+
24
+ [r, g, b, 1.0]
25
+ end
26
+
27
+ def self.oklab_to_xyz(color_input)
28
+ l = color_input[:l].to_d
29
+ a = color_input[:a].to_d
30
+ b = color_input[:b].to_d
31
+
32
+ # Now, scale l to a decimal
33
+ l /= 100.0.to_d
34
+
35
+ # Convert Oklab (L*a*b*) to LMS'
36
+ lab_to_lms_matrix = ::Matrix[
37
+ [1.0000000000000000.to_d, 0.3963377773761749.to_d, 0.2158037573099136.to_d],
38
+ [1.0000000000000000.to_d, -0.1055613458156586.to_d, -0.0638541728258133.to_d],
39
+ [1.0000000000000000.to_d, -0.0894841775298119.to_d, -1.2914855480194092.to_d]
40
+ ]
41
+
42
+ l_lms, m_lms, s_lms = (lab_to_lms_matrix * ::Matrix.column_vector([l, a, b])).to_a.flatten
43
+
44
+ l_lms **= 3.to_d
45
+ m_lms **= 3.to_d
46
+ s_lms **= 3.to_d
47
+
48
+ lms_to_xyz_matrix = ::Matrix[
49
+ [1.2268798758459243.to_d, -0.5578149944602171.to_d, 0.2813910456659647.to_d],
50
+ [-0.0405757452148008.to_d, 1.1122868032803170.to_d, -0.0717110580655164.to_d],
51
+ [-0.0763729366746601.to_d, -0.4214933324022432.to_d, 1.5869240198367816.to_d]
52
+ ]
53
+
54
+ x, y, z = (lms_to_xyz_matrix * ::Matrix.column_vector([l_lms, m_lms, s_lms])).to_a.flatten
55
+
56
+ x *= 100.0.to_d
57
+ y *= 100.0.to_d
58
+ z *= 100.0.to_d
59
+
60
+ [x, y, z]
61
+ end
62
+
63
+ def self.xyz_to_oklab(xyz_array)
64
+ x, y, z = xyz_array.map(&:to_d)
65
+
66
+ # The transformation matrix expects normalised X, Y, Z values.
67
+ x /= 100.0.to_d
68
+ y /= 100.0.to_d
69
+ z /= 100.0.to_d
70
+
71
+ # Given XYZ relative to D65, convert to OKLab
72
+ xyz_to_lms_matrix = ::Matrix[
73
+ [0.8190224379967030.to_d, 0.3619062600528904.to_d, -0.1288737815209879.to_d],
74
+ [0.0329836539323885.to_d, 0.9292868615863434.to_d, 0.0361446663506424.to_d],
75
+ [0.0481771893596242.to_d, 0.2642395317527308.to_d, 0.6335478284694309.to_d]
76
+ ]
77
+
78
+ l_lms, m_lms, s_lms = (xyz_to_lms_matrix * ::Matrix.column_vector([x, y, z])).to_a.flatten
79
+
80
+ l_lms **= (1.0.to_d / 3.0.to_d)
81
+ m_lms **= (1.0.to_d / 3.0.to_d)
82
+ s_lms **= (1.0.to_d / 3.0.to_d)
83
+
84
+ lms_to_lab_matrix = ::Matrix[
85
+ [0.2104542683093140.to_d, 0.7936177747023054.to_d, -0.0040720430116193.to_d],
86
+ [1.9779985324311684.to_d, -2.4285922420485799.to_d, 0.4505937096174110.to_d],
87
+ [0.0259040424655478.to_d, 0.7827717124575296.to_d, -0.8086757549230774.to_d]
88
+ ]
89
+
90
+ l_lab, a_lab, b_lab = (lms_to_lab_matrix * ::Matrix.column_vector([l_lms, m_lms, s_lms])).to_a.flatten
91
+
92
+ # Now, scale l to a percentage
93
+ l_lab *= 100.0.to_d
94
+
95
+ [l_lab, a_lab, b_lab]
96
+ end
97
+ end
98
+ end
@@ -3,11 +3,11 @@ module ColorConverters
3
3
  def self.matches?(color_input)
4
4
  return false unless color_input.is_a?(Hash)
5
5
 
6
- color_input.keys - [:l, :c, :h] == []
6
+ color_input.keys - [:l, :c, :h, :space] == [] && color_input[:space].to_s == 'ok'
7
7
  end
8
8
 
9
9
  def self.bounds
10
- { l: [0.0, 100.0], c: [0.0, 100.0], h: [0.0, 360.0] }
10
+ { l: [0.0, 100.0], c: [0.0, 500.0], h: [0.0, 360.0] }
11
11
  end
12
12
 
13
13
  private
@@ -18,34 +18,40 @@ module ColorConverters
18
18
  end
19
19
 
20
20
  def input_to_rgba(color_input)
21
- lab_hash = OklchConverter.lch_to_lab(color_input)
22
- xyz_hash = CielabConverter.lab_to_xyz(lab_hash)
23
- XyzConverter.new(xyz_hash, limit_override: true).rgba
21
+ l, a, b = OklchConverter.oklch_to_oklab(color_input)
22
+ x, y, z = OklabConverter.oklab_to_xyz({ l: l, a: a, b: b })
23
+ r, g, b = XyzConverter.xyz_to_rgb({ x: x, y: y, z: z })
24
+
25
+ [r, g, b, 1.0]
24
26
  end
25
27
 
26
- def self.lch_to_lab(color_input)
27
- l = color_input[:l].to_f
28
- c = color_input[:c].to_f
29
- h = color_input[:h].to_f
28
+ def self.oklch_to_oklab(color_input)
29
+ l = color_input[:l].to_d
30
+ c = color_input[:c].to_d
31
+ h = color_input[:h].to_d
30
32
 
31
- h_rad = h * (Math::PI / 180.0)
33
+ h_rad = h * (Math::PI.to_d / 180.0.to_d)
32
34
 
33
- a = c * Math.cos(h_rad)
34
- b = c * Math.sin(h_rad)
35
+ a = c * Math.cos(h_rad).to_d
36
+ b = c * Math.sin(h_rad).to_d
35
37
 
36
- { l: l, a: a, b: b }
38
+ [l, a, b]
37
39
  end
38
40
 
39
- def self.lab_to_lch(lab_array)
40
- l, aa, bb = lab_array
41
+ def self.oklab_to_oklch(lab_array)
42
+ l, aa, bb = lab_array.map(&:to_d)
43
+
44
+ e = 0.000015.to_d; # if chroma is smaller than this, set hue to 0 similar to CIELch
41
45
 
42
- c = ((aa**2) + (bb**2))**0.5
46
+ c = ((aa**2.to_d) + (bb**2.to_d))**0.5.to_d
43
47
 
44
- h_rad = Math.atan2(bb, aa)
45
- h = h_rad * (180.0 / Math::PI)
48
+ h_rad = Math.atan2(bb, aa).to_d
49
+ h = h_rad * (180.0.to_d / Math::PI.to_d)
46
50
 
47
51
  h %= 360
48
52
 
53
+ h = 0 if c < e
54
+
49
55
  [l, c, h]
50
56
  end
51
57
  end
@@ -23,7 +23,84 @@ module ColorConverters
23
23
  b = color_input[:b].to_f
24
24
  a = (color_input[:a] || 1.0).to_f
25
25
 
26
- { r: r.round(IMPORT_DP), g: g.round(IMPORT_DP), b: b.round(IMPORT_DP), a: a.round(IMPORT_DP) }
26
+ [r, g, b, a]
27
+ end
28
+
29
+ def self.rgb_to_lrgb(rgb_array_frac)
30
+ # [0, 1]
31
+ r, g, b = rgb_array_frac
32
+
33
+ # Inverse sRGB companding. Linearizes RGB channels with respect to energy.
34
+ # Assumption that r, g, b are always positive
35
+ rr, gg, bb = [r, g, b].map do
36
+ if _1.to_d <= 0.04045.to_d
37
+ _1.to_d / 12.92.to_d
38
+ else
39
+ # sRGB Inverse Companding (Non-linear to Linear RGB)
40
+ # The sRGB specification (IEC 61966-2-1) defines the exponent as 2.4.
41
+ #
42
+ (((_1.to_d + 0.055.to_d) / 1.055.to_d)**2.4.to_d)
43
+
44
+ # IMPORTANT NUMERICAL NOTE:
45
+ # On this specific system (and confirmed by Wolfram Alpha for direct calculation),
46
+ # the power function for val**2.4 yields a result that deviates from the value expected by widely-used color science libraries (like Bruce Lindbloom's).
47
+ #
48
+ # To compensate for this numerical discrepancy and ensure the final CIELAB values match standard online calculators and specifications,
49
+ # an empirically determined exponent of 2.5 has been found to produce the correct linearized sRGB values on this environment.
50
+ #
51
+ # Choose 2.4 for strict adherence to the standard's definition (knowing your results may slightly deviate from common calculators),
52
+ # or choose 2.5 to ensure your calculated linear RGB values (and thus CIELAB) match authoritative external tools on this system.
53
+ #
54
+ # ((_1 + 0.055) / 1.055)**2.5
55
+ end
56
+ end
57
+
58
+ # [0, 1]
59
+ [rr, gg, bb]
60
+ end
61
+
62
+ def self.lrgb_to_rgb(lrgb_array)
63
+ rr, gg, bb = lrgb_array
64
+
65
+ # Apply sRGB Companding (gamma correction) to convert from Linear RGB to non-linear sRGB.
66
+ # This is defined by the sRGB specification (IEC 61966-2-1).
67
+ # The exponent for the non-linear segment is 1/2.4 (approximately 0.41666...).
68
+ # Assumption that rr, gg, bb are always positive
69
+ r, g, b = [rr, gg, bb].map do
70
+ if _1.to_d <= 0.0031308.to_d
71
+ # Linear portion of the sRGB curve
72
+ _1.to_d * 12.92.to_d
73
+ else
74
+ # Non-linear (gamma-corrected) portion of the sRGB curve
75
+ # The sRGB specification uses an exponent of 1/2.4.
76
+ #
77
+ (1.055.to_d * (_1.to_d**(1.0.to_d / 2.4.to_d))) - 0.055.to_d
78
+
79
+ # IMPORTANT NUMERICAL NOTE:
80
+ # On this specific system (and confirmed by Wolfram Alpha for direct calculation),
81
+ # the inverse power function for val**2.4 yields a result that deviates from the value expected by widely-used color science libraries (like Bruce Lindbloom's).
82
+ #
83
+ # To compensate for this numerical discrepancy and ensure the final CIELAB values match standard online calculators and specifications,
84
+ # an empirically determined exponent of 2.5 has been found to produce the correct linearized sRGB values on this environment.
85
+ #
86
+ # Choose 1/2.4 for strict adherence to the standard's definition (knowing your results may slightly deviate from common calculators),
87
+ # or choose 1/2.5 to ensure your calculated linear RGB values (and thus CIELAB) match authoritative external tools on this system.
88
+ #
89
+ # (1.055 * (_1**(1.0 / 2.5))) - 0.055
90
+ end
91
+ end
92
+
93
+ # Scale the 0-1 sRGB value to the 0-255 range for 8-bit color components.
94
+ r *= 255.0.to_d
95
+ g *= 255.0.to_d
96
+ b *= 255.0.to_d
97
+
98
+ # Clamping RGB values to prevent out-of-gamut issues and numerical errors and ensures these values stay within the valid and expected range.
99
+ r = r.clamp(0.0..255.0)
100
+ g = g.clamp(0.0..255.0)
101
+ b = b.clamp(0.0..255.0)
102
+
103
+ [r, g, b]
27
104
  end
28
105
  end
29
106
  end
@@ -8,7 +8,7 @@ module ColorConverters
8
8
 
9
9
  private
10
10
 
11
- def validate_input(color_input)
11
+ def validate_input(_color_input)
12
12
  true
13
13
  end
14
14
 
@@ -21,12 +21,12 @@ module ColorConverters
21
21
 
22
22
  a ||= 1.0
23
23
 
24
- r = r.to_f.round(IMPORT_DP)
25
- g = g.to_f.round(IMPORT_DP)
26
- b = b.to_f.round(IMPORT_DP)
27
- a = a.to_f.round(IMPORT_DP)
24
+ r = r.to_f
25
+ g = g.to_f
26
+ b = b.to_f
27
+ a = a.to_f
28
28
 
29
- { r: r, g: g, b: b, a: a }
29
+ [r, g, b, a]
30
30
  end
31
31
  end
32
32
  end
@@ -6,8 +6,12 @@ module ColorConverters
6
6
  color_input.keys - [:x, :y, :z] == []
7
7
  end
8
8
 
9
+ def self.d65
10
+ { x: 95.047, y: 100.0, z: 108.883 }
11
+ end
12
+
9
13
  def self.bounds
10
- { x: [0.0, 95.047], y: [0.0, 100.0], z: [0.0, 108.883] }
14
+ { x: [0.0, 100.0], y: [0.0, 100.0], z: [0.0, 110.0] }
11
15
  end
12
16
 
13
17
  private
@@ -18,98 +22,50 @@ module ColorConverters
18
22
  end
19
23
 
20
24
  def input_to_rgba(color_input)
21
- r, g, b = xyz_to_rgb(color_input)
25
+ r, g, b = XyzConverter.xyz_to_rgb(color_input)
22
26
 
23
- { r: r.round(IMPORT_DP), g: g.round(IMPORT_DP), b: b.round(IMPORT_DP), a: 1.0 }
27
+ [r, g, b, 1.0]
24
28
  end
25
29
 
26
- def xyz_to_rgb(xyz_hash)
30
+ def self.xyz_to_rgb(xyz_hash)
31
+ # [0, 100]
32
+ x = xyz_hash[:x].to_d
33
+ y = xyz_hash[:y].to_d
34
+ z = xyz_hash[:z].to_d
35
+
27
36
  # Convert XYZ (typically with Y=100 for white) to normalized XYZ (Y=1 for white).
28
37
  # The transformation matrix expects X, Y, Z values in the 0-1 range.
29
- x = xyz_hash[:x].to_f / 100.0
30
- y = xyz_hash[:y].to_f / 100.0
31
- z = xyz_hash[:z].to_f / 100.0
32
-
33
- # Convert normalized XYZ to Linear sRGB values.
34
- # This uses the inverse sRGB matrix for D65 illuminant.
35
- # The resulting rr, gg, bb values are linear (gamma-uncorrected) and in the 0-1 range.
36
- rr = (x * 3.2404542) + (y * -1.5371385) + (z * -0.4985314)
37
- gg = (x * -0.9692660) + (y * 1.8760108) + (z * 0.0415560)
38
- bb = (x * 0.0556434) + (y * -0.2040259) + (z * 1.0572252)
39
-
40
- # Apply sRGB Companding (gamma correction) to convert from Linear RGB to non-linear sRGB.
41
- # This is defined by the sRGB specification (IEC 61966-2-1).
42
- # The exponent for the non-linear segment is 1/2.4 (approximately 0.41666...).
43
- r, g, b = [rr, gg, bb].map do
44
- if _1 <= 0.0031308
45
- # Linear portion of the sRGB curve
46
- _1 * 12.92
47
- else
48
- # Non-linear (gamma-corrected) portion of the sRGB curve
49
- # The sRGB specification uses an exponent of 1/2.4.
50
- #
51
- (1.055 * (_1**(1.0 / 2.4))) - 0.055
52
-
53
- # IMPORTANT NUMERICAL NOTE:
54
- # On this specific system (and confirmed by Wolfram Alpha for direct calculation),
55
- # the inverse power function for val**2.4 yields a result that deviates from the value expected by widely-used color science libraries (like Bruce Lindbloom's).
56
- #
57
- # To compensate for this numerical discrepancy and ensure the final CIELAB values match standard online calculators and specifications,
58
- # an empirically determined exponent of 2.5 has been found to produce the correct linearized sRGB values on this environment.
59
- #
60
- # Choose 1/2.4 for strict adherence to the standard's definition (knowing your results may slightly deviate from common calculators),
61
- # or choose 1/2.5 to ensure your calculated linear RGB values (and thus CIELAB) match authoritative external tools on this system.
62
- #
63
- # (1.055 * (_1**(1.0 / 2.5))) - 0.055
64
- end
65
- end
66
-
67
- # Scale the 0-1 sRGB value to the 0-255 range for 8-bit color components.
68
- r *= 255.0
69
- g *= 255.0
70
- b *= 255.0
71
-
72
- # Clamping RGB values to prevent out-of-gamut issues and numerical errors and ensures these values stay within the valid and expected range.
73
- r = r.clamp(0.0..255.0)
74
- g = g.clamp(0.0..255.0)
75
- b = b.clamp(0.0..255.0)
76
-
77
- [r, g, b]
38
+ x /= 100.0.to_d
39
+ y /= 100.0.to_d
40
+ z /= 100.0.to_d
41
+
42
+ # Convert normalized XYZ to Linear sRGB values using sRGB's own white, D65 (no chromatic adaptation)
43
+ # https://www.w3.org/TR/css-color-4/#color-conversion-code
44
+ conversion_matrix = ::Matrix[
45
+ [BigDecimal('3.2409699419045213'), BigDecimal('-1.5373831775700935'), BigDecimal('-0.4986107602930033')],
46
+ [BigDecimal('-0.9692436362808798'), BigDecimal('1.8759675015077206'), BigDecimal('0.04155505740717561')],
47
+ [BigDecimal('0.05563007969699361'), BigDecimal('-0.20397695888897657'), BigDecimal('1.0569715142428786')]
48
+ ]
49
+
50
+ rr, gg, bb = (conversion_matrix * ::Matrix.column_vector([x, y, z])).to_a.flatten
51
+
52
+ # [0, 1]
53
+ RgbConverter.lrgb_to_rgb([rr, gg, bb])
78
54
  end
79
55
 
80
56
  # http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
81
57
  def self.rgb_to_xyz(rgb_array_frac)
82
- r, g, b = rgb_array_frac
83
-
84
- # Inverse sRGB companding. Linearizes RGB channels with respect to energy.
85
- rr, gg, bb = [r, g, b].map do
86
- if _1 <= 0.04045
87
- _1 / 12.92
88
- else
89
- # sRGB Inverse Companding (Non-linear to Linear RGB)
90
- # The sRGB specification (IEC 61966-2-1) defines the exponent as 2.4.
91
- #
92
- (((_1 + 0.055) / 1.055)**2.4)
93
-
94
- # IMPORTANT NUMERICAL NOTE:
95
- # On this specific system (and confirmed by Wolfram Alpha for direct calculation),
96
- # the power function for val**2.4 yields a result that deviates from the value expected by widely-used color science libraries (like Bruce Lindbloom's).
97
- #
98
- # To compensate for this numerical discrepancy and ensure the final CIELAB values match standard online calculators and specifications,
99
- # an empirically determined exponent of 2.5 has been found to produce the correct linearized sRGB values on this environment.
100
- #
101
- # Choose 2.4 for strict adherence to the standard's definition (knowing your results may slightly deviate from common calculators),
102
- # or choose 2.5 to ensure your calculated linear RGB values (and thus CIELAB) match authoritative external tools on this system.
103
- #
104
- # ((_1 + 0.055) / 1.055)**2.5
105
- end
106
- end
107
-
108
- # Convert using the RGB/XYZ matrix at:
109
- # http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html#WSMatrices
110
- x = (rr * 0.4124564) + (gg * 0.3575761) + (bb * 0.1804375)
111
- y = (rr * 0.2126729) + (gg * 0.7151522) + (bb * 0.0721750)
112
- z = (rr * 0.0193339) + (gg * 0.1191920) + (bb * 0.9503041)
58
+ rr, gg, bb = RgbConverter.rgb_to_lrgb(rgb_array_frac)
59
+
60
+ # Convert using the RGB/XYZ matrix and sRGB's own white, D65 (no chromatic adaptation)
61
+ # https://www.w3.org/TR/css-color-4/#color-conversion-code
62
+ conversion_matrix = ::Matrix[
63
+ [BigDecimal('0.4123907992659595'), BigDecimal('0.35758433938387796'), BigDecimal('0.1804807884018343')],
64
+ [BigDecimal('0.21263900587151036'), BigDecimal('0.7151686787677559'), BigDecimal('0.07219231536073371')],
65
+ [BigDecimal('0.01933081871559185'), BigDecimal('0.11919477979462599'), BigDecimal('0.9505321522496606')]
66
+ ]
67
+
68
+ x, y, z = (conversion_matrix * ::Matrix.column_vector([rr, gg, bb])).to_a.flatten
113
69
 
114
70
  # Now, scale X, Y, Z so that Y for D65 white would be 100.
115
71
  x *= 100.0
@@ -117,11 +73,35 @@ module ColorConverters
117
73
  z *= 100.0
118
74
 
119
75
  # Clamping XYZ values to prevent out-of-gamut issues and numerical errors and ensures these values stay within the valid and expected range.
120
- x = x.clamp(0.0..95.047)
121
- y = y.clamp(0.0..100.0)
122
- z = z.clamp(0.0..108.883)
76
+ # x = x.clamp(0.0..95.047)
77
+ # y = y.clamp(0.0..100.0)
78
+ # z = z.clamp(0.0..108.883)
123
79
 
124
80
  [x, y, z]
125
81
  end
82
+
83
+ def self.d50_to_d65(xyz_array)
84
+ x, y, z = xyz_array
85
+
86
+ conversion_matrix = ::Matrix[
87
+ [BigDecimal('0.955473421488075'), BigDecimal('-0.02309845494876471'), BigDecimal('0.06325924320057072')],
88
+ [BigDecimal('-0.0283697093338637'), BigDecimal('1.0099953980813041'), BigDecimal('0.021041441191917323')],
89
+ [BigDecimal('0.012314014864481998'), BigDecimal('-0.020507649298898964'), BigDecimal('1.330365926242124')]
90
+ ]
91
+
92
+ (conversion_matrix * ::Matrix.column_vector([x, y, z])).to_a.flatten
93
+ end
94
+
95
+ def self.d65_to_d50(xyz_array)
96
+ x, y, z = xyz_array
97
+
98
+ conversion_matrix = ::Matrix[
99
+ [BigDecimal('1.0479297925449969'), BigDecimal('0.022946870601609652'), BigDecimal('-0.05019226628920524')],
100
+ [BigDecimal('0.02962780877005599'), BigDecimal('0.9904344267538799'), BigDecimal('-0.017073799063418826')],
101
+ [BigDecimal('-0.009243040646204504'), BigDecimal('0.015055191490298152'), BigDecimal('0.7518742814281371')]
102
+ ]
103
+
104
+ (conversion_matrix * ::Matrix.column_vector([x, y, z])).to_a.flatten
105
+ end
126
106
  end
127
107
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ColorConverters
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.3'
5
5
  end
@@ -16,6 +16,8 @@ require 'color_converters/converters/hsv_converter'
16
16
  require 'color_converters/converters/cmyk_converter'
17
17
  require 'color_converters/converters/xyz_converter'
18
18
  require 'color_converters/converters/cielab_converter'
19
+ require 'color_converters/converters/cielch_converter'
20
+ require 'color_converters/converters/oklab_converter'
19
21
  require 'color_converters/converters/oklch_converter'
20
22
 
21
23
  require 'color_converters/converters/name_converter'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: color_converters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Louis Davis
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-06-25 00:00:00.000000000 Z
10
+ date: 2025-08-18 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: 8.0.2
26
+ - !ruby/object:Gem::Dependency
27
+ name: matrix
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
26
40
  description: Convert colors to hex/rgb/hsv/cmyk/hsl/xyz/cielab/oklch
27
41
  email:
28
42
  - LouisWilliamDavis@gmail.com
@@ -36,6 +50,7 @@ files:
36
50
  - lib/color_converters/base_converter.rb
37
51
  - lib/color_converters/color.rb
38
52
  - lib/color_converters/converters/cielab_converter.rb
53
+ - lib/color_converters/converters/cielch_converter.rb
39
54
  - lib/color_converters/converters/cmyk_converter.rb
40
55
  - lib/color_converters/converters/hex_converter.rb
41
56
  - lib/color_converters/converters/hsl_converter.rb
@@ -43,6 +58,7 @@ files:
43
58
  - lib/color_converters/converters/hsv_converter.rb
44
59
  - lib/color_converters/converters/name_converter.rb
45
60
  - lib/color_converters/converters/null_converter.rb
61
+ - lib/color_converters/converters/oklab_converter.rb
46
62
  - lib/color_converters/converters/oklch_converter.rb
47
63
  - lib/color_converters/converters/rgb_converter.rb
48
64
  - lib/color_converters/converters/rgb_string_converter.rb