Blender V4.5
math_color.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_color.h"
10#include "BLI_math_color.hh"
11#include "BLI_math_matrix.hh"
12#include "BLI_math_vector.hh"
13#include "BLI_simd.hh"
14#include "BLI_utildefines.h"
15
16#include <algorithm>
17#include <cstring>
18
19#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
20
21void hsv_to_rgb(float h, float s, float v, float *r_r, float *r_g, float *r_b)
22{
23 float nr, ng, nb;
24
25 nr = fabsf(h * 6.0f - 3.0f) - 1.0f;
26 ng = 2.0f - fabsf(h * 6.0f - 2.0f);
27 nb = 2.0f - fabsf(h * 6.0f - 4.0f);
28
29 CLAMP(nr, 0.0f, 1.0f);
30 CLAMP(nb, 0.0f, 1.0f);
31 CLAMP(ng, 0.0f, 1.0f);
32
33 *r_r = ((nr - 1.0f) * s + 1.0f) * v;
34 *r_g = ((ng - 1.0f) * s + 1.0f) * v;
35 *r_b = ((nb - 1.0f) * s + 1.0f) * v;
36}
37
38void hsl_to_rgb(float h, float s, float l, float *r_r, float *r_g, float *r_b)
39{
40 float nr, ng, nb, chroma;
41
42 nr = fabsf(h * 6.0f - 3.0f) - 1.0f;
43 ng = 2.0f - fabsf(h * 6.0f - 2.0f);
44 nb = 2.0f - fabsf(h * 6.0f - 4.0f);
45
46 CLAMP(nr, 0.0f, 1.0f);
47 CLAMP(nb, 0.0f, 1.0f);
48 CLAMP(ng, 0.0f, 1.0f);
49
50 chroma = (1.0f - fabsf(2.0f * l - 1.0f)) * s;
51
52 *r_r = (nr - 0.5f) * chroma + l;
53 *r_g = (ng - 0.5f) * chroma + l;
54 *r_b = (nb - 0.5f) * chroma + l;
55}
56
57void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
58{
59 hsv_to_rgb(hsv[0], hsv[1], hsv[2], &r_rgb[0], &r_rgb[1], &r_rgb[2]);
60}
61
62void hsl_to_rgb_v(const float hsl[3], float r_rgb[3])
63{
64 hsl_to_rgb(hsl[0], hsl[1], hsl[2], &r_rgb[0], &r_rgb[1], &r_rgb[2]);
65}
66
67void rgb_to_yuv(float r, float g, float b, float *r_y, float *r_u, float *r_v, int colorspace)
68{
69 float y, u, v;
70
71 switch (colorspace) {
73 y = 0.299f * r + 0.587f * g + 0.114f * b;
74 u = -0.147f * r - 0.289f * g + 0.436f * b;
75 v = 0.615f * r - 0.515f * g - 0.100f * b;
76 break;
78 default:
79 BLI_assert(colorspace == BLI_YUV_ITU_BT709);
80 y = 0.2126f * r + 0.7152f * g + 0.0722f * b;
81 u = -0.09991f * r - 0.33609f * g + 0.436f * b;
82 v = 0.615f * r - 0.55861f * g - 0.05639f * b;
83 break;
84 }
85
86 *r_y = y;
87 *r_u = u;
88 *r_v = v;
89}
90
91void yuv_to_rgb(float y, float u, float v, float *r_r, float *r_g, float *r_b, int colorspace)
92{
93 float r, g, b;
94
95 switch (colorspace) {
97 r = y + 1.140f * v;
98 g = y - 0.394f * u - 0.581f * v;
99 b = y + 2.032f * u;
100 break;
102 default:
103 BLI_assert(colorspace == BLI_YUV_ITU_BT709);
104 r = y + 1.28033f * v;
105 g = y - 0.21482f * u - 0.38059f * v;
106 b = y + 2.12798f * u;
107 break;
108 }
109
110 *r_r = r;
111 *r_g = g;
112 *r_b = b;
113}
114
115void rgb_to_ycc(float r, float g, float b, float *r_y, float *r_cb, float *r_cr, int colorspace)
116{
117 float sr, sg, sb;
118 float y = 128.0f, cr = 128.0f, cb = 128.0f;
119
120 sr = 255.0f * r;
121 sg = 255.0f * g;
122 sb = 255.0f * b;
123
124 switch (colorspace) {
126 y = (0.257f * sr) + (0.504f * sg) + (0.098f * sb) + 16.0f;
127 cb = (-0.148f * sr) - (0.291f * sg) + (0.439f * sb) + 128.0f;
128 cr = (0.439f * sr) - (0.368f * sg) - (0.071f * sb) + 128.0f;
129 break;
131 y = (0.183f * sr) + (0.614f * sg) + (0.062f * sb) + 16.0f;
132 cb = (-0.101f * sr) - (0.338f * sg) + (0.439f * sb) + 128.0f;
133 cr = (0.439f * sr) - (0.399f * sg) - (0.040f * sb) + 128.0f;
134 break;
136 y = (0.299f * sr) + (0.587f * sg) + (0.114f * sb);
137 cb = (-0.16874f * sr) - (0.33126f * sg) + (0.5f * sb) + 128.0f;
138 cr = (0.5f * sr) - (0.41869f * sg) - (0.08131f * sb) + 128.0f;
139 break;
140 default:
141 BLI_assert_msg(0, "invalid colorspace");
142 break;
143 }
144
145 *r_y = y;
146 *r_cb = cb;
147 *r_cr = cr;
148}
149
150void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, int colorspace)
151{
152 /* FIXME the following comment must be wrong because:
153 * BLI_YCC_ITU_BT601 y 16.0 cr 16.0 -> r -0.7009. */
154
155 /* YCC input have a range of 16-235 and 16-240 except with JFIF_0_255 where the range is 0-255
156 * RGB outputs are in the range 0 - 1.0f. */
157
158 float r = 128.0f, g = 128.0f, b = 128.0f;
159
160 switch (colorspace) {
162 r = 1.164f * (y - 16.0f) + 1.596f * (cr - 128.0f);
163 g = 1.164f * (y - 16.0f) - 0.813f * (cr - 128.0f) - 0.392f * (cb - 128.0f);
164 b = 1.164f * (y - 16.0f) + 2.017f * (cb - 128.0f);
165 break;
167 r = 1.164f * (y - 16.0f) + 1.793f * (cr - 128.0f);
168 g = 1.164f * (y - 16.0f) - 0.534f * (cr - 128.0f) - 0.213f * (cb - 128.0f);
169 b = 1.164f * (y - 16.0f) + 2.115f * (cb - 128.0f);
170 break;
172 r = y + 1.402f * cr - 179.456f;
173 g = y - 0.34414f * cb - 0.71414f * cr + 135.45984f;
174 b = y + 1.772f * cb - 226.816f;
175 break;
176 default:
178 break;
179 }
180 *r_r = r / 255.0f;
181 *r_g = g / 255.0f;
182 *r_b = b / 255.0f;
183}
184
185void hex_to_rgb(const char *hexcol, float *r_r, float *r_g, float *r_b)
186{
187 hex_to_rgba(hexcol, r_r, r_g, r_b, nullptr);
188}
189
190void hex_to_rgba(const char *hexcol, float *r_r, float *r_g, float *r_b, float *r_a)
191{
192 uint ri, gi, bi, ai;
193 bool has_alpha = false;
194
195 if (hexcol[0] == '#') {
196 hexcol++;
197 }
198
199 if (sscanf(hexcol, "%02x%02x%02x%02x", &ri, &gi, &bi, &ai) == 4) {
200 /* height digit hex colors with alpha */
201 has_alpha = true;
202 }
203 else if (sscanf(hexcol, "%02x%02x%02x", &ri, &gi, &bi) == 3) {
204 /* six digit hex colors */
205 }
206 else if (sscanf(hexcol, "%01x%01x%01x", &ri, &gi, &bi) == 3) {
207 /* three digit hex colors (#123 becomes #112233) */
208 ri += ri << 4;
209 gi += gi << 4;
210 bi += bi << 4;
211 }
212 else {
213 /* avoid using un-initialized vars */
214 *r_r = *r_g = *r_b = 0.0f;
215 if (r_a) {
216 *r_a = 0.0f;
217 }
218 return;
219 }
220
221 *r_r = float(ri) * (1.0f / 255.0f);
222 *r_g = float(gi) * (1.0f / 255.0f);
223 *r_b = float(bi) * (1.0f / 255.0f);
224 CLAMP(*r_r, 0.0f, 1.0f);
225 CLAMP(*r_g, 0.0f, 1.0f);
226 CLAMP(*r_b, 0.0f, 1.0f);
227
228 if (r_a && has_alpha) {
229 *r_a = float(ai) * (1.0f / 255.0f);
230 CLAMP(*r_a, 0.0f, 1.0f);
231 }
232}
233
234void rgb_to_hsv(float r, float g, float b, float *r_h, float *r_s, float *r_v)
235{
236 float k = 0.0f;
237 float chroma;
238 float min_gb;
239
240 if (g < b) {
241 SWAP(float, g, b);
242 k = -1.0f;
243 }
244 min_gb = b;
245 if (r < g) {
246 SWAP(float, r, g);
247 k = -2.0f / 6.0f - k;
248 min_gb = min_ff(g, b);
249 }
250
251 chroma = r - min_gb;
252
253 *r_h = fabsf(k + (g - b) / (6.0f * chroma + 1e-20f));
254 *r_s = chroma / (r + 1e-20f);
255 *r_v = r;
256}
257
258void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
259{
260 rgb_to_hsv(rgb[0], rgb[1], rgb[2], &r_hsv[0], &r_hsv[1], &r_hsv[2]);
261}
262
263void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l)
264{
265 const float cmax = max_fff(r, g, b);
266 const float cmin = min_fff(r, g, b);
267 float h, s, l = min_ff(1.0f, (cmax + cmin) / 2.0f);
268
269 if (cmax == cmin) {
270 h = s = 0.0f; /* achromatic */
271 }
272 else {
273 float d = cmax - cmin;
274 s = l > 0.5f ? d / (2.0f - cmax - cmin) : d / (cmax + cmin);
275 if (cmax == r) {
276 h = (g - b) / d + (g < b ? 6.0f : 0.0f);
277 }
278 else if (cmax == g) {
279 h = (b - r) / d + 2.0f;
280 }
281 else {
282 h = (r - g) / d + 4.0f;
283 }
284 }
285 h /= 6.0f;
286
287 *r_h = h;
288 *r_s = s;
289 *r_l = l;
290}
291
292void rgb_to_hsl_compat(float r, float g, float b, float *r_h, float *r_s, float *r_l)
293{
294 const float orig_s = *r_s;
295 const float orig_h = *r_h;
296
297 rgb_to_hsl(r, g, b, r_h, r_s, r_l);
298
299 if (*r_l <= 0.0f) {
300 *r_h = orig_h;
301 *r_s = orig_s;
302 }
303 else if (*r_s <= 0.0f) {
304 *r_h = orig_h;
305 *r_s = orig_s;
306 }
307
308 if (*r_h == 0.0f && orig_h >= 1.0f) {
309 *r_h = 1.0f;
310 }
311}
312
313void rgb_to_hsl_compat_v(const float rgb[3], float r_hsl[3])
314{
315 rgb_to_hsl_compat(rgb[0], rgb[1], rgb[2], &r_hsl[0], &r_hsl[1], &r_hsl[2]);
316}
317
318void rgb_to_hsl_v(const float rgb[3], float r_hsl[3])
319{
320 rgb_to_hsl(rgb[0], rgb[1], rgb[2], &r_hsl[0], &r_hsl[1], &r_hsl[2]);
321}
322
323void rgb_to_hsv_compat(float r, float g, float b, float *r_h, float *r_s, float *r_v)
324{
325 const float orig_h = *r_h;
326 const float orig_s = *r_s;
327
328 rgb_to_hsv(r, g, b, r_h, r_s, r_v);
329
330 if (*r_v <= 1e-8) {
331 /* Very low V values will affect the HS values, correct them in post. */
332 *r_h = orig_h;
333 *r_s = orig_s;
334 }
335 else if (*r_s <= 1e-8) {
336 *r_h = orig_h;
337 }
338
339 if (*r_h == 0.0f && orig_h >= 1.0f) {
340 *r_h = 1.0f;
341 }
342}
343
344void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3])
345{
346 rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], &r_hsv[0], &r_hsv[1], &r_hsv[2]);
347}
348
349void hsv_clamp_v(float hsv[3], float v_max)
350{
351 if (UNLIKELY(hsv[0] < 0.0f || hsv[0] > 1.0f)) {
352 hsv[0] = hsv[0] - floorf(hsv[0]);
353 }
354 CLAMP(hsv[1], 0.0f, 1.0f);
355 CLAMP(hsv[2], 0.0f, v_max);
356}
357
358uint hsv_to_cpack(float h, float s, float v)
359{
360 uint r, g, b;
361 float rf, gf, bf;
362 uint col;
363
364 hsv_to_rgb(h, s, v, &rf, &gf, &bf);
365
366 r = uint(rf * 255.0f);
367 g = uint(gf * 255.0f);
368 b = uint(bf * 255.0f);
369
370 col = (r + (g * 256) + (b * 256 * 256));
371 return col;
372}
373
374uint rgb_to_cpack(float r, float g, float b)
375{
376 uint ir, ig, ib;
377
378 ir = uint(floorf(255.0f * max_ff(r, 0.0f)));
379 ig = uint(floorf(255.0f * max_ff(g, 0.0f)));
380 ib = uint(floorf(255.0f * max_ff(b, 0.0f)));
381
382 ir = std::min<uint>(ir, 255);
383 ig = std::min<uint>(ig, 255);
384 ib = std::min<uint>(ib, 255);
385
386 return (ir + (ig * 256) + (ib * 256 * 256));
387}
388
389void cpack_to_rgb(uint col, float *r_r, float *r_g, float *r_b)
390{
391 *r_r = float(col & 0xFF) * (1.0f / 255.0f);
392 *r_g = float((col >> 8) & 0xFF) * (1.0f / 255.0f);
393 *r_b = float((col >> 16) & 0xFF) * (1.0f / 255.0f);
394}
395
396void rgb_uchar_to_float(float r_col[3], const uchar col_ub[3])
397{
398 r_col[0] = float(col_ub[0]) * (1.0f / 255.0f);
399 r_col[1] = float(col_ub[1]) * (1.0f / 255.0f);
400 r_col[2] = float(col_ub[2]) * (1.0f / 255.0f);
401}
402
403void rgba_uchar_to_float(float r_col[4], const uchar col_ub[4])
404{
405 r_col[0] = float(col_ub[0]) * (1.0f / 255.0f);
406 r_col[1] = float(col_ub[1]) * (1.0f / 255.0f);
407 r_col[2] = float(col_ub[2]) * (1.0f / 255.0f);
408 r_col[3] = float(col_ub[3]) * (1.0f / 255.0f);
409}
410
411void rgb_float_to_uchar(uchar r_col[3], const float col_f[3])
412{
413 unit_float_to_uchar_clamp_v3(r_col, col_f);
414}
415
416void rgba_float_to_uchar(uchar r_col[4], const float col_f[4])
417{
418 unit_float_to_uchar_clamp_v4(r_col, col_f);
419}
420
421/* ********************************* color transforms ********************************* */
422
423float srgb_to_linearrgb(float c)
424{
425 if (c < 0.04045f) {
426 return (c < 0.0f) ? 0.0f : c * (1.0f / 12.92f);
427 }
428
429 return powf((c + 0.055f) * (1.0f / 1.055f), 2.4f);
430}
431
432float linearrgb_to_srgb(float c)
433{
434 if (c < 0.0031308f) {
435 return (c < 0.0f) ? 0.0f : c * 12.92f;
436 }
437
438 return 1.055f * powf(c, 1.0f / 2.4f) - 0.055f;
439}
440
441/* SIMD code path, with pow 2.4 and 1/2.4 approximations. */
442#if BLI_HAVE_SSE2
443
454MALWAYS_INLINE __m128 _bli_math_fastpow(const int exp, const int e2coeff, const __m128 arg)
455{
456 __m128 ret;
457 ret = _mm_mul_ps(arg, _mm_castsi128_ps(_mm_set1_epi32(e2coeff)));
458 ret = _mm_cvtepi32_ps(_mm_castps_si128(ret));
459 ret = _mm_mul_ps(ret, _mm_castsi128_ps(_mm_set1_epi32(exp)));
460 ret = _mm_castsi128_ps(_mm_cvtps_epi32(ret));
461 return ret;
462}
463
465MALWAYS_INLINE __m128 _bli_math_improve_5throot_solution(const __m128 old_result, const __m128 x)
466{
467 __m128 approx2 = _mm_mul_ps(old_result, old_result);
468 __m128 approx4 = _mm_mul_ps(approx2, approx2);
469 __m128 t = _mm_div_ps(x, approx4);
470 __m128 summ = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(4.0f), old_result), t); /* FMA. */
471 return _mm_mul_ps(summ, _mm_set1_ps(1.0f / 5.0f));
472}
473
475MALWAYS_INLINE __m128 _bli_math_fastpow24(const __m128 arg)
476{
477 /* max, avg and |avg| errors were calculated in GCC without FMA instructions
478 * The final precision should be better than `powf` in GLIBC. */
479
480 /* Calculate x^4/5, coefficient 0.994 was constructed manually to minimize
481 * avg error.
482 */
483 /* 0x3F4CCCCD = 4/5 */
484 /* 0x4F55A7FB = 2^(127/(4/5) - 127) * 0.994^(1/(4/5)) */
485 /* error max = 0.17, avg = 0.0018, |avg| = 0.05 */
486 __m128 x = _bli_math_fastpow(0x3F4CCCCD, 0x4F55A7FB, arg);
487 __m128 arg2 = _mm_mul_ps(arg, arg);
488 __m128 arg4 = _mm_mul_ps(arg2, arg2);
489 /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */
491 /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */
493 /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */
495 return _mm_mul_ps(x, _mm_mul_ps(x, x));
496}
497
498MALWAYS_INLINE __m128 _bli_math_rsqrt(__m128 in)
499{
500 __m128 r = _mm_rsqrt_ps(in);
501 /* Only do additional Newton-Raphson iterations when using actual SSE
502 * code path. When we are emulating SSE on NEON via sse2neon, the
503 * additional NR iterations are already done inside _mm_rsqrt_ps
504 * emulation. */
505# if defined(__SSE2__)
506 r = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(1.5f), r),
507 _mm_mul_ps(_mm_mul_ps(_mm_mul_ps(in, _mm_set1_ps(-0.5f)), r), _mm_mul_ps(r, r)));
508# endif
509 return r;
510}
511
512/* Calculate `powf(x, 1.0f / 2.4)`. */
513MALWAYS_INLINE __m128 _bli_math_fastpow512(const __m128 arg)
514{
515 /* 5/12 is too small, so compute the 4th root of 20/12 instead.
516 * 20/12 = 5/3 = 1 + 2/3 = 2 - 1/3. 2/3 is a suitable argument for fastpow.
517 * weighting coefficient: a^-1/2 = 2 a; a = 2^-2/3
518 */
519 __m128 xf = _bli_math_fastpow(0x3f2aaaab, 0x5eb504f3, arg);
520 __m128 xover = _mm_mul_ps(arg, xf);
521 __m128 xfm1 = _bli_math_rsqrt(xf);
522 __m128 x2 = _mm_mul_ps(arg, arg);
523 __m128 xunder = _mm_mul_ps(x2, xfm1);
524 /* sqrt2 * over + 2 * sqrt2 * under */
525 __m128 xavg = _mm_mul_ps(_mm_set1_ps(1.0f / (3.0f * 0.629960524947437f) * 0.999852f),
526 _mm_add_ps(xover, xunder));
527 xavg = _mm_mul_ps(xavg, _bli_math_rsqrt(xavg));
528 xavg = _mm_mul_ps(xavg, _bli_math_rsqrt(xavg));
529 return xavg;
530}
531
532MALWAYS_INLINE __m128 _bli_math_blend_sse(const __m128 mask, const __m128 a, const __m128 b)
533{
534# if BLI_HAVE_SSE4
535 return _mm_blendv_ps(b, a, mask);
536# else
537 return _mm_or_ps(_mm_and_ps(mask, a), _mm_andnot_ps(mask, b));
538# endif
539}
540
541MALWAYS_INLINE __m128 srgb_to_linearrgb_v4_simd(const __m128 c)
542{
543 __m128 cmp = _mm_cmplt_ps(c, _mm_set1_ps(0.04045f));
544 __m128 lt = _mm_max_ps(_mm_mul_ps(c, _mm_set1_ps(1.0f / 12.92f)), _mm_set1_ps(0.0f));
545 __m128 gtebase = _mm_mul_ps(_mm_add_ps(c, _mm_set1_ps(0.055f)),
546 _mm_set1_ps(1.0f / 1.055f)); /* FMA. */
547 __m128 gte = _bli_math_fastpow24(gtebase);
548 return _bli_math_blend_sse(cmp, lt, gte);
549}
550
551MALWAYS_INLINE __m128 linearrgb_to_srgb_v4_simd(const __m128 c)
552{
553 __m128 cmp = _mm_cmplt_ps(c, _mm_set1_ps(0.0031308f));
554 __m128 lt = _mm_max_ps(_mm_mul_ps(c, _mm_set1_ps(12.92f)), _mm_set1_ps(0.0f));
555 __m128 gte = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(1.055f), _bli_math_fastpow512(c)),
556 _mm_set1_ps(-0.055f));
557 return _bli_math_blend_sse(cmp, lt, gte);
558}
559
560void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
561{
562 float r[4] = {srgb[0], srgb[1], srgb[2], 1.0f};
563 __m128 *rv = (__m128 *)&r;
564 *rv = srgb_to_linearrgb_v4_simd(*rv);
565 linear[0] = r[0];
566 linear[1] = r[1];
567 linear[2] = r[2];
568}
569
570void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
571{
572 float r[4] = {linear[0], linear[1], linear[2], 1.0f};
573 __m128 *rv = (__m128 *)&r;
574 *rv = linearrgb_to_srgb_v4_simd(*rv);
575 srgb[0] = r[0];
576 srgb[1] = r[1];
577 srgb[2] = r[2];
578}
579
580#else /* BLI_HAVE_SSE2 */
581
582/* Non-SIMD code path, with the same pow approximations as SIMD one. */
583
585{
586 float r;
587 memcpy(&r, &v, sizeof(v));
588 return r;
589}
590
592{
593 int32_t r;
594 memcpy(&r, &v, sizeof(v));
595 return r;
596}
597
598MALWAYS_INLINE float _bli_math_fastpow(const int exp, const int e2coeff, const float arg)
599{
600 float ret = arg * int_as_float(e2coeff);
601 ret = float(float_as_int(ret));
602 ret = ret * int_as_float(exp);
603 ret = int_as_float(int(ret));
604 return ret;
605}
606
607MALWAYS_INLINE float _bli_math_improve_5throot_solution(const float old_result, const float x)
608{
609 float approx2 = old_result * old_result;
610 float approx4 = approx2 * approx2;
611 float t = x / approx4;
612 float summ = 4.0f * old_result + t;
613 return summ * (1.0f / 5.0f);
614}
615
617{
618 float x = _bli_math_fastpow(0x3F4CCCCD, 0x4F55A7FB, arg);
619 float arg2 = arg * arg;
620 float arg4 = arg2 * arg2;
624 return x * x * x;
625}
626
628{
629 return 1.0f / sqrtf(in);
630}
631
633{
634 float xf = _bli_math_fastpow(0x3f2aaaab, 0x5eb504f3, arg);
635 float xover = arg * xf;
636 float xfm1 = _bli_math_rsqrt(xf);
637 float x2 = arg * arg;
638 float xunder = x2 * xfm1;
639 float xavg = (1.0f / (3.0f * 0.629960524947437f) * 0.999852f) * (xover + xunder);
640 xavg = xavg * _bli_math_rsqrt(xavg);
641 xavg = xavg * _bli_math_rsqrt(xavg);
642 return xavg;
643}
644
646{
647 if (c < 0.04045f) {
648 return (c < 0.0f) ? 0.0f : c * (1.0f / 12.92f);
649 }
650
651 return _bli_math_fastpow24((c + 0.055f) * (1.0f / 1.055f));
652}
653
655{
656 if (c < 0.0031308f) {
657 return (c < 0.0f) ? 0.0f : c * 12.92f;
658 }
659
660 return 1.055f * _bli_math_fastpow512(c) - 0.055f;
661}
662
663void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
664{
665 linear[0] = srgb_to_linearrgb_approx(srgb[0]);
666 linear[1] = srgb_to_linearrgb_approx(srgb[1]);
667 linear[2] = srgb_to_linearrgb_approx(srgb[2]);
668}
669
670void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
671{
672 srgb[0] = linearrgb_to_srgb_approx(linear[0]);
673 srgb[1] = linearrgb_to_srgb_approx(linear[1]);
674 srgb[2] = linearrgb_to_srgb_approx(linear[2]);
675}
676
677#endif /* BLI_HAVE_SSE2 */
678
679/* ************************************* other ************************************************* */
680
681void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset)
682{
683 float hsv[3];
684
685 rgb_to_hsv(rgb[0], rgb[1], rgb[2], hsv, hsv + 1, hsv + 2);
686
687 hsv[0] += hue_offset;
688 if (hsv[0] > 1.0f) {
689 hsv[0] -= 1.0f;
690 }
691 else if (hsv[0] < 0.0f) {
692 hsv[0] += 1.0f;
693 }
694
695 hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb + 1, rgb + 2);
696}
697
698void rgb_byte_set_hue_float_offset(uchar rgb[3], float hue_offset)
699{
700 float rgb_float[3];
701
702 rgb_uchar_to_float(rgb_float, rgb);
703 rgb_float_set_hue_float_offset(rgb_float, hue_offset);
704 rgb_float_to_uchar(rgb, rgb_float);
705}
706
707/* fast sRGB conversion
708 * LUT from linear float to 16-bit short
709 * based on http://mysite.verizon.net/spitzak/conversion/
710 */
711
714
715static ushort hipart(const float f)
716{
717 union {
718 float f;
719 ushort us[2];
720 } tmp;
721
722 tmp.f = f;
723
724#ifdef __BIG_ENDIAN__
725 return tmp.us[0];
726#else
727 return tmp.us[1];
728#endif
729}
730
731static float index_to_float(const ushort i)
732{
733
734 union {
735 float f;
736 ushort us[2];
737 } tmp;
738
739 /* positive and negative zeros, and all gradual underflow, turn into zero: */
740 if (i < 0x80 || (i >= 0x8000 && i < 0x8080)) {
741 return 0;
742 }
743 /* All NaN's and infinity turn into the largest possible legal float: */
744 if (i >= 0x7f80 && i < 0x8000) {
745 return FLT_MAX;
746 }
747 if (i >= 0xff80) {
748 return -FLT_MAX;
749 }
750
751#ifdef __BIG_ENDIAN__
752 tmp.us[0] = i;
753 tmp.us[1] = 0x8000;
754#else
755 tmp.us[0] = 0x8000;
756 tmp.us[1] = i;
757#endif
758
759 return tmp.f;
760}
761
763{
764 static bool initialized = false;
765 uint i, b;
766
767 if (initialized) {
768 return;
769 }
770 initialized = true;
771
772 /* Fill in the lookup table to convert floats to bytes: */
773 for (i = 0; i < 0x10000; i++) {
774 float f = linearrgb_to_srgb(index_to_float(ushort(i))) * 255.0f;
775 if (f <= 0) {
777 }
778 else if (f < 255) {
779 BLI_color_to_srgb_table[i] = ushort(f * 0x100 + 0.5f);
780 }
781 else {
782 BLI_color_to_srgb_table[i] = 0xff00;
783 }
784 }
785
786 /* Fill in the lookup table to convert bytes to float: */
787 for (b = 0; b <= 255; b++) {
788 float f = srgb_to_linearrgb(float(b) * (1.0f / 255.0f));
790 i = hipart(f);
791 /* replace entries so byte->float->byte does not change the data: */
792 BLI_color_to_srgb_table[i] = ushort(b * 0x100);
793 }
794}
795
796namespace blender::math {
797
799 float mired; /* Inverse temperature */
800 float2 uv; /* CIE 1960 uv coordinates */
801 float t; /* Isotherm parameter */
802 float dist(const float2 p) const
803 {
804 const float2 diff = p - uv;
805 return diff.y - t * diff.x;
806 }
807};
808
809/* Tabulated approximation of the Planckian locus.
810 * Based on http://www.brucelindbloom.com/Eqn_XYZ_to_T.html.
811 * Original source:
812 * "Color Science: Concepts and Methods, Quantitative Data and Formulae", Second Edition,
813 * Gunter Wyszecki and W. S. Stiles, John Wiley & Sons, 1982, pp. 227, 228. */
814static const std::array<locus_entry_t, 31> planck_locus{{
815 {0.0f, {0.18006f, 0.26352f}, -0.24341f}, {10.0f, {0.18066f, 0.26589f}, -0.25479f},
816 {20.0f, {0.18133f, 0.26846f}, -0.26876f}, {30.0f, {0.18208f, 0.27119f}, -0.28539f},
817 {40.0f, {0.18293f, 0.27407f}, -0.30470f}, {50.0f, {0.18388f, 0.27709f}, -0.32675f},
818 {60.0f, {0.18494f, 0.28021f}, -0.35156f}, {70.0f, {0.18611f, 0.28342f}, -0.37915f},
819 {80.0f, {0.18740f, 0.28668f}, -0.40955f}, {90.0f, {0.18880f, 0.28997f}, -0.44278f},
820 {100.0f, {0.19032f, 0.29326f}, -0.47888f}, {125.0f, {0.19462f, 0.30141f}, -0.58204f},
821 {150.0f, {0.19962f, 0.30921f}, -0.70471f}, {175.0f, {0.20525f, 0.31647f}, -0.84901f},
822 {200.0f, {0.21142f, 0.32312f}, -1.0182f}, {225.0f, {0.21807f, 0.32909f}, -1.2168f},
823 {250.0f, {0.22511f, 0.33439f}, -1.4512f}, {275.0f, {0.23247f, 0.33904f}, -1.7298f},
824 {300.0f, {0.24010f, 0.34308f}, -2.0637f}, {325.0f, {0.24792f, 0.34655f}, -2.4681f},
825 {350.0f, {0.25591f, 0.34951f}, -2.9641f}, {375.0f, {0.26400f, 0.35200f}, -3.5814f},
826 {400.0f, {0.27218f, 0.35407f}, -4.3633f}, {425.0f, {0.28039f, 0.35577f}, -5.3762f},
827 {450.0f, {0.28863f, 0.35714f}, -6.7262f}, {475.0f, {0.29685f, 0.35823f}, -8.5955f},
828 {500.0f, {0.30505f, 0.35907f}, -11.324f}, {525.0f, {0.31320f, 0.35968f}, -15.628f},
829 {550.0f, {0.32129f, 0.36011f}, -23.325f}, {575.0f, {0.32931f, 0.36038f}, -40.770f},
830 {600.0f, {0.33724f, 0.36051f}, -116.45f},
831}};
832
833bool whitepoint_to_temp_tint(const float3 &white, float &temperature, float &tint)
834{
835 /* Convert XYZ -> CIE 1960 uv. */
836 const float2 uv = float2{4.0f * white.x, 6.0f * white.y} / dot(white, {1.0f, 15.0f, 3.0f});
837
838 /* Find first entry that's "to the right" of the white point. */
839 auto check = [uv](const float val, const locus_entry_t &entry) { return entry.dist(uv) < val; };
840 const auto entry = std::upper_bound(planck_locus.begin(), planck_locus.end(), 0.0f, check);
841 if (entry == planck_locus.begin() || entry == planck_locus.end()) {
842 return false;
843 }
844 const size_t i = size_t(entry - planck_locus.begin());
845 const locus_entry_t &low = planck_locus[i - 1], high = planck_locus[i];
846
847 /* Find closest point on locus. */
848 const float d_low = low.dist(uv) / sqrtf(1.0f + low.t * low.t);
849 const float d_high = high.dist(uv) / sqrtf(1.0f + high.t * high.t);
850 const float f = d_low / (d_low - d_high);
851
852 /* Find tint based on distance to closest point on locus. */
853 const float2 uv_temp = interpolate(low.uv, high.uv, f);
854 const float abs_tint = length(uv - uv_temp) * 3000.0f;
855 if (abs_tint > 150.0f) {
856 return false;
857 }
858
859 temperature = 1e6f / interpolate(low.mired, high.mired, f);
860 tint = abs_tint * ((uv.x < uv_temp.x) ? 1.0f : -1.0f);
861 return true;
862}
863
864float3 whitepoint_from_temp_tint(const float temperature, const float tint)
865{
866 /* Find table entry. */
867 const float mired = clamp(
868 1e6f / temperature, planck_locus[0].mired, planck_locus[planck_locus.size() - 1].mired);
869 auto check = [](const locus_entry_t &entry, const float val) { return entry.mired < val; };
870 const auto entry = std::lower_bound(planck_locus.begin(), planck_locus.end(), mired, check);
871 const size_t i = size_t(entry - planck_locus.begin());
872 const locus_entry_t &low = planck_locus[i - 1], high = planck_locus[i];
873
874 /* Find interpolation factor. */
875 const float f = (mired - low.mired) / (high.mired - low.mired);
876
877 /* Interpolate point along Planckian locus. */
878 float2 uv = interpolate(low.uv, high.uv, f);
879
880 /* Compute and interpolate isotherm. */
881 const float2 isotherm0 = normalize(float2(1.0f, low.t));
882 const float2 isotherm1 = normalize(float2(1.0f, high.t));
883 const float2 isotherm = normalize(interpolate(isotherm0, isotherm1, f));
884
885 /* Offset away from the Planckian locus according to the tint.
886 * Tint is parameterized such that +-3000 tint corresponds to +-1 delta UV. */
887 uv -= isotherm * tint / 3000.0f;
888
889 /* Convert CIE 1960 uv -> xyY. */
890 const float x = 3.0f * uv.x / (2.0f * uv.x - 8.0f * uv.y + 4.0f);
891 const float y = 2.0f * uv.y / (2.0f * uv.x - 8.0f * uv.y + 4.0f);
892
893 /* Convert xyY -> XYZ (assuming Y=1). */
894 return float3{x / y, 1.0f, (1.0f - x - y) / y};
895}
896
897float3x3 chromatic_adaption_matrix(const float3 &from_XYZ, const float3 &to_XYZ)
898{
899 /* Bradford transformation matrix (XYZ -> LMS). */
900 static const float3x3 bradford{
901 {0.8951f, -0.7502f, 0.0389f},
902 {0.2664f, 1.7135f, -0.0685f},
903 {-0.1614f, 0.0367f, 1.0296f},
904 };
905
906 /* Compute white points in LMS space. */
907 const float3 from_LMS = bradford * from_XYZ / from_XYZ.y;
908 const float3 to_LMS = bradford * to_XYZ / to_XYZ.y;
909
910 /* Assemble full transform: XYZ -> LMS -> adapted LMS -> adapted XYZ. */
911 return invert(bradford) * from_scale<float3x3>(to_LMS / from_LMS) * bradford;
912}
913
914} // namespace blender::math
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE float max_fff(float a, float b, float c)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float min_fff(float a, float b, float c)
#define BLI_YUV_ITU_BT709
#define BLI_YCC_JFIF_0_255
#define BLI_YCC_ITU_BT601
#define BLI_YUV_ITU_BT601
#define BLI_YCC_ITU_BT709
#define MALWAYS_INLINE
unsigned char uchar
unsigned int uint
unsigned short ushort
#define CLAMP(a, b, c)
#define SWAP(type, a, b)
#define UNLIKELY(x)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
#define powf(x, y)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint col
#define exp
VecBase< float, D > normalize(VecOp< float, D >) RET
#define in
static bool initialized
#define unit_float_to_uchar_clamp_v4(v1, v2)
#define unit_float_to_uchar_clamp_v3(v1, v2)
MALWAYS_INLINE float _bli_math_fastpow(const int exp, const int e2coeff, const float arg)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
static float index_to_float(const ushort i)
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
MALWAYS_INLINE float srgb_to_linearrgb_approx(float c)
void rgb_float_to_uchar(uchar r_col[3], const float col_f[3])
void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l)
void rgba_uchar_to_float(float r_col[4], const uchar col_ub[4])
void rgb_to_hsl_compat(float r, float g, float b, float *r_h, float *r_s, float *r_l)
MALWAYS_INLINE float int_as_float(int32_t v)
void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3])
MALWAYS_INLINE float _bli_math_rsqrt(float in)
void hex_to_rgba(const char *hexcol, float *r_r, float *r_g, float *r_b, float *r_a)
void rgb_to_hsv(float r, float g, float b, float *r_h, float *r_s, float *r_v)
uint hsv_to_cpack(float h, float s, float v)
MALWAYS_INLINE float linearrgb_to_srgb_approx(float c)
void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
MALWAYS_INLINE float _bli_math_improve_5throot_solution(const float old_result, const float x)
void hsl_to_rgb_v(const float hsl[3], float r_rgb[3])
Definition math_color.cc:62
void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, int colorspace)
void rgb_byte_set_hue_float_offset(uchar rgb[3], float hue_offset)
void hsv_to_rgb(float h, float s, float v, float *r_r, float *r_g, float *r_b)
Definition math_color.cc:21
void rgb_uchar_to_float(float r_col[3], const uchar col_ub[3])
void rgb_to_ycc(float r, float g, float b, float *r_y, float *r_cb, float *r_cr, int colorspace)
MALWAYS_INLINE float _bli_math_fastpow24(const float arg)
void rgb_to_yuv(float r, float g, float b, float *r_y, float *r_u, float *r_v, int colorspace)
Definition math_color.cc:67
void rgb_to_hsv_compat(float r, float g, float b, float *r_h, float *r_s, float *r_v)
void BLI_init_srgb_conversion()
void rgba_float_to_uchar(uchar r_col[4], const float col_f[4])
void hsv_clamp_v(float hsv[3], float v_max)
void hsl_to_rgb(float h, float s, float l, float *r_r, float *r_g, float *r_b)
Definition math_color.cc:38
float srgb_to_linearrgb(float c)
static ushort hipart(const float f)
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
ushort BLI_color_to_srgb_table[0x10000]
void hex_to_rgb(const char *hexcol, float *r_r, float *r_g, float *r_b)
void cpack_to_rgb(uint col, float *r_r, float *r_g, float *r_b)
float BLI_color_from_srgb_table[256]
MALWAYS_INLINE int32_t float_as_int(float v)
float linearrgb_to_srgb(float c)
void yuv_to_rgb(float y, float u, float v, float *r_r, float *r_g, float *r_b, int colorspace)
Definition math_color.cc:91
void rgb_to_hsl_v(const float rgb[3], float r_hsl[3])
void rgb_to_hsl_compat_v(const float rgb[3], float r_hsl[3])
MALWAYS_INLINE float _bli_math_fastpow512(const float arg)
uint rgb_to_cpack(float r, float g, float b)
void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
T clamp(const T &a, const T &min, const T &max)
float3 whitepoint_from_temp_tint(float temperature, float tint)
bool whitepoint_to_temp_tint(const float3 &white, float &temperature, float &tint)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
static const std::array< locus_entry_t, 31 > planck_locus
CartesianBasis invert(const CartesianBasis &basis)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
T interpolate(const T &a, const T &b, const FactorT &t)
float3x3 chromatic_adaption_matrix(const float3 &from_XYZ, const float3 &to_XYZ)
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
return ret
#define FLT_MAX
Definition stdcycles.h:14
float mired
float dist(const float2 p) const
float2 uv
float t
i
Definition text_draw.cc:230