decolmor 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/decolmor/main.rb CHANGED
@@ -1,282 +1,410 @@
1
1
  module Decolmor
2
2
 
3
- #========= HEX <==> RGB(A) =============================================
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
- def self.rgb_to_hex(rgb)
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
- # simple generator RGB, you can set any channel(s)
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
- #========= RGB(A) to HSL/HSV/HSB =======================================
16
+ attr_writer :hsx_round
32
17
 
33
- def self.rgb_to_hsl(rgb_arr, rounding = hsx_round)
34
- # scaling RGB values into range 0..1
35
- red, green, blue, alpha = rgb_arr.map { |color| color / 255.to_f }
18
+ def hsx_round
19
+ @hsx_round ||= HSX_ROUND
20
+ end
36
21
 
37
- # calculation intermediate values
38
- cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
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
- # calculation HSL values
41
- hue = get_hue(red, green, blue)
42
- lightness = (cmax + cmin) / 2
43
- saturation = chroma == 0 ? 0 : chroma / (1 - (2 * lightness - 1).abs)
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
- # scaling values to fill 0..100 interval
46
- saturation *= 100
47
- lightness *= 100
48
+ #=======================================================================
48
49
 
49
- # rounding, drop Alpha if not set (nil)
50
- hsl = [hue, saturation, lightness].map { |x| x.round(rounding) }
51
- alpha.nil? ? hsl : hsl + [alpha * 255]
52
- end
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
- def self.rgb_to_hsv(rgb_arr, rounding = hsx_round)
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
- # calculation intermediate values
59
- _cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
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
- # calculation HSV values
62
- hue = get_hue(red, green, blue)
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
- # scaling values into range 0..100
67
- saturation *= 100
68
- value *= 100
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
- # rounding
71
- hsv = [hue, saturation, value].map { |x| x.round(rounding) }
72
- alpha.nil? ? hsv : hsv + [alpha * 255]
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
- self.singleton_class.send(:alias_method, :rgb_to_hsb, :rgb_to_hsv)
84
+ # calculation intermediate values
85
+ _cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
77
86
 
78
- #========= HSL/HSV/HSB to RGB(A) =======================================
87
+ # calculation HSV values
88
+ hue = get_hue(red, green, blue)
89
+ saturation = chroma == 0 ? 0 : chroma / cmax
90
+ value = cmax
79
91
 
80
- def self.hsl_to_rgb(hsl_arr)
81
- hue, saturation, lightness, alpha = hsl_arr.map(&:to_f)
82
- # scaling values into range 0..1
83
- saturation /= 100
84
- lightness /= 100
92
+ # scaling values into range 0..100
93
+ saturation *= 100
94
+ value *= 100
85
95
 
86
- # calculation intermediate values
87
- a = saturation * [lightness, 1 - lightness].min
88
- converter = proc do |n|
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
- # calculation rgb & scaling into range 0..255
94
- rgb = [0, 8, 4]
95
- rgb.map! { |channel| (converter.call(channel) * 255).round }
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
- def self.hsv_to_rgb(hsv_arr)
100
- hue, saturation, value, alpha = hsv_arr.map(&:to_f)
101
- # scaling values into range 0..1
102
- saturation /= 100
103
- value /= 100
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
- # calculation intermediate values
106
- converter = proc do |n|
107
- k = (n + hue / 60) % 6
108
- value - value * saturation * [0, [k, 4 - k, 1].min].max
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
- # calculation rgb & scaling into range 0..255
112
- rgb = [5, 3, 1]
113
- rgb.map! { |channel| (converter.call(channel) * 255).round }
114
- alpha.nil? ? rgb : rgb + [alpha]
115
- end
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
- self.singleton_class.send(:alias_method, :hsb_to_rgb, :hsv_to_rgb)
140
+ alias_method :hsb_to_rgb, :hsv_to_rgb
118
141
 
119
- #========= Alternative implementation HSL/HSV/HSB to RGB(A) ============
142
+ #========= Alternative implementation HSL/HSV/HSB to RGB(A) ============
120
143
 
121
- def self.hsl_to_rgb_alt(hsl_arr)
122
- hue, saturation, lightness, alpha = hsl_arr.map(&:to_f)
123
- # scaling values into range 0..1
124
- saturation /= 100
125
- lightness /= 100
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
- # calculation chroma & intermediate values
128
- chroma = (1 - (2 * lightness - 1).abs) * saturation
129
- hue /= 60
130
- x = chroma * (1 - (hue % 2 - 1).abs)
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
- # possible RGB points
133
- points = [[chroma, x, 0],
134
- [x, chroma, 0],
135
- [0, chroma, x],
136
- [0, x, chroma],
137
- [x, 0, chroma],
138
- [chroma, 0, x]]
139
- # point selection based on entering HUE input in range
140
- point = points.each_with_index.detect { |rgb_, n| (n..n + 1).include? hue }&.first
141
- # if point == nil (hue undefined)
142
- rgb = point || [0, 0, 0]
143
-
144
- # calculation rgb & scaling into range 0..255
145
- m = lightness - chroma / 2
146
- rgb.map! { |channel| ((channel + m) * 255).round }
147
- alpha.nil? ? rgb : rgb + [alpha]
148
- end
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
- def self.hsv_to_rgb_alt(hsv_arr)
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
- # calculation chroma & intermediate values
157
- chroma = value * saturation
158
- hue /= 60
159
- x = chroma * (1 - (hue % 2 - 1).abs)
182
+ #========= RGB <==> HSI ================================================
160
183
 
161
- # possible RGB points
162
- points = [[chroma, x, 0],
163
- [x, chroma, 0],
164
- [0, chroma, x],
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
- self.singleton_class.send(:alias_method, :hsb_to_rgb_alt, :hsv_to_rgb_alt)
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
- #========= HSL <==> HSV (HSB) ==========================================
194
+ # scaling values to fill 0..100 interval
195
+ saturation *= 100
196
+ intensity *= 100
182
197
 
183
- def self.hsl_to_hsv(hsl_arr, rounding = hsx_round)
184
- hue, saturation, lightness, alpha = hsl_arr.map(&:to_f)
185
- # scaling values into range 0..1
186
- saturation /= 100
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
- # calculation value & saturation HSV
190
- value = lightness + saturation * [lightness, 1 - lightness].min
191
- saturation_hsv = lightness == 0 ? 0 : 2 * (1 - lightness / value)
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
- # scaling HSV values & rounding
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
- self.singleton_class.send(:alias_method, :hsl_to_hsb, :hsl_to_hsv)
199
-
200
- def self.hsv_to_hsl(hsv_arr, rounding = hsx_round)
201
- hue, saturation, value, alpha = hsv_arr.map(&:to_f)
202
- # scaling values into range 0..1
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
- self.singleton_class.send(:alias_method, :hsb_to_hsl, :hsv_to_hsl)
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
- #========= RGB(A) <==> CMYK ============================================
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
- def self.rgb_to_cmyk(rgb_arr, rounding = hsx_round)
224
- # scaling RGB values into range 0..1
225
- rgb = rgb_arr[0..2].map { |color| color / 255.to_f }
226
- k = 1 - rgb.max
227
- converter = proc do |color|
228
- (1 - k) == 0 ? 0 : (1 - color - k) / (1 - k)
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
- # calculation CMYK & scaling into percentages & rounding
232
- c, m, y = rgb.map { |color| converter.call(color) || 0 }
233
- cmyk = [c, m, y, k].map { |x| (x * 100).round(rounding) }
234
- rgb_arr.size == 4 ? cmyk + [rgb_arr.last] : cmyk
235
- end
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
- def self.cmyk_to_rgb(cmyk_arr)
238
- c, m, y, k = cmyk_arr[0..3].map { |color| color / 100.to_f }
239
- converter = proc do |channel|
240
- 255 * (1 - channel) * (1 - k)
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
- # calculation RGB & rounding
244
- rgb = [c, m, y].map { |channel| converter.call(channel).round }
245
- cmyk_arr.size == 5 ? rgb + [cmyk_arr.last] : rgb
246
- end
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
- private
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
- #========= helper methods for RGB to HSL/HSB/HSV =======================
299
+ #========= HEX <==> HSL/HSV/HSB/HSI ========================================
251
300
 
252
- # find greatest and smallest channel values and chroma from RGB
253
- def self.get_min_max_chroma(red, green, blue)
254
- cmin = [red, green, blue].min
255
- cmax = [red, green, blue].max
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
- [cmin, cmax, chroma]
260
- end
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
- # calculation HUE from RGB
263
- def self.get_hue(red, green, blue)
264
- _cmin, cmax, chroma = get_min_max_chroma(red, green, blue)
265
-
266
- hue = if chroma == 0
267
- 0
268
- elsif cmax == red
269
- # red is max
270
- ((green - blue) / chroma) % 6
271
- elsif cmax == green
272
- # green is max
273
- (blue - red) / chroma + 2
274
- else
275
- # blue is max
276
- (red - green) / chroma + 4
277
- end
278
- hue *= 60
279
- # make negative HUEs positive behind 360°
280
- 0 <= hue ? hue : hue + 360
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Decolmor
4
- VERSION = '1.1.0'
4
+ VERSION = '1.3.0'
5
5
  end
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