71
71
# -----------------
72
72
73
73
correct_gamut (c:: CV ) where {CV<: AbstractRGB } = CV (clamp01 (red (c)), clamp01 (green (c)), clamp01 (blue (c)))
74
- clamp01 (v:: T ) where {T<: Fractional } = ifelse (v < zero (T), zero (T), ifelse (v > one (T), one (T), v))
74
+ correct_gamut (c:: CV ) where {T<: Union{N0f8,N0f16,N0f32,N0f64} ,
75
+ CV<: Union{AbstractRGB{T},TransparentRGB{T}} } = c
75
76
76
77
function srgb_compand (v:: Fractional )
77
78
# the following is an optimization technique for `1.055v^(1/2.4) - 0.055`.
81
82
82
83
cnvt (:: Type{CV} , c:: AbstractRGB ) where {CV<: AbstractRGB } = CV (red (c), green (c), blue (c))
83
84
84
- function cnvt (:: Type{CV} , c:: HSV ) where CV<: AbstractRGB
85
- h = c. h / 60
86
- i = floor (Int, h)
87
- f = h - i
88
- if i & 1 == 0
89
- f = 1 - f
90
- end
91
- m = c. v * (1 - c. s)
92
- n = c. v * (1 - c. s * f)
93
- if i == 6 || i == 0 ; CV (c. v, n, m)
94
- elseif i == 1 ; CV (n, c. v, m)
95
- elseif i == 2 ; CV (m, c. v, n)
96
- elseif i == 3 ; CV (m, n, c. v)
97
- elseif i == 4 ; CV (n, m, c. v)
98
- else ; CV (c. v, m, n)
99
- end
100
- end
101
-
102
- function qtrans (u, v, hue)
103
- hue = normalize_hue (hue)
104
-
105
- if hue < 60 ; u + (v - u) * hue / 60
106
- elseif hue < 180 ; v
107
- elseif hue < 240 ; u + (v - u) * (240 - hue) / 60
108
- else ; u
109
- end
110
- end
111
-
112
- function cnvt (:: Type{CV} , c:: HSL ) where CV<: AbstractRGB
113
- v = c. l <= 0.5 ? c. l * (1 + c. s) : c. l + c. s - (c. l * c. s)
114
- u = 2 * c. l - v
115
-
116
- if c. s == 0 ; CV (c. l, c. l, c. l)
117
- else ; CV (qtrans (u, v, c. h + 120 ),
118
- qtrans (u, v, c. h),
119
- qtrans (u, v, c. h - 120 ))
120
- end
121
- end
122
85
123
- function cnvt (:: Type{CV} , c:: HSI ) where CV<: AbstractRGB
124
- h, s, i = normalize_hue (c. h), c. s, c. i
125
- is = i* s
126
- if h < 120
127
- cosr = cosd (h) / cosd (60 - h)
128
- CV (i+ is* cosr, i+ is* (1 - cosr), i- is)
129
- elseif h < 240
130
- cosr = cosd (h- 120 ) / cosd (180 - h)
131
- CV (i- is, i+ is* cosr, i+ is* (1 - cosr))
86
+ function _hsx_to_rgb (im:: UInt8 , v, n, m)
87
+ #=
88
+ if hue < 60; im = 0b000001 # ---------+
89
+ elseif hue < 120; im = 0b000010 # --------+|
90
+ elseif hue < 180; im = 0b000100 # -------+||
91
+ elseif hue < 240; im = 0b001000 # ------+|||
92
+ elseif hue < 300; im = 0b010000 # -----+||||
93
+ else ; im = 0b100000 # ----+|||||
94
+ end # ||||||
95
+ (hue < 60 || hue >= 300) === ((im & 0b100001) != 0x0)
96
+ =#
97
+ r = ifelse ((im & 0b100001 ) == 0x0 , ifelse ((im & 0b010010 ) == 0x0 , m, n), v)
98
+ g = ifelse ((im & 0b000110 ) == 0x0 , ifelse ((im & 0b001001 ) == 0x0 , m, n), v)
99
+ b = ifelse ((im & 0b011000 ) == 0x0 , ifelse ((im & 0b100100 ) == 0x0 , m, n), v)
100
+ return (r, g, b)
101
+ end
102
+ function _hsx_to_rgb (im:: UInt8 , v:: T , n:: T , m:: T ) where T <: Union{Float16, Float32, Float64}
103
+ vu, nu, mu = reinterpret .(Unsigned, (v, n, m)) # prompt the compiler to use conditional moves
104
+ r = ifelse ((im & 0b100001 ) == 0x0 , ifelse ((im & 0b010010 ) == 0x0 , mu, nu), vu)
105
+ g = ifelse ((im & 0b000110 ) == 0x0 , ifelse ((im & 0b001001 ) == 0x0 , mu, nu), vu)
106
+ b = ifelse ((im & 0b011000 ) == 0x0 , ifelse ((im & 0b100100 ) == 0x0 , mu, nu), vu)
107
+ return reinterpret .(T, (r, g, b))
108
+ end
109
+
110
+ function cnvt (:: Type{CV} , c:: HSV ) where {T, CV<: AbstractRGB{T} }
111
+ F = promote_type (T, eltype (c))
112
+ h, s, v = div60 (F (c. h)), clamp01 (F (c. s)), clamp01 (F (c. v))
113
+ hi = unsafe_trunc (Int32, h) # instead of floor
114
+ i = h < 0 ? hi - one (hi) : hi
115
+ f = i & one (i) == zero (i) ? 1 - (h - i) : h - i
116
+ im = 0x1 << (mod6 (UInt8, i) & 0x07 )
117
+ # use `@fastmath` just to reduce the estimated costs for inlining
118
+ @fastmath m = v * (1 - s)
119
+ @fastmath n = v * (1 - s * f)
120
+
121
+ r, g, b = _hsx_to_rgb (im, v, n, m)
122
+ T <: FixedPoint && typemax (T) >= 1 ? CV (r % T, g % T, b % T) : CV (r, g, b)
123
+ end
124
+
125
+ function cnvt (:: Type{CV} , c:: HSL ) where {T, CV<: AbstractRGB{T} }
126
+ F = promote_type (T, eltype (c))
127
+ h, s, l = div60 (F (c. h)), clamp01 (F (c. s)), clamp01 (F (c. l))
128
+ a = @fastmath min (l, 1 - l) * s
129
+ v = l + a
130
+ hi = unsafe_trunc (Int32, h) # instead of floor
131
+ i = h < 0 ? hi - one (hi) : hi
132
+ f = i & one (i) == zero (i) ? 1 - (h - i) : h - i
133
+ im = 0x1 << (mod6 (UInt8, i) & 0x07 )
134
+ # use `@fastmath` just to reduce the estimated costs for inlining
135
+ @fastmath m = l - a # v - 2 * a
136
+ @fastmath n = v - 2 * a * f
137
+
138
+ r, g, b = _hsx_to_rgb (im, v, n, m)
139
+ T <: FixedPoint && typemax (T) >= 1 ? CV (r % T, g % T, b % T) : CV (r, g, b)
140
+ end
141
+
142
+ function cnvt (:: Type{CV} , c:: HSI ) where {T, CV<: AbstractRGB{T} }
143
+ F = promote_type (T, eltype (c))
144
+ h, s, i = deg2rad (normalize_hue (F (c. h))), clamp01 (F (c. s)), clamp01 (F (c. i))
145
+ is = i * s
146
+ if h < F (2 π/ 3 )
147
+ @fastmath cosr = cos (h) / cos (F (π/ 3 )- h)
148
+ r0, g0, b0 = muladd (is, cosr, i), muladd (is, 1 - cosr, i), i - is
149
+ elseif h < F (4 π/ 3 )
150
+ @fastmath cosr = cos (h- F (2 π/ 3 )) / cos (F (π)- h)
151
+ r0, g0, b0 = i - is, muladd (is, cosr, i), muladd (is, 1 - cosr, i)
132
152
else
133
- cosr = cosd (h- 240 ) / cosd ( 300 - h)
134
- CV (i + is * ( 1 - cosr), i- is, i + is * cosr)
153
+ @fastmath cosr = cos (h- F ( 4 π / 3 )) / cos ( F ( 5 π / 3 ) - h)
154
+ r0, g0, b0 = muladd (is, 1 - cosr, i ), i - is, muladd (is, cosr, i )
135
155
end
156
+ r, g, b = min (r0, oneunit (F)), min (g0, oneunit (F)), min (b0, oneunit (F))
157
+ T <: FixedPoint && typemax (T) >= 1 ? CV (r % T, g % T, b % T) : CV (r, g, b)
136
158
end
137
159
138
160
function cnvt (:: Type{CV} , c:: XYZ ) where CV<: AbstractRGB
@@ -175,26 +197,20 @@ end
175
197
# -----------------
176
198
177
199
function cnvt (:: Type{HSV{T}} , c:: AbstractRGB ) where T
178
- c_min = Float64 (min (red (c), green (c), blue (c)))
179
- c_max = Float64 (max (red (c), green (c), blue (c)))
180
- if c_min == c_max
181
- return HSV {T} (zero (T), zero (T), c_max)
182
- end
200
+ F = promote_type (T, eltype (c))
201
+ r, g, b = F .((red (c), green (c), blue (c)))
202
+ c_min = @fastmath min (min (r, g), b)
203
+ c_max = @fastmath max (max (r, g), b)
204
+ s0 = c_max - c_min
205
+ s0 == zero (F) && return HSV {T} (zero (T), zero (T), T (c_max))
206
+ s = @fastmath s0 / c_max
183
207
184
- if c_min == red (c)
185
- f = Float64 (green (c)) - Float64 (blue (c))
186
- i = 3
187
- elseif c_min == green (c)
188
- f = Float64 (blue (c)) - Float64 (red (c))
189
- i = 5
190
- else
191
- f = Float64 (red (c)) - Float64 (green (c))
192
- i = 1
193
- end
208
+ # In general, it is dangerous to compare floating point numbers with `===`.
209
+ diff = ifelse (c_max === r, g - b, ifelse (c_max === g, b - r, r - g))
210
+ ofs = ifelse (c_max === r, (g < b)* F (360 ), ifelse (c_max === g, F (120 ), F (240 )))
211
+ h0 = @fastmath diff * F (60 ) / s0
194
212
195
- HSV {T} (60 * (i - f / (c_max - c_min)),
196
- (c_max - c_min) / c_max,
197
- c_max)
213
+ HSV {T} (h0 + ofs, s, c_max)
198
214
end
199
215
200
216
@@ -205,28 +221,22 @@ cnvt(::Type{HSV{T}}, c::Color3) where {T} = cnvt(HSV{T}, convert(RGB{T}, c))
205
221
# -----------------
206
222
207
223
function cnvt (:: Type{HSL{T}} , c:: AbstractRGB ) where T
208
- r, g, b = T (red (c)), T (green (c)), T (blue (c))
209
- c_min = min (r, g, b)
210
- c_max = max (r, g, b)
211
- l = (c_max + c_min) / 2
212
-
213
- if c_max == c_min
214
- return HSL (zero (T), zero (T), l)
215
- end
224
+ F = promote_type (T, eltype (c))
225
+ r, g, b = F (red (c)), F (green (c)), F (blue (c))
226
+ c_min = @fastmath min (min (r, g), b)
227
+ c_max = @fastmath max (max (r, g), b)
228
+ l0 = c_max + c_min
229
+ s0 = c_max - c_min
230
+ l = l0 * F (0.5 )
231
+ s0 == zero (F) && return HSL {T} (zero (T), zero (T), T (l))
232
+ s = @fastmath s0 / min (l0, F (2 ) - l0)
216
233
217
- if l < 0.5 ; s = (c_max - c_min) / (c_max + c_min)
218
- else ; s = (c_max - c_min) / (convert (T, 2 ) - c_max - c_min)
219
- end
234
+ # In general, it is dangerous to compare floating point numbers with `===`.
235
+ diff = ifelse (c_max === r, g - b, ifelse (c_max === g, b - r, r - g))
236
+ ofs = ifelse (c_max === r, (g < b)* F (360 ), ifelse (c_max === g, F (120 ), F (240 )))
237
+ h0 = @fastmath diff * F (60 ) / s0
220
238
221
- if c_max == red (c)
222
- h = (g - b) / (c_max - c_min)
223
- elseif c_max == green (c)
224
- h = convert (T, 2 ) + (b - r) / (c_max - c_min)
225
- else
226
- h = convert (T, 4 ) + (r - g) / (c_max - c_min)
227
- end
228
-
229
- HSL {T} (normalize_hue (h * 60 ), s, l)
239
+ HSL {T} (h0 + ofs, s, l)
230
240
end
231
241
232
242
@@ -236,22 +246,20 @@ cnvt(::Type{HSL{T}}, c::Color3) where {T} = cnvt(HSL{T}, convert(RGB{T}, c))
236
246
# Everything to HSI
237
247
# -----------------
238
248
239
- function cnvt (:: Type{HSI{T}} , c:: AbstractRGB ) where T
249
+ # Since acosd() is slow, the following is "inline-worthy".
250
+ @inline function cnvt (:: Type{HSI{T}} , c:: AbstractRGB ) where T
240
251
rgb = correct_gamut (c)
241
- r, g, b = float (red (rgb)), float (green (rgb)), float (blue (rgb))
242
- isum = r+ g+ b
243
- dnorm = sqrt (((r- g)^ 2 + (r- b)^ 2 + (g- b)^ 2 )/ 2 )
244
- dnorm = dnorm == 0 ? oftype (dnorm, 1 ) : dnorm
245
- i = isum/ 3
246
- m = min (r, g, b)
247
- s = i > 0 ? 1 - m/ i : zero (1 - m/ i)
248
- val = (r- (g+ b)/ 2 )/ dnorm
249
- val = clamp (val, - oneunit (val), oneunit (val))
250
- h = acosd (val)
251
- if b > g
252
- h = 360 - h
253
- end
254
- HSI {T} (h, s, i)
252
+ F = promote_type (T, eltype (c))
253
+ r, g, b = F (red (rgb)), F (green (rgb)), F (blue (rgb))
254
+ dnorm = @fastmath sqrt (((r- g)^ 2 + (r- b)^ 2 + (g- b)^ 2 ) * F (0.5 ))
255
+ isum = r + g + b
256
+ i = isum / F (3 )
257
+ dnorm == zero (F) && return HSI {T} (T (90 ), zero (T), T (i))
258
+ val = muladd (g + b, F (- 0.5 ), r) / dnorm
259
+ h = @fastmath acosd (clamp (val, - oneunit (F), oneunit (F)))
260
+ m = @fastmath min (min (r, g), b)
261
+ s = oneunit (F) - m/ i
262
+ HSI {T} (b > g ? F (360 ) - h : h, s, i)
255
263
end
256
264
257
265
cnvt (:: Type{HSI{T}} , c:: Color3 ) where {T} = cnvt (HSI{T}, convert (RGB{T}, c))
0 commit comments