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.
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