decolmor 1.1.0 → 1.3.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 +4 -4
- data/CHANGELOG.md +47 -5
- data/NEWS.md +24 -3
- data/README.md +88 -30
- data/decolmor.gemspec +5 -5
- data/lib/decolmor/main.rb +353 -225
- data/lib/decolmor/version.rb +1 -1
- data/lib/decolmor.rb +0 -14
- data/spec/decolmor_spec.rb +373 -90
- data/spec/factories/colors.rb +51 -24
- metadata +8 -8
data/lib/decolmor/main.rb
CHANGED
@@ -1,282 +1,410 @@
|
|
1
1
|
module Decolmor
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
def self.hex_to_rgb(hex, alpha_round = 3)
|
6
|
-
hex = hex.gsub('#','')
|
7
|
-
hex = if [3, 4].include? hex.length
|
8
|
-
hex.chars.map{ |char| char * 2 }
|
9
|
-
else
|
10
|
-
hex.scan(/../)
|
11
|
-
end
|
12
|
-
rgb = hex.map(&:hex)
|
13
|
-
rgb.size == 4 ? rgb + [(rgb.delete_at(3) / 255.to_f).round(alpha_round)] : rgb
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
14
5
|
end
|
15
6
|
|
16
|
-
|
17
|
-
template = rgb.size == 3 ? "#%02X%02X%02X" : "#%02X%02X%02X%02X"
|
18
|
-
rgb = rgb[0..2] + [(rgb[3] * 255).round] if rgb.size == 4
|
19
|
-
template % rgb
|
20
|
-
end
|
7
|
+
#========= Set default rounding for HSL/HSV/HSB/CMYK conversion ========
|
21
8
|
|
22
|
-
|
9
|
+
# round 1 enough for lossless conversion RGB -> HSL/HSV/HSB -> RGB
|
10
|
+
# for lossless conversion HSL <==> HSV (HSB) better to use round 2
|
11
|
+
#
|
12
|
+
HSX_ROUND = 1
|
23
13
|
|
24
|
-
|
25
|
-
def self.new_rgb(red: nil, green: nil, blue: nil, alpha: nil)
|
26
|
-
range = 0..255
|
27
|
-
rgb = [red, green, blue].map { |channel| channel || rand(range) }
|
28
|
-
alpha.nil? ? rgb : rgb + [alpha]
|
29
|
-
end
|
14
|
+
module ClassMethods
|
30
15
|
|
31
|
-
|
16
|
+
attr_writer :hsx_round
|
32
17
|
|
33
|
-
|
34
|
-
|
35
|
-
|
18
|
+
def hsx_round
|
19
|
+
@hsx_round ||= HSX_ROUND
|
20
|
+
end
|
36
21
|
|
37
|
-
|
38
|
-
|
22
|
+
#========= HEX <==> RGB(A) =============================================
|
23
|
+
|
24
|
+
def hex_to_rgb(hex, alpha_round = 3, alpha_255: false)
|
25
|
+
hex = hex.gsub('#','')
|
26
|
+
hex = if [3, 4].include? hex.length
|
27
|
+
hex.chars.map{ |char| char * 2 }
|
28
|
+
else
|
29
|
+
hex.scan(/../)
|
30
|
+
end
|
31
|
+
rgb = hex.map(&:hex)
|
32
|
+
if rgb.size == 4
|
33
|
+
rgb[3] = (rgb[3] / 255.to_f).round(alpha_round) unless alpha_255
|
34
|
+
end
|
35
|
+
|
36
|
+
rgb
|
37
|
+
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
def rgb_to_hex(rgb, alpha_255: false)
|
40
|
+
if rgb.size == 3
|
41
|
+
"#%02X%02X%02X" % rgb
|
42
|
+
else
|
43
|
+
rgb[3] = (rgb[3] * 255).round unless alpha_255
|
44
|
+
"#%02X%02X%02X%02X" % rgb
|
45
|
+
end
|
46
|
+
end
|
44
47
|
|
45
|
-
|
46
|
-
saturation *= 100
|
47
|
-
lightness *= 100
|
48
|
+
#=======================================================================
|
48
49
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
# simple generator RGB, you can set any channel(s)
|
51
|
+
def new_rgb(red: nil, green: nil, blue: nil, alpha: nil)
|
52
|
+
range = 0..255
|
53
|
+
rgb = [red, green, blue].map { |channel| channel || rand(range) }
|
54
|
+
alpha.nil? ? rgb : rgb + [alpha]
|
55
|
+
end
|
53
56
|
|
54
|
-
|
55
|
-
# scaling RGB values into range 0..1
|
56
|
-
red, green, blue, alpha = rgb_arr.map { |color| color / 255.to_f }
|
57
|
+
#========= RGB(A) to HSL/HSV/HSB =======================================
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
def rgb_to_hsl(rgb_arr, rounding = hsx_round)
|
60
|
+
# scaling RGB values into range 0..1
|
61
|
+
red, green, blue, alpha = rgb_arr.map { |color| color / 255.to_f }
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
saturation = chroma == 0 ? 0 : chroma / cmax
|
64
|
-
value = cmax
|
63
|
+
# calculation intermediate values
|
64
|
+
cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
# calculation HSL values
|
67
|
+
hue = get_hue(red, green, blue)
|
68
|
+
lightness = (cmax + cmin) / 2
|
69
|
+
saturation = chroma == 0 ? 0 : chroma / (1 - (2 * lightness - 1).abs)
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
71
|
+
# scaling values to fill 0..100 interval
|
72
|
+
saturation *= 100
|
73
|
+
lightness *= 100
|
74
74
|
|
75
|
+
# rounding, drop Alpha if not set (nil)
|
76
|
+
hsl = [hue, saturation, lightness].map { |x| x.round(rounding) }
|
77
|
+
alpha.nil? ? hsl : hsl + [alpha * 255]
|
78
|
+
end
|
79
|
+
|
80
|
+
def rgb_to_hsv(rgb_arr, rounding = hsx_round)
|
81
|
+
# scaling RGB values into range 0..1
|
82
|
+
red, green, blue, alpha = rgb_arr.map { |color| color / 255.to_f }
|
75
83
|
|
76
|
-
|
84
|
+
# calculation intermediate values
|
85
|
+
_cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
|
77
86
|
|
78
|
-
|
87
|
+
# calculation HSV values
|
88
|
+
hue = get_hue(red, green, blue)
|
89
|
+
saturation = chroma == 0 ? 0 : chroma / cmax
|
90
|
+
value = cmax
|
79
91
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
saturation /= 100
|
84
|
-
lightness /= 100
|
92
|
+
# scaling values into range 0..100
|
93
|
+
saturation *= 100
|
94
|
+
value *= 100
|
85
95
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
k = (n + hue / 30) % 12
|
90
|
-
lightness - a * [-1, [k - 3, 9 - k, 1].min].max
|
96
|
+
# rounding
|
97
|
+
hsv = [hue, saturation, value].map { |x| x.round(rounding) }
|
98
|
+
alpha.nil? ? hsv : hsv + [alpha * 255]
|
91
99
|
end
|
92
100
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
alpha.nil? ? rgb : rgb + [alpha]
|
97
|
-
end
|
101
|
+
alias_method :rgb_to_hsb, :rgb_to_hsv
|
102
|
+
|
103
|
+
#========= HSL/HSV/HSB to RGB(A) =======================================
|
98
104
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
105
|
+
def hsl_to_rgb(hsl_arr)
|
106
|
+
hue, saturation, lightness, alpha = hsl_arr.map(&:to_f)
|
107
|
+
# scaling values into range 0..1
|
108
|
+
saturation /= 100
|
109
|
+
lightness /= 100
|
104
110
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
111
|
+
# calculation intermediate values
|
112
|
+
a = saturation * [lightness, 1 - lightness].min
|
113
|
+
|
114
|
+
# calculation rgb & scaling into range 0..255
|
115
|
+
rgb = [0, 8, 4]
|
116
|
+
rgb.map! do |channel|
|
117
|
+
k = (channel + hue / 30) % 12
|
118
|
+
channel = lightness - a * [-1, [k - 3, 9 - k, 1].min].max
|
119
|
+
(channel * 255).round
|
120
|
+
end
|
121
|
+
alpha.nil? ? rgb : rgb + [alpha]
|
109
122
|
end
|
110
123
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
124
|
+
def hsv_to_rgb(hsv_arr)
|
125
|
+
hue, saturation, value, alpha = hsv_arr.map(&:to_f)
|
126
|
+
# scaling values into range 0..1
|
127
|
+
saturation /= 100
|
128
|
+
value /= 100
|
129
|
+
|
130
|
+
# calculation rgb & scaling into range 0..255
|
131
|
+
rgb = [5, 3, 1]
|
132
|
+
rgb.map! do |channel|
|
133
|
+
k = (channel + hue / 60) % 6
|
134
|
+
channel = value - value * saturation * [0, [k, 4 - k, 1].min].max
|
135
|
+
(channel * 255).round
|
136
|
+
end
|
137
|
+
alpha.nil? ? rgb : rgb + [alpha]
|
138
|
+
end
|
116
139
|
|
117
|
-
|
140
|
+
alias_method :hsb_to_rgb, :hsv_to_rgb
|
118
141
|
|
119
|
-
|
142
|
+
#========= Alternative implementation HSL/HSV/HSB to RGB(A) ============
|
120
143
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
144
|
+
def hsl_to_rgb_alt(hsl_arr)
|
145
|
+
hue, saturation, lightness, alpha = hsl_arr.map(&:to_f)
|
146
|
+
# scaling values into range 0..1
|
147
|
+
saturation /= 100
|
148
|
+
lightness /= 100
|
126
149
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
150
|
+
# calculation chroma & intermediate values
|
151
|
+
hue = (hue % 360) / 60
|
152
|
+
chroma = (1 - (2 * lightness - 1).abs) * saturation
|
153
|
+
x = chroma * (1 - (hue % 2 - 1).abs)
|
154
|
+
point = get_rgb_point(hue, chroma, x)
|
131
155
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
156
|
+
# calculation rgb & scaling into range 0..255
|
157
|
+
m = lightness - chroma / 2
|
158
|
+
rgb = point.map { |channel| ((channel + m) * 255).round }
|
159
|
+
alpha.nil? ? rgb : rgb + [alpha]
|
160
|
+
end
|
161
|
+
|
162
|
+
def hsv_to_rgb_alt(hsv_arr)
|
163
|
+
hue, saturation, value, alpha = hsv_arr.map(&:to_f)
|
164
|
+
# scaling values into range 0..1
|
165
|
+
saturation /= 100
|
166
|
+
value /= 100
|
167
|
+
|
168
|
+
# calculation chroma & intermediate values
|
169
|
+
hue = (hue % 360) / 60
|
170
|
+
chroma = value * saturation
|
171
|
+
x = chroma * (1 - (hue % 2 - 1).abs)
|
172
|
+
point = get_rgb_point(hue, chroma, x)
|
173
|
+
|
174
|
+
# calculation rgb & scaling into range 0..255
|
175
|
+
m = value - chroma
|
176
|
+
rgb = point.map { |channel| ((channel + m) * 255).round }
|
177
|
+
alpha.nil? ? rgb : rgb + [alpha]
|
178
|
+
end
|
149
179
|
|
150
|
-
|
151
|
-
hue, saturation, value, alpha = hsv_arr.map(&:to_f)
|
152
|
-
# scaling values into range 0..1
|
153
|
-
saturation /= 100
|
154
|
-
value /= 100
|
180
|
+
alias_method :hsb_to_rgb_alt, :hsv_to_rgb_alt
|
155
181
|
|
156
|
-
|
157
|
-
chroma = value * saturation
|
158
|
-
hue /= 60
|
159
|
-
x = chroma * (1 - (hue % 2 - 1).abs)
|
182
|
+
#========= RGB <==> HSI ================================================
|
160
183
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
[0, x, chroma],
|
166
|
-
[x, 0, chroma],
|
167
|
-
[chroma, 0, x]]
|
168
|
-
# point selection based on entering HUE input in range
|
169
|
-
point = points.each_with_index.detect { |rgb_, n| (n * (1 / 100.000)...n + 1).include? hue }&.first
|
170
|
-
# if point == nil (hue undefined)
|
171
|
-
rgb = point || [0, 0, 0]
|
172
|
-
|
173
|
-
# calculation rgb & scaling into range 0..255
|
174
|
-
m = value - chroma
|
175
|
-
rgb.map! { |channel| ((channel + m) * 255).round }
|
176
|
-
alpha.nil? ? rgb : rgb + [alpha]
|
177
|
-
end
|
184
|
+
def rgb_to_hsi(rgb_arr, rounding = hsx_round)
|
185
|
+
# scaling RGB values into range 0..1
|
186
|
+
rgb = rgb_arr[0..2].map { |color| color / 255.to_f }
|
187
|
+
alpha = rgb_arr[3]
|
178
188
|
|
179
|
-
|
189
|
+
# calculation HSI values
|
190
|
+
hue = get_hue(*rgb)
|
191
|
+
intensity = rgb.sum / 3
|
192
|
+
saturation = intensity.zero? ? 0 : 1 - rgb.min / intensity
|
180
193
|
|
181
|
-
|
194
|
+
# scaling values to fill 0..100 interval
|
195
|
+
saturation *= 100
|
196
|
+
intensity *= 100
|
182
197
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
lightness /= 100
|
198
|
+
# rounding, drop Alpha if not set (nil)
|
199
|
+
hsi = [hue, saturation, intensity].map { |x| x.round(rounding) }
|
200
|
+
alpha.nil? ? hsi : hsi + [alpha]
|
201
|
+
end
|
188
202
|
|
189
|
-
|
190
|
-
|
191
|
-
|
203
|
+
def hsi_to_rgb(hsi_arr)
|
204
|
+
hue, saturation, intensity, alpha = hsi_arr.map(&:to_f)
|
205
|
+
# scaling values into range 0..1
|
206
|
+
saturation /= 100
|
207
|
+
intensity /= 100
|
208
|
+
|
209
|
+
# calculation chroma & intermediate values
|
210
|
+
#
|
211
|
+
# as 360/60 does not get_rgb_point in any of the ranges 0...1 or 5...6
|
212
|
+
# so in the method we use (hue % 360)
|
213
|
+
# at the same time solving if hue is not in the 0..360 range
|
214
|
+
hue = (hue % 360) / 60
|
215
|
+
z = 1 - (hue % 2 - 1).abs
|
216
|
+
chroma = (3 * intensity * saturation) / (1 + z)
|
217
|
+
x = chroma * z
|
218
|
+
point = get_rgb_point(hue, chroma, x)
|
219
|
+
|
220
|
+
# calculation rgb
|
221
|
+
m = intensity * (1 - saturation)
|
222
|
+
rgb = point.map { |channel| channel + m }
|
223
|
+
|
224
|
+
# checking rgb on overrange 0..1
|
225
|
+
rgb = fix_overrange_rgb(rgb)
|
226
|
+
# scaling into range 0..255 & rounding
|
227
|
+
rgb.map! { |channel| (channel * 255).round }
|
228
|
+
|
229
|
+
alpha.nil? ? rgb : rgb + [alpha]
|
230
|
+
end
|
192
231
|
|
193
|
-
|
194
|
-
hsv = [hue, saturation_hsv * 100, value * 100].map { |x| x.round(rounding) }
|
195
|
-
alpha.nil? ? hsv : hsv + [alpha]
|
196
|
-
end
|
232
|
+
#========= HSL <==> HSV (HSB) ==========================================
|
197
233
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
saturation /= 100
|
204
|
-
value /= 100
|
205
|
-
|
206
|
-
# calculation lightness & saturation HSL
|
207
|
-
lightness = value * (1 - saturation / 2)
|
208
|
-
saturation_hsl = if [0, 1].any? { |v| v == lightness }
|
209
|
-
0
|
210
|
-
else
|
211
|
-
(value - lightness) / [lightness, 1 - lightness].min
|
212
|
-
end
|
213
|
-
|
214
|
-
# scaling HSL values & rounding
|
215
|
-
hsl = [hue, saturation_hsl * 100, lightness * 100].map { |x| x.round(rounding) }
|
216
|
-
alpha.nil? ? hsl : hsl + [alpha]
|
217
|
-
end
|
234
|
+
def hsl_to_hsv(hsl_arr, rounding = hsx_round)
|
235
|
+
hue, saturation, lightness, alpha = hsl_arr.map(&:to_f)
|
236
|
+
# scaling values into range 0..1
|
237
|
+
saturation /= 100
|
238
|
+
lightness /= 100
|
218
239
|
|
219
|
-
|
240
|
+
# calculation value & saturation HSV
|
241
|
+
value = lightness + saturation * [lightness, 1 - lightness].min
|
242
|
+
saturation_hsv = lightness == 0 ? 0 : 2 * (1 - lightness / value)
|
220
243
|
|
221
|
-
|
244
|
+
# scaling HSV values & rounding
|
245
|
+
hsv = [hue, saturation_hsv * 100, value * 100].map { |x| x.round(rounding) }
|
246
|
+
alpha.nil? ? hsv : hsv + [alpha]
|
247
|
+
end
|
222
248
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
249
|
+
alias_method :hsl_to_hsb, :hsl_to_hsv
|
250
|
+
|
251
|
+
def hsv_to_hsl(hsv_arr, rounding = hsx_round)
|
252
|
+
hue, saturation, value, alpha = hsv_arr.map(&:to_f)
|
253
|
+
# scaling values into range 0..1
|
254
|
+
saturation /= 100
|
255
|
+
value /= 100
|
256
|
+
|
257
|
+
# calculation lightness & saturation HSL
|
258
|
+
lightness = value * (1 - saturation / 2)
|
259
|
+
saturation_hsl = if [0, 1].any? { |v| v == lightness }
|
260
|
+
0
|
261
|
+
else
|
262
|
+
(value - lightness) / [lightness, 1 - lightness].min
|
263
|
+
end
|
264
|
+
|
265
|
+
# scaling HSL values & rounding
|
266
|
+
hsl = [hue, saturation_hsl * 100, lightness * 100].map { |x| x.round(rounding) }
|
267
|
+
alpha.nil? ? hsl : hsl + [alpha]
|
229
268
|
end
|
230
269
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
270
|
+
alias_method :hsb_to_hsl, :hsv_to_hsl
|
271
|
+
|
272
|
+
#========= RGB(A) <==> CMYK ============================================
|
273
|
+
|
274
|
+
def rgb_to_cmyk(rgb_arr, rounding = hsx_round)
|
275
|
+
# scaling RGB values into range 0..1
|
276
|
+
rgb = rgb_arr[0..2].map { |color| color / 255.to_f }
|
277
|
+
k = 1 - rgb.max
|
278
|
+
converter = proc do |color|
|
279
|
+
(1 - k) == 0 ? 0 : (1 - color - k) / (1 - k)
|
280
|
+
end
|
236
281
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
282
|
+
# calculation CMYK & scaling into percentages & rounding
|
283
|
+
c, m, y = rgb.map { |color| converter.call(color) || 0 }
|
284
|
+
cmyk = [c, m, y, k].map { |x| (x * 100).round(rounding) }
|
285
|
+
rgb_arr.size == 4 ? cmyk + [rgb_arr.last] : cmyk
|
241
286
|
end
|
242
287
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
288
|
+
def cmyk_to_rgb(cmyk_arr)
|
289
|
+
c, m, y, k = cmyk_arr[0..3].map { |color| color / 100.to_f }
|
290
|
+
converter = proc do |channel|
|
291
|
+
255 * (1 - channel) * (1 - k)
|
292
|
+
end
|
247
293
|
|
248
|
-
|
294
|
+
# calculation RGB & rounding
|
295
|
+
rgb = [c, m, y].map { |channel| converter.call(channel).round }
|
296
|
+
cmyk_arr.size == 5 ? rgb + [cmyk_arr.last] : rgb
|
297
|
+
end
|
249
298
|
|
250
|
-
|
299
|
+
#========= HEX <==> HSL/HSV/HSB/HSI ========================================
|
251
300
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
# calculation chroma
|
257
|
-
chroma = cmax - cmin
|
301
|
+
def hex_to_hsl(hex, rounding = hsx_round, alpha_255: false)
|
302
|
+
rgb = hex_to_rgb(hex, alpha_255: alpha_255)
|
303
|
+
rgb_to_hsl(rgb, rounding)
|
304
|
+
end
|
258
305
|
|
259
|
-
|
260
|
-
|
306
|
+
def hsl_to_hex(hsl_arr, alpha_255: false)
|
307
|
+
rgb = hsl_to_rgb(hsl_arr)
|
308
|
+
rgb_to_hex(rgb, alpha_255: alpha_255)
|
309
|
+
end
|
310
|
+
|
311
|
+
def hex_to_hsv(hex, rounding = hsx_round, alpha_255: false)
|
312
|
+
rgb = hex_to_rgb(hex, alpha_255: alpha_255)
|
313
|
+
rgb_to_hsv(rgb, rounding)
|
314
|
+
end
|
315
|
+
|
316
|
+
def hsv_to_hex(hsv_arr, alpha_255: false)
|
317
|
+
rgb = hsv_to_rgb(hsv_arr)
|
318
|
+
rgb_to_hex(rgb, alpha_255: alpha_255)
|
319
|
+
end
|
320
|
+
|
321
|
+
alias_method :hex_to_hsb, :hex_to_hsv
|
322
|
+
alias_method :hsb_to_hex, :hsv_to_hex
|
323
|
+
|
324
|
+
def hex_to_hsi(hex, rounding = hsx_round, alpha_255: false)
|
325
|
+
rgb = hex_to_rgb(hex, alpha_255: alpha_255)
|
326
|
+
rgb_to_hsi(rgb, rounding)
|
327
|
+
end
|
328
|
+
|
329
|
+
def hsi_to_hex(hsi_arr, alpha_255: false)
|
330
|
+
rgb = hsi_to_rgb(hsi_arr)
|
331
|
+
rgb_to_hex(rgb, alpha_255: alpha_255)
|
332
|
+
end
|
333
|
+
|
334
|
+
def hex_to_cmyk(hex, rounding = hsx_round, alpha_255: false)
|
335
|
+
rgb = hex_to_rgb(hex, alpha_255: alpha_255)
|
336
|
+
rgb_to_cmyk(rgb, rounding)
|
337
|
+
end
|
261
338
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
339
|
+
def cmyk_to_hex(cmyk_arr, alpha_255: false)
|
340
|
+
rgb = cmyk_to_rgb(cmyk_arr)
|
341
|
+
rgb_to_hex(rgb, alpha_255: alpha_255)
|
342
|
+
end
|
343
|
+
|
344
|
+
|
345
|
+
private
|
346
|
+
|
347
|
+
#========= helper methods ==============================================
|
348
|
+
|
349
|
+
# find greatest and smallest channel values and chroma from RGB
|
350
|
+
def get_min_max_chroma(red, green, blue)
|
351
|
+
cmin = [red, green, blue].min
|
352
|
+
cmax = [red, green, blue].max
|
353
|
+
# calculation chroma
|
354
|
+
chroma = cmax - cmin
|
355
|
+
|
356
|
+
[cmin, cmax, chroma]
|
357
|
+
end
|
358
|
+
|
359
|
+
# calculation HUE from RGB
|
360
|
+
def get_hue(red, green, blue)
|
361
|
+
_cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
|
362
|
+
|
363
|
+
hue = if chroma == 0
|
364
|
+
0
|
365
|
+
elsif cmax == red
|
366
|
+
# red is max
|
367
|
+
((green - blue) / chroma) % 6
|
368
|
+
elsif cmax == green
|
369
|
+
# green is max
|
370
|
+
(blue - red) / chroma + 2
|
371
|
+
else
|
372
|
+
# blue is max
|
373
|
+
(red - green) / chroma + 4
|
374
|
+
end
|
375
|
+
hue * 60
|
376
|
+
|
377
|
+
# HUE will never leave the 0..360 range when RGB is within 0..255
|
378
|
+
# make negative HUEs positive
|
379
|
+
# 0 <= hue ? hue : hue + 360
|
380
|
+
end
|
381
|
+
|
382
|
+
# possible RGB points
|
383
|
+
# point selection based on entering HUE input in range
|
384
|
+
def get_rgb_point(hue, chroma, x)
|
385
|
+
case hue
|
386
|
+
when 0...1 then [chroma, x, 0]
|
387
|
+
when 1...2 then [x, chroma, 0]
|
388
|
+
when 2...3 then [0, chroma, x]
|
389
|
+
when 3...4 then [0, x, chroma]
|
390
|
+
when 4...5 then [x, 0, chroma]
|
391
|
+
when 5...6 then [chroma, 0, x]
|
392
|
+
# HUE will never leave the 0..359 range because we use (hue % 360)
|
393
|
+
# else [0, 0, 0]
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
# checking rgb on overrange 0..1
|
398
|
+
def fix_overrange_rgb(rgb)
|
399
|
+
max = rgb.max
|
400
|
+
# so we keep HUE
|
401
|
+
# if we had just used clipping [[value, 255].min, 0].max
|
402
|
+
# we would have changed HUE
|
403
|
+
#
|
404
|
+
# Thx to Rotem & Giacomo Catenazzi from stackoverflow
|
405
|
+
max > 1 ? rgb.map { |channel| channel / max } : rgb
|
406
|
+
end
|
281
407
|
end
|
408
|
+
|
409
|
+
extend ClassMethods
|
282
410
|
end
|
data/lib/decolmor/version.rb
CHANGED
data/lib/decolmor.rb
CHANGED
@@ -2,18 +2,4 @@ require 'decolmor/main'
|
|
2
2
|
require 'decolmor/version'
|
3
3
|
|
4
4
|
module Decolmor
|
5
|
-
#========= Set default rounding for HSL/HSV/HSB/CMYK conversion ========
|
6
|
-
|
7
|
-
# round 1 enough for lossless conversion RGB -> HSL/HSV/HSB -> RGB
|
8
|
-
# for lossless conversion HSL <==> HSV (HSB) better to use round 2
|
9
|
-
#
|
10
|
-
HSX_ROUND = 1
|
11
|
-
|
12
|
-
class << self
|
13
|
-
attr_writer :hsx_round
|
14
|
-
|
15
|
-
def hsx_round
|
16
|
-
@hsx_round ||= HSX_ROUND
|
17
|
-
end
|
18
|
-
end
|
19
5
|
end
|