red-colors 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bace2ec96d092df5552c1d8f422bb8c641965bb896d4255842f633d2f0e0238
4
- data.tar.gz: d19df3fab59429a3175e2cecb5243b7dce42f3bec54bce55e12a6c26cfebdc61
3
+ metadata.gz: 3587e5433ccb431efbc46112034d3b9f00d40ad58e8389cb4081bf0bdcb3304f
4
+ data.tar.gz: 2568105af67abd20c5133a7181194489006eef94cc3c4834d0f3509eaf01c5f9
5
5
  SHA512:
6
- metadata.gz: 0f1619704538a7cc04ffee56c3eba67c2b0d4bef96f059e253435e93dbf4c1ec52cf76708a1b82e6ac9d09f2d2838610535948f1d82137ca6fa49cafc6180dd6
7
- data.tar.gz: 3bd2f146e561b3583a4abc160af383f335b8d35e12d829dc467332eb2d1753ec6e8b7523ae49c8778d1a4bf594e709509d30f6d732582cbf864d1ac72816b13e
6
+ metadata.gz: d15ae1556244a040ff0822fc739e7dd561d2f29fc9cec04ba68bbc72a1bd6afe097c66fa88f5450bd7f7bce3e89622b34726bdc5e64320255cb6ba71a0c508f7
7
+ data.tar.gz: aa96cd5773128f79d48993319d920e7325b4db7520429eb211347abfb375688566b083d049e1e679aa81ca05f545682490d70f2b29beaedcbc6ea1c094c45c50
data/README.md CHANGED
@@ -20,6 +20,7 @@ $ gem install red-colors
20
20
  - HSL
21
21
  - HSLA
22
22
  - HUSL
23
+ - xyY
23
24
  - XYZ
24
25
 
25
26
  ## Usage
@@ -1,20 +1,23 @@
1
- require_relative "colors/helper"
2
1
  require_relative "colors/alpha_component"
2
+ require_relative "colors/convert"
3
+ require_relative "colors/helper"
3
4
 
4
5
  require_relative "colors/abstract_color"
5
- require_relative "colors/xyz"
6
- require_relative "colors/rgb"
7
- require_relative "colors/rgba"
8
6
  require_relative "colors/hsl"
9
7
  require_relative "colors/hsla"
10
8
  require_relative "colors/husl"
9
+ require_relative "colors/rgb"
10
+ require_relative "colors/rgba"
11
+ require_relative "colors/xyy"
12
+ require_relative "colors/xyz"
11
13
 
14
+ require_relative "colors/color_data"
12
15
  require_relative "colors/named_colors"
13
16
 
14
17
  module Colors
15
18
  # ITU-R BT.709 D65 white point
16
19
  # See https://en.wikipedia.org/wiki/Rec._709 for details
17
- WHITE_POINT_D65 = Colors::XYZ.from_xyY(0.3127r, 0.3290r, 1r)
20
+ WHITE_POINT_D65 = Colors::XYY.new(0.3127r, 0.3290r, 1r).to_xyz
18
21
 
19
22
  def self.desaturate(c, factor)
20
23
  case c
@@ -0,0 +1,180 @@
1
+ module Colors
2
+ module Convert
3
+ module_function
4
+
5
+ # Sort alphabetically by FROM name such as degree, LCh, LUV and so on.
6
+
7
+ # Utilities
8
+
9
+ private def dot_product(matrix, vector)
10
+ matrix.map do |row|
11
+ product = 0.0
12
+ row.zip(vector) do |value1, value2|
13
+ product += value1 * value2
14
+ end
15
+ product
16
+ end
17
+ end
18
+
19
+ def max_chroma(l, h)
20
+ h_rad = degree_to_radian(h)
21
+ sin_h = Math.sin(h_rad).to_r
22
+ cos_h = Math.cos(h_rad).to_r
23
+
24
+ max = Float::INFINITY
25
+ luminance_bounds(l).each do |line|
26
+ len = line[1] / (sin_h - line[0] * cos_h)
27
+ max = len if 0 <= len && len < max
28
+ end
29
+ max
30
+ end
31
+
32
+ private def luminance_bounds(l)
33
+ sub1 = (l + 16)**3 / 1560896r
34
+ sub2 = sub1 > XYZ::EPSILON ? sub1 : l/XYZ::KAPPA
35
+
36
+ bounds = Array.new(6) { [0r, 0r] }
37
+ 0.upto(2) do |ch|
38
+ m1 = XYZ2RGB[ch][0].to_r
39
+ m2 = XYZ2RGB[ch][1].to_r
40
+ m3 = XYZ2RGB[ch][2].to_r
41
+
42
+ [0, 1].each do |t|
43
+ top1 = (284517r * m1 - 94839r * m3) * sub2
44
+ top2 = (838422r * m3 + 769860r * m2 + 731718r * m1) * l * sub2 - 769860r * t * l
45
+ bottom = (632260r * m3 - 126452r * m2) * sub2 + 126452r * t
46
+
47
+ bounds[ch*2 + t][0] = top1 / bottom
48
+ bounds[ch*2 + t][1] = top2 / bottom
49
+ end
50
+ end
51
+ bounds
52
+ end
53
+
54
+ # degree -> ???
55
+
56
+ DEG2RAD = 0.01745329251994329577r # 2 * pi / 360
57
+ def degree_to_radian(d)
58
+ d * DEG2RAD
59
+ end
60
+
61
+ # LCh -> ???
62
+
63
+ def lch_to_husl(l, c, h)
64
+ if l > 99.9999999 || l < 1e-8
65
+ s = 0r
66
+ else
67
+ mx = max_chroma(l, h)
68
+ s = c / mx * 100r
69
+ end
70
+
71
+ h = 0r if c < 1e-8
72
+
73
+ [h, s/100r, l/100r]
74
+ end
75
+
76
+ def lch_to_luv(l, c, h)
77
+ h_rad = degree_to_radian(h)
78
+ u = Math.cos(h_rad).to_r * c
79
+ v = Math.sin(h_rad).to_r * c
80
+ [l, u, v]
81
+ end
82
+
83
+ def lch_to_xyz(l, c, h)
84
+ luv_to_xyz(*lch_to_luv(l, c, h))
85
+ end
86
+
87
+ # linear-sRGB -> ???
88
+
89
+ def linear_srgb_to_srgb(r, g, b)
90
+ [r, g, b].map do |v|
91
+ # the following is an optimization technique for `1.055*v**(1/2.4) - 0.055`.
92
+ # x^y ~= exp(y*log(x)) ~= exp2(y*log2(y)); the middle form is faster
93
+ #
94
+ # See https://github.com/JuliaGraphics/Colors.jl/issues/351#issuecomment-532073196
95
+ # for more detail benchmark in Julia language.
96
+ if v <= 0.0031308
97
+ 12.92*v
98
+ else
99
+ 1.055 * Math.exp(1/2.4 * Math.log(v)) - 0.055
100
+ end
101
+ end
102
+ end
103
+
104
+ # Luv -> ???
105
+
106
+ def luv_to_husl(l, u, v)
107
+ lch_to_husl(*luv_to_lch(l, u, v))
108
+ end
109
+
110
+ def luv_to_lch(l, u, v)
111
+ c = Math.sqrt(u*u + v*v).to_r
112
+
113
+ if c < 1e-8
114
+ h = 0r
115
+ else
116
+ h = Math.atan2(v, u).to_r * 180/Math::PI.to_r
117
+ h += 360r if h < 0
118
+ end
119
+
120
+ [l, c, h]
121
+ end
122
+
123
+ def luv_to_xyz(l, u, v)
124
+ return [0r, 0r, 0r] if l <= 1e-8
125
+
126
+ wp_u, wp_v = WHITE_POINT_D65.uv_values
127
+ var_u = u / (13 * l) + wp_u
128
+ var_v = v / (13 * l) + wp_v
129
+ y = if l < 8
130
+ l / XYZ::KAPPA
131
+ else
132
+ ((l + 16r) / 116r)**3
133
+ end
134
+ x = -(9 * y * var_u) / ((var_u - 4) * var_v - var_u * var_v)
135
+ z = (9 * y - (15 * var_v * y) - (var_v * x)) / (3 * var_v)
136
+ [x, y, z]
137
+ end
138
+
139
+ # RGB -> ???
140
+
141
+ RGB2XYZ = [
142
+ [ 0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751 ],
143
+ [ 0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500 ],
144
+ [ 0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086 ]
145
+ ]
146
+
147
+ def rgb_to_xyz(r, g, b)
148
+ dot_product(RGB2XYZ, srgb_to_linear_srgb(r, g, b))
149
+ end
150
+
151
+ # sRGB -> ???
152
+
153
+ def srgb_to_linear_srgb(r, g, b)
154
+ [r, g, b].map do |v|
155
+ if v > 0.04045
156
+ ((v + 0.055r) / 1.055r) ** 2.4r
157
+ else
158
+ v / 12.92r
159
+ end
160
+ end
161
+ end
162
+
163
+ # XYZ -> ???
164
+
165
+ XYZ2RGB = [
166
+ [ 3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366 ],
167
+ [ -0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247 ],
168
+ [ 0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072 ]
169
+ ]
170
+ def xyz_to_rgb(x, y, z)
171
+ r, g, b = dot_product(XYZ2RGB, [x, y, z])
172
+ r, g, b = srgb_to_linear_srgb(r, g, b)
173
+ [
174
+ r.clamp(0r, 1r),
175
+ g.clamp(0r, 1r),
176
+ b.clamp(0r, 1r)
177
+ ]
178
+ end
179
+ end
180
+ end
@@ -1,45 +1,7 @@
1
- require "numo/narray"
2
-
3
1
  module Colors
4
2
  # Human-friendly alternative to HSL color space.
5
3
  # The definition of HUSL is provided in <http://www.hsluv.org>.
6
4
  class HUSL < HSL
7
- DEG2RAD = 0.01745329251994329577r # 2 * pi / 360
8
-
9
- def self.from_rgb(r, g, b)
10
- c = XYZ.from_rgb(r, g, b)
11
- l, u, v = c.luv_components(WHITE_POINT_D65)
12
- l, c, h = convert_luv_to_lch(l, u, v)
13
- h, s, l = convert_lch_to_husl(l, c, h)
14
- new(h, s.to_r.clamp(0r, 1r), l.to_r.clamp(0r, 1r))
15
- end
16
-
17
- private_class_method def self.convert_luv_to_lch(l, u, v)
18
- c = Math.sqrt(u*u + v*v).to_r
19
-
20
- if c < 1e-8
21
- h = 0r
22
- else
23
- h = Math.atan2(v, u).to_r * 180/Math::PI.to_r
24
- h += 360r if h < 0
25
- end
26
-
27
- [l, c, h]
28
- end
29
-
30
- private_class_method def self.convert_lch_to_husl(l, c, h)
31
- if l > 99.9999999 || l < 1e-8
32
- s = 0r
33
- else
34
- mx = max_chroma(l, h)
35
- s = c / mx * 100r
36
- end
37
-
38
- h = 0r if c < 1e-8
39
-
40
- [h, s/100r, l/100r]
41
- end
42
-
43
5
  def ==(other)
44
6
  case other
45
7
  when HUSL
@@ -61,10 +23,13 @@ module Colors
61
23
  RGB.new(*rgb_components)
62
24
  end
63
25
 
26
+ def to_xyz
27
+ x, y, z = Convert.lch_to_xyz(*lch_components)
28
+ XYZ.new(x, y, z)
29
+ end
30
+
64
31
  def rgb_components
65
- l, u, v = convert_lch_to_luv(*lch_components)
66
- x, y, z = convert_luv_to_xyz(l, u, v)
67
- XYZ.new(x, y, z).rgb_components
32
+ to_xyz.rgb_components
68
33
  end
69
34
 
70
35
  def lch_components
@@ -74,7 +39,7 @@ module Colors
74
39
  if l > 99.9999999 || l < 1e-8
75
40
  c = 0r
76
41
  else
77
- mx = self.class.max_chroma(l, h)
42
+ mx = Convert.max_chroma(l, h)
78
43
  c = mx / 100r * s
79
44
  end
80
45
 
@@ -82,63 +47,5 @@ module Colors
82
47
 
83
48
  [l, c, h]
84
49
  end
85
-
86
- private def convert_lch_to_luv(l, c, h)
87
- h_rad = h * DEG2RAD
88
- u = Math.cos(h_rad).to_r * c
89
- v = Math.sin(h_rad).to_r * c
90
- [l, u, v]
91
- end
92
-
93
- private def convert_luv_to_xyz(l, u, v)
94
- return [0r, 0r, 0r] if l <= 1e-8
95
-
96
- wp_u, wp_v = WHITE_POINT_D65.uv_values
97
- var_u = u / (13 * l) + wp_u
98
- var_v = v / (13 * l) + wp_v
99
- y = if l < 8
100
- l / XYZ::KAPPA
101
- else
102
- ((l + 16r) / 116r)**3
103
- end
104
- x = -(9 * y * var_u) / ((var_u - 4) * var_v - var_u * var_v)
105
- z = (9 * y - (15 * var_v * y) - (var_v * x)) / (3 * var_v)
106
- [x, y, z]
107
- end
108
-
109
- def self.max_chroma(l, h)
110
- h_rad = h * DEG2RAD
111
- sin_h = Math.sin(h_rad).to_r
112
- cos_h = Math.cos(h_rad).to_r
113
-
114
- result = Float::INFINITY
115
- get_bounds(l).each do |line|
116
- len = line[1] / (sin_h - line[0] * cos_h)
117
- result = len if 0 <= len && len < result
118
- end
119
- result
120
- end
121
-
122
- def self.get_bounds(l)
123
- sub1 = (l + 16)**3 / 1560896r
124
- sub2 = sub1 > XYZ::EPSILON ? sub1 : l/XYZ::KAPPA
125
-
126
- bounds = Array.new(6) { [0r, 0r] }
127
- 0.upto(2) do |ch|
128
- m1 = XYZ2RGB[ch, 0].to_r
129
- m2 = XYZ2RGB[ch, 1].to_r
130
- m3 = XYZ2RGB[ch, 2].to_r
131
-
132
- [0, 1].each do |t|
133
- top1 = (284517r * m1 - 94839r * m3) * sub2
134
- top2 = (838422r * m3 + 769860r * m2 + 731718r * m1) * l * sub2 - 769860r * t * l
135
- bottom = (632260r * m3 - 126452r * m2) * sub2 + 126452r * t
136
-
137
- bounds[ch*2 + t][0] = top1 / bottom
138
- bounds[ch*2 + t][1] = top2 / bottom
139
- end
140
- end
141
- bounds
142
- end
143
50
  end
144
51
  end
@@ -1,5 +1,3 @@
1
- require_relative 'color_data'
2
-
3
1
  module Colors
4
2
  module NamedColors
5
3
  class Mapping
@@ -8,33 +6,26 @@ module Colors
8
6
  @cache = {}
9
7
  end
10
8
 
11
- attr_reader :cache
12
-
13
9
  def [](name)
14
10
  if NamedColors.nth_color?(name)
15
11
  cycle = ColorData::DEFAULT_COLOR_CYCLE
16
12
  name = cycle[name[1..-1].to_i % cycle.length]
17
13
  end
18
- if cache.has_key?(name)
19
- cache[name]
20
- else
21
- cache[name] = lookup_no_color_cycle(name)
22
- end
14
+ @cache[name] ||= lookup_no_color_cycle(name)
23
15
  end
24
16
 
25
- private def lookup_no_color_cycle(color)
26
- orig_color = color
27
- case color
17
+ private def lookup_no_color_cycle(name)
18
+ case name
28
19
  when /\Anone\z/i
29
20
  return RGBA.new(0, 0, 0, 0)
30
21
  when String
31
22
  # nothing to do
32
23
  when Symbol
33
- color = color.to_s
24
+ name = name.to_s
34
25
  else
35
- color = color.to_str
26
+ name = name.to_str
36
27
  end
37
- color = @mapping.fetch(color, color)
28
+ color = @mapping.fetch(name, name)
38
29
  case color
39
30
  when /\A#\h+\z/
40
31
  case color.length - 1
@@ -64,19 +55,19 @@ module Colors
64
55
  def []=(name, value)
65
56
  @mapping[name] = value
66
57
  ensure
67
- cache.clear
58
+ @cache.clear
68
59
  end
69
60
 
70
61
  def delete(name)
71
62
  @mapping.delete(name)
72
63
  ensure
73
- cache.clear
64
+ @cache.clear
74
65
  end
75
66
 
76
67
  def update(other)
77
68
  @mapping.update(other)
78
69
  ensure
79
- cache.clear
70
+ @cache.clear
80
71
  end
81
72
  end
82
73
 
@@ -104,11 +95,10 @@ module Colors
104
95
  when Symbol
105
96
  name = name.to_s
106
97
  else
98
+ return false unless name.respond_to?(:to_str)
107
99
  name = name.to_str
108
100
  end
109
101
  name.match?(/\AC\d+\z/)
110
- rescue NoMethodError, TypeError
111
- false
112
102
  end
113
103
  end
114
104
  end
@@ -1,22 +1,25 @@
1
- require_relative 'helper'
2
-
3
1
  module Colors
4
2
  class RGB < AbstractColor
5
3
  include Helper
6
4
 
7
5
  def self.parse(hex_string)
8
- case hex_string.to_str.match(/\A#(\h+)\z/) { $1 }.length
6
+ error_message = "must be a hexadecimal string of `#rrggbb` or `#rgb` form"
7
+ unless hex_string.respond_to?(:to_str)
8
+ raise ArgumentError, "#{error_message}: #{hex_string.inspect}"
9
+ end
10
+
11
+ hex_string = hex_string.to_str
12
+ hexes = hex_string.match(/\A#(\h+)\z/) { $1 }
13
+ case hexes&.length
9
14
  when 3 # rgb
10
- r, g, b = hex_string.scan(/\h/).map {|h| h.hex * 17 }
15
+ r, g, b = hexes.scan(/\h/).map {|h| h.hex * 17 }
11
16
  new(r, g, b)
12
17
  when 6 # rrggbb
13
- r, g, b = hex_string.scan(/\h{2}/).map(&:hex)
18
+ r, g, b = hexes.scan(/\h{2}/).map(&:hex)
14
19
  new(r, g, b)
15
20
  else
16
- raise ArgumentError, "Invalid hex string: #{hex_string.inspect}"
21
+ raise ArgumentError, "#{error_message}: #{hex_string.inspect}"
17
22
  end
18
- rescue NoMethodError
19
- raise ArgumentError, "hex_string must be a hexadecimal string of `#rrggbb` or `#rgb` form"
20
23
  end
21
24
 
22
25
  def initialize(r, g, b)
@@ -112,11 +115,14 @@ module Colors
112
115
  end
113
116
 
114
117
  def to_husl
115
- HUSL.from_rgb(r, g, b)
118
+ c = RGB.new(r, g, b).to_xyz
119
+ l, u, v = c.luv_components(WHITE_POINT_D65)
120
+ h, s, l = Convert.luv_to_husl(l, u, v)
121
+ HUSL.new(h, s.to_r.clamp(0r, 1r), l.to_r.clamp(0r, 1r))
116
122
  end
117
123
 
118
124
  def to_xyz
119
- XYZ.from_rgb(r, g, b)
125
+ XYZ.new(*Convert.rgb_to_xyz(r, g, b))
120
126
  end
121
127
 
122
128
  private def canonicalize(r, g, b)
@@ -1,24 +1,30 @@
1
1
  module Colors
2
2
  class RGBA < RGB
3
3
  def self.parse(hex_string)
4
- case hex_string.to_str.match(/\A#(\h+)\z/) { $1 }.length
4
+ error_message = "must be a hexadecimal string of " +
5
+ "`#rrggbbaa`, `#rgba`, `#rrggbb` or `#rgb` form"
6
+ unless hex_string.respond_to?(:to_str)
7
+ raise ArgumentError, "#{error_message}: #{hex_string.inspect}"
8
+ end
9
+
10
+ hex_string = hex_string.to_str
11
+ hexes = hex_string.match(/\A#(\h+)\z/) { $1 }
12
+ case hexes&.length
5
13
  when 3 # rgb
6
- r, g, b = hex_string.scan(/\h/).map {|h| h.hex * 17 }
14
+ r, g, b = hexes.scan(/\h/).map {|h| h.hex * 17 }
7
15
  new(r, g, b, 255)
8
16
  when 6 # rrggbb
9
- r, g, b = hex_string.scan(/\h{2}/).map(&:hex)
17
+ r, g, b = hexes.scan(/\h{2}/).map(&:hex)
10
18
  new(r, g, b, 255)
11
19
  when 4 # rgba
12
- r, g, b, a = hex_string.scan(/\h/).map {|h| h.hex * 17 }
20
+ r, g, b, a = hexes.scan(/\h/).map {|h| h.hex * 17 }
13
21
  new(r, g, b, a)
14
22
  when 8 # rrggbbaa
15
- r, g, b, a = hex_string.scan(/\h{2}/).map(&:hex)
23
+ r, g, b, a = hexes.scan(/\h{2}/).map(&:hex)
16
24
  new(r, g, b, a)
17
25
  else
18
- raise ArgumentError, "Invalid hex string: #{hex_string.inspect}"
26
+ raise ArgumentError, "#{error_message}: #{hex_string.inspect}"
19
27
  end
20
- rescue NoMethodError
21
- raise ArgumentError, "hex_string must be a hexadecimal string of `#rrggbb` or `#rgb` form"
22
28
  end
23
29
 
24
30
  def initialize(r, g, b, a)
@@ -1,3 +1,3 @@
1
1
  module Colors
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,50 @@
1
+ module Colors
2
+ class XYY < AbstractColor
3
+ include Helper
4
+
5
+ def initialize(x, y, large_y)
6
+ @x, @y, @large_y = canonicalize(x, y, large_y)
7
+ end
8
+
9
+ attr_reader :x, :y, :large_y
10
+
11
+ def components
12
+ [x, y, large_y]
13
+ end
14
+
15
+ def ==(other)
16
+ case other
17
+ when XYY
18
+ x == other.x && y == other.y && large_y == other.large_y
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def to_rgb
25
+ to_xyz.to_rgb
26
+ end
27
+
28
+ def rgb_components
29
+ to_xyz.rgb_components
30
+ end
31
+
32
+ def luv_components(wp)
33
+ to_xyz.luv_components(wp)
34
+ end
35
+
36
+ def to_xyz
37
+ large_x = large_y*x/y
38
+ large_z = large_y*(1 - x - y)/y
39
+ XYZ.new(large_x, large_y, large_z)
40
+ end
41
+
42
+ private def canonicalize(x, y, large_y)
43
+ [
44
+ Rational(x),
45
+ Rational(y),
46
+ Rational(large_y)
47
+ ]
48
+ end
49
+ end
50
+ end
@@ -1,20 +1,4 @@
1
- require_relative "helper"
2
-
3
- require "numo/narray"
4
-
5
1
  module Colors
6
- XYZ2RGB = Numo::DFloat[
7
- [ 3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366 ],
8
- [ -0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247 ],
9
- [ 0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072 ]
10
- ]
11
-
12
- RGB2XYZ = Numo::DFloat[
13
- [ 0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751 ],
14
- [ 0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500 ],
15
- [ 0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086 ]
16
- ]
17
-
18
2
  class XYZ < AbstractColor
19
3
  include Helper
20
4
 
@@ -22,25 +6,6 @@ module Colors
22
6
 
23
7
  KAPPA = (29/3)**3
24
8
 
25
- def self.from_xyY(x, y, large_y)
26
- large_x = large_y*x/y
27
- large_z = large_y*(1 - x - y)/y
28
- new(large_x, large_y, large_z)
29
- end
30
-
31
- def self.from_rgb(r, g, b)
32
- c = RGB2XYZ.dot(Numo::DFloat[to_linear(r), to_linear(g), to_linear(b)])
33
- new(c[0], c[1], c[2])
34
- end
35
-
36
- private_class_method def self.to_linear(v)
37
- if v > 0.04045
38
- ((v + 0.055r) / 1.055r) ** 2.4r
39
- else
40
- v / 12.92r
41
- end
42
- end
43
-
44
9
  def initialize(x, y, z)
45
10
  @x, @y, @z = canonicalize(x, y, z)
46
11
  end
@@ -65,12 +30,7 @@ module Colors
65
30
  end
66
31
 
67
32
  def rgb_components
68
- c = XYZ2RGB.dot(Numo::DFloat[x, y, z])
69
- [
70
- srgb_compand(c[0]).clamp(0r, 1r),
71
- srgb_compand(c[1]).clamp(0r, 1r),
72
- srgb_compand(c[2]).clamp(0r, 1r)
73
- ]
33
+ Convert.xyz_to_rgb(x, y, z)
74
34
  end
75
35
 
76
36
  def luv_components(wp)
@@ -99,19 +59,6 @@ module Colors
99
59
  [u, v]
100
60
  end
101
61
 
102
- private def srgb_compand(v)
103
- # the following is an optimization technique for `1.055*v**(1/2.4) - 0.055`.
104
- # x^y ~= exp(y*log(x)) ~= exp2(y*log2(y)); the middle form is faster
105
- #
106
- # See https://github.com/JuliaGraphics/Colors.jl/issues/351#issuecomment-532073196
107
- # for more detail benchmark in Julia language.
108
- if v <= 0.0031308
109
- 12.92*v
110
- else
111
- 1.055 * Math.exp(1/2.4 * Math.log(v)) - 0.055
112
- end
113
- end
114
-
115
62
  private def canonicalize(x, y, z)
116
63
  [
117
64
  Rational(x),
@@ -37,5 +37,4 @@ Gem::Specification.new do |spec|
37
37
  spec.add_development_dependency("test-unit")
38
38
  spec.add_development_dependency("yard")
39
39
  spec.add_development_dependency("kramdown")
40
- spec.add_development_dependency("numo-narray")
41
40
  end
@@ -175,44 +175,17 @@ class ColorsHUSLTest < Test::Unit::TestCase
175
175
  end
176
176
 
177
177
  test("#desaturate") do
178
- c = Colors::HUSL.from_rgb(1r, 1r, 0r).desaturate(0.8)
178
+ c = Colors::RGB.new(1r, 1r, 0r).to_husl.desaturate(0.8)
179
179
  assert_instance_of(Colors::HUSL, c)
180
180
  assert_near(Colors::HUSL.new(85.87432021817473r, 0.9838589961976354r, 0.8850923805142681r), c)
181
181
  end
182
182
 
183
- test("to_husl") do
183
+ test("#to_husl") do
184
184
  black = Colors::HUSL.new(0, 0, 0)
185
185
  assert_same(black, black.to_hsl)
186
186
  end
187
187
 
188
- test(".from_rgb") do
189
- # black
190
- assert_equal(Colors::HUSL.new(0, 0, 0),
191
- Colors::HUSL.from_rgb(0, 0, 0))
192
- # red
193
- assert_near(Colors::HUSL.new(12.177050630061776r, 1r, 0.5323711559542933r),
194
- Colors::HUSL.from_rgb(1r, 0, 0))
195
- ## yellow
196
- assert_near(Colors::HUSL.new(85.87432021817473r, 1r, 0.9713855934179674r),
197
- Colors::HUSL.from_rgb(1r, 1r, 0))
198
- ## green
199
- assert_near(Colors::HUSL.new(127.71501294924047r, 1r, 0.8773551910965973r),
200
- Colors::HUSL.from_rgb(0r, 1r, 0))
201
- ## cyan
202
- assert_near(Colors::HUSL.new(192.17705063006116r, 1r, 0.9111475231670507r),
203
- Colors::HUSL.from_rgb(0r, 1r, 1r))
204
- ## blue
205
- assert_near(Colors::HUSL.new(265.8743202181779r, 1r, 0.3230087290398002r),
206
- Colors::HUSL.from_rgb(0r, 0r, 1r))
207
- ## magenta
208
- assert_near(Colors::HUSL.new(307.7150129492436r, 1r, 0.60322731354551294r),
209
- Colors::HUSL.from_rgb(1r, 0r, 1r))
210
- ## white
211
- assert_near(Colors::HUSL.new(0r, 0r, 1r),
212
- Colors::HUSL.from_rgb(1r, 1r, 1r))
213
- end
214
-
215
- test("to_rgb") do
188
+ test("#to_rgb") do
216
189
  # black
217
190
  assert_equal(Colors::RGB.new(0, 0, 0),
218
191
  Colors::HUSL.new(0, 0, 0).to_rgb)
@@ -234,7 +234,7 @@ class ColorsRGBTest < Test::Unit::TestCase
234
234
  Colors::RGB.new(0x33, 0x33, 0x33).to_hex_string)
235
235
  end
236
236
 
237
- test("to_rgb") do
237
+ test("#to_rgb") do
238
238
  black = Colors::RGB.new(0, 0, 0)
239
239
  assert_same(black, black.to_rgb)
240
240
  end
@@ -291,4 +291,31 @@ class ColorsRGBTest < Test::Unit::TestCase
291
291
  assert_equal(Colors::HSL.new(0r, 0r, 1r),
292
292
  Colors::RGB.new(1r, 1r, 1r).to_hsl)
293
293
  end
294
+
295
+ test("#to_husl") do
296
+ # black
297
+ assert_equal(Colors::HUSL.new(0, 0, 0),
298
+ Colors::RGB.new(0, 0, 0).to_husl)
299
+ # red
300
+ assert_near(Colors::HUSL.new(12.177050630061776r, 1r, 0.5323711559542933r),
301
+ Colors::RGB.new(1r, 0, 0).to_husl)
302
+ # yellow
303
+ assert_near(Colors::HUSL.new(85.87432021817473r, 1r, 0.9713855934179674r),
304
+ Colors::RGB.new(1r, 1r, 0).to_husl)
305
+ # green
306
+ assert_near(Colors::HUSL.new(127.71501294924047r, 1r, 0.8773551910965973r),
307
+ Colors::RGB.new(0r, 1r, 0).to_husl)
308
+ # cyan
309
+ assert_near(Colors::HUSL.new(192.17705063006116r, 1r, 0.9111475231670507r),
310
+ Colors::RGB.new(0r, 1r, 1r).to_husl)
311
+ # blue
312
+ assert_near(Colors::HUSL.new(265.8743202181779r, 1r, 0.3230087290398002r),
313
+ Colors::RGB.new(0r, 0r, 1r).to_husl)
314
+ # magenta
315
+ assert_near(Colors::HUSL.new(307.7150129492436r, 1r, 0.60322731354551294r),
316
+ Colors::RGB.new(1r, 0r, 1r).to_husl)
317
+ # white
318
+ assert_near(Colors::HUSL.new(0r, 0r, 1r),
319
+ Colors::RGB.new(1r, 1r, 1r).to_husl)
320
+ end
294
321
  end
@@ -1,7 +1,7 @@
1
1
  class ColorsXYZTest < Test::Unit::TestCase
2
2
  sub_test_case("#luv_components") do
3
3
  test("on ITU-R BT.709 D65 white point") do
4
- l, u, v = Colors::XYZ.from_rgb(0r, 0r, 0r).luv_components(Colors::WHITE_POINT_D65)
4
+ l, u, v = Colors::RGB.new(0r, 0r, 0r).to_xyz.luv_components(Colors::WHITE_POINT_D65)
5
5
  assert_in_delta(0, l)
6
6
  assert_in_delta(0, u)
7
7
  assert_in_delta(0, v)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: red-colors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-16 00:00:00.000000000 Z
11
+ date: 2019-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: numo-narray
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  description: ''
98
84
  email:
99
85
  - mrkn@mrkn.jp
@@ -110,6 +96,7 @@ files:
110
96
  - lib/colors/abstract_color.rb
111
97
  - lib/colors/alpha_component.rb
112
98
  - lib/colors/color_data.rb
99
+ - lib/colors/convert.rb
113
100
  - lib/colors/helper.rb
114
101
  - lib/colors/hsl.rb
115
102
  - lib/colors/hsla.rb
@@ -118,6 +105,7 @@ files:
118
105
  - lib/colors/rgb.rb
119
106
  - lib/colors/rgba.rb
120
107
  - lib/colors/version.rb
108
+ - lib/colors/xyy.rb
121
109
  - lib/colors/xyz.rb
122
110
  - red-colors.gemspec
123
111
  - test/helper.rb
@@ -148,18 +136,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
136
  - !ruby/object:Gem::Version
149
137
  version: '0'
150
138
  requirements: []
151
- rubygems_version: 3.0.6
139
+ rubyforge_project:
140
+ rubygems_version: 2.7.6.2
152
141
  signing_key:
153
142
  specification_version: 4
154
143
  summary: Red Colors provides a wide array of features for dealing with colors. This
155
144
  includes conversion between colorspaces, desaturation, and parsing colors.
156
145
  test_files:
157
- - test/test-named-color.rb
158
- - test/test-hsl.rb
159
- - test/test-husl.rb
160
- - test/test-xyz.rb
161
146
  - test/test-hsla.rb
147
+ - test/test-named-color.rb
148
+ - test/test-rgba.rb
162
149
  - test/helper.rb
163
150
  - test/test-rgb.rb
164
- - test/test-rgba.rb
165
151
  - test/run.rb
152
+ - test/test-hsl.rb
153
+ - test/test-xyz.rb
154
+ - test/test-husl.rb