Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorspaces_inline_conversions.h
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2017 Tobias Ellinghaus.
4 * Copyright (C) 2018-2022, 2025 Aurélien PIERRE.
5 * Copyright (C) 2018, 2020-2021 Pascal Obry.
6 * Copyright (C) 2019 Edgardo Hoszowski.
7 * Copyright (C) 2019, 2021 Philippe Weyland.
8 * Copyright (C) 2019 Roman Lebedev.
9 * Copyright (C) 2020 Harold le Clément de Saint-Marcq.
10 * Copyright (C) 2020-2021 Ralf Brown.
11 * Copyright (C) 2021-2022 Sakari Kapanen.
12 * Copyright (C) 2022 Martin Bařinka.
13 *
14 * darktable is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * darktable is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28#pragma once
29
30#include "common/matrices.h"
31#include "common/math.h"
32
33static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
34dt_colormatrix_row_to_simd(const dt_colormatrix_t matrix, const int row)
35{
36 return (dt_aligned_pixel_simd_t){ matrix[row][0], matrix[row][1], matrix[row][2], matrix[row][3] };
37}
38
39__OMP_DECLARE_SIMD__(aligned(in,out:16) aligned(matrix:64) uniform(matrix))
42{
43 const dt_aligned_pixel_simd_t vin = dt_load_simd_aligned(in);
44 dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0),
45 dt_colormatrix_row_to_simd(matrix, 1),
46 dt_colormatrix_row_to_simd(matrix, 2)));
47}
48
49__OMP_DECLARE_SIMD__(simdlen(4))
50static inline float cbrt_5f(float f)
51{
52 uint32_t * const p = (uint32_t *)&f;
53 *p = *p / 3 + 709921077;
54 return f;
55}
56
57__OMP_DECLARE_SIMD__(simdlen(4))
58static inline float cbrta_halleyf(const float a, const float R)
59{
60 const float a3 = a * a * a;
61 const float b = a * (a3 + R + R) / (a3 + a3 + R);
62 return b;
63}
64
65__OMP_DECLARE_SIMD__(simdlen(4))
66static inline float lab_f(const float x)
67{
68 const float epsilon = 216.0f / 24389.0f;
69 const float kappa = 24389.0f / 27.0f;
70 return (x > epsilon) ? cbrta_halleyf(cbrt_5f(x), x) : (kappa * x + 16.0f) / 116.0f;
71}
72
74static const dt_aligned_pixel_t d50 = { 0.9642f, 1.0f, 0.8249f };
75
76__OMP_DECLARE_SIMD__(aligned(XYZ, Lab:16))
78{
81 f[i] = lab_f(XYZ[i] / d50[i]);
82 Lab[0] = 116.0f * f[1] - 16.0f;
83 Lab[1] = 500.0f * (f[0] - f[1]);
84 Lab[2] = 200.0f * (f[1] - f[2]);
85}
86
87__OMP_DECLARE_SIMD__(simdlen(4))
88static inline float lab_f_inv(const float x)
89{
90 const float epsilon = 0.20689655172413796f; // cbrtf(216.0f/24389.0f);
91 const float kappa = 24389.0f / 27.0f;
92 return (x > epsilon) ? x * x * x : (116.0f * x - 16.0f) / kappa;
93}
94
96__OMP_DECLARE_SIMD__(aligned(Lab, XYZ:16))
98{
99 const float fy = (Lab[0] + 16.0f) / 116.0f;
100 const float fx = Lab[1] / 500.0f + fy;
101 const float fz = fy - Lab[2] / 200.0f;
102 const dt_aligned_pixel_t f = { fx, fy, fz };
104 XYZ[c] = d50[c] * lab_f_inv(f[c]);
105}
106
107
108__OMP_DECLARE_SIMD__(aligned(xyY, XYZ:16))
110{
111 const float sum = XYZ[0] + XYZ[1] + XYZ[2];
112 xyY[0] = XYZ[0] / sum;
113 xyY[1] = XYZ[1] / sum;
114 xyY[2] = XYZ[1];
115}
116
117static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
118dt_XYZ_to_xyY_simd(const dt_aligned_pixel_simd_t XYZ)
119{
120 const float sum = XYZ[0] + XYZ[1] + XYZ[2];
121 return (dt_aligned_pixel_simd_t){ XYZ[0] / sum, XYZ[1] / sum, XYZ[1], 0.f };
122}
123
124
125__OMP_DECLARE_SIMD__(aligned(xyY, XYZ:16))
126static inline __attribute__((always_inline)) void dt_xyY_to_XYZ(const dt_aligned_pixel_t xyY, dt_aligned_pixel_t XYZ)
127{
128 XYZ[0] = xyY[2] * xyY[0] / xyY[1];
129 XYZ[1] = xyY[2];
130 XYZ[2] = xyY[2] * (1.f - xyY[0] - xyY[1]) / xyY[1];
131}
132
133static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
134dt_xyY_to_XYZ_simd(const dt_aligned_pixel_simd_t xyY)
135{
136 const float y_over_x = xyY[2] / xyY[1];
137 return (dt_aligned_pixel_simd_t){ y_over_x * xyY[0], xyY[2], y_over_x * (1.f - xyY[0] - xyY[1]), 0.f };
138}
139
140
141__OMP_DECLARE_SIMD__(aligned(xyY, uvY:16))
143{
144 // This is the linear part of the chromaticity transform from CIE L*u*v* e.g. u'v'.
145 // See https://en.wikipedia.org/wiki/CIELUV
146 // It rescales the chromaticity diagram xyY in a more perceptual way,
147 // but it is still not hue-linear and not perfectly perceptual.
148 // As such, it is the only radiometricly-accurate representation of hue non-linearity in human vision system.
149 // Use it for "hue preserving" (as much as possible) gamut mapping in scene-referred space
150 const float denominator = -2.f * xyY[0] + 12.f * xyY[1] + 3.f;
151 uvY[0] = 4.f * xyY[0] / denominator; // u'
152 uvY[1] = 9.f * xyY[1] / denominator; // v'
153 uvY[2] = xyY[2]; // Y
154}
155
157static inline float cbf(const float x)
158{
159 return x * x * x;
160}
161
162
163__OMP_DECLARE_SIMD__(aligned(xyY, Luv:16))
165{
166 // This is the second, non-linear, part of the the 1976 CIE L*u*v* transform.
167 // See https://en.wikipedia.org/wiki/CIELUV
168 // It is intended to provide perceptual hue-linear-ish controls and settings for more intuitive GUI.
169 // Don't ever use it for pixel-processing, it sucks, it's old, it kills kittens and makes your mother cry.
170 // Seriously, don't.
171 // You need to convert Luv parameters to XYZ or RGB and properly process pixels in RGB or XYZ or related spaces.
174
175 // We assume Yn == 1 == peak luminance
176 const float threshold = cbf(6.0f / 29.0f);
177 Luv[0] = (uvY[2] <= threshold) ? cbf(29.0f / 3.0f) * uvY[2] : 116.0f * cbrtf(uvY[2]) - 16.f;
178
179 const float D50[2] DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
180 Luv[1] = 13.f * Luv[0] * (uvY[0] - D50[0]); // u*
181 Luv[2] = 13.f * Luv[0] * (uvY[1] - D50[1]); // v*
182
183 // Output is in [0; 100] for all channels
184}
185
186
187static inline __attribute__((always_inline)) void dt_Luv_to_Lch(const dt_aligned_pixel_t Luv, dt_aligned_pixel_t Lch)
188{
189 Lch[0] = Luv[0]; // L stays L
190 Lch[1] = hypotf(Luv[2], Luv[1]); // chroma radius
191 Lch[2] = atan2f(Luv[2], Luv[1]); // hue angle
192 Lch[2] = (Lch[2] < 0.f) ? 2.f * M_PI + Lch[2] : Lch[2]; // ensure angle is positive modulo 2 pi
193}
194
195
196static inline __attribute__((always_inline)) void dt_xyY_to_Lch(const dt_aligned_pixel_t xyY, dt_aligned_pixel_t Lch)
197{
201}
202
203
204__OMP_DECLARE_SIMD__(aligned(uvY, xyY:16))
205static inline __attribute__((always_inline)) void dt_uvY_to_xyY(const dt_aligned_pixel_t uvY, dt_aligned_pixel_t xyY)
206{
207 // This is the linear part of chromaticity transform from CIE L*u*v* e.g. u'v'.
208 // See https://en.wikipedia.org/wiki/CIELUV
209 // It rescales the chromaticity diagram xyY in a more perceptual way,
210 // but it is still not hue-linear and not perfectly perceptual.
211 // As such, it is the only radiometricly-accurate representation of hue non-linearity in human vision system.
212 // Use it for "hue preserving" (as much as possible) gamut mapping in scene-referred space
213 const float denominator = 6.0f * uvY[0] - 16.f * uvY[1] + 12.0f;
214 xyY[0] = 9.f * uvY[0] / denominator; // x
215 xyY[1] = 4.f * uvY[1] / denominator; // y
216 xyY[2] = uvY[2]; // Y
217}
218
219
220__OMP_DECLARE_SIMD__(aligned(xyY, Luv:16))
221static inline __attribute__((always_inline)) void dt_Luv_to_xyY(const dt_aligned_pixel_t Luv, dt_aligned_pixel_t xyY)
222{
223 // This is the second, non-linear, part of the the 1976 CIE L*u*v* transform.
224 // See https://en.wikipedia.org/wiki/CIELUV
225 // It is intended to provide perceptual hue-linear-ish controls and settings for more intuitive GUI.
226 // Don't ever use it for pixel-processing, it sucks, it's old, it kills kittens and makes your mother cry.
227 // Seriously, don't.
228 // You need to convert Luv parameters to XYZ or RGB and properly process pixels in RGB or XYZ or related spaces.
230
231 // We assume Yn == 1 == peak luminance
232 static const float threshold = 8.0f;
233 uvY[2] = (Luv[0] <= threshold) ? Luv[0] * cbf(3.f / 29.f) : cbf((Luv[0] + 16.f) / 116.f);
234
235 static const float D50[2] DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
236 uvY[0] = Luv[1] / (Luv[0] * 13.f) + D50[0]; // u' = u* / 13 L + u_n
237 uvY[1] = Luv[2] / (Luv[0] * 13.f) + D50[1]; // v' = v* / 13 L + v_n
238
240 // Output is normalized for all channels
241}
242
243static inline __attribute__((always_inline)) void dt_Lch_to_Luv(const dt_aligned_pixel_t Lch, dt_aligned_pixel_t Luv)
244{
245 Luv[0] = Lch[0]; // L stays L
246 Luv[1] = Lch[1] * cosf(Lch[2]); // radius * cos(angle)
247 Luv[2] = Lch[1] * sinf(Lch[2]); // radius * sin(angle)
248}
249
250static inline __attribute__((always_inline)) void dt_Lch_to_xyY(const dt_aligned_pixel_t Lch, dt_aligned_pixel_t xyY)
251{
255}
256
258__OMP_DECLARE_SIMD__(aligned(XYZ, sRGB:16))
260{
261 // transpose and pad the conversion matrix to enable vectorization
262 static const dt_colormatrix_t xyz_to_srgb_matrix_transposed =
263 { { 3.1338561f, -0.9787684f, 0.0719453f, 0.0f },
264 { -1.6168667f, 1.9161415f, -0.2289914f, 0.0f },
265 { -0.4906146f, 0.0334540f, 1.4052427f, 0.0f } };
266
267 // XYZ -> linear sRGB
268 dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB);
269}
270
271
273__OMP_DECLARE_SIMD__(aligned(XYZ, sRGB:16))
274static inline __attribute__((always_inline)) void dt_XYZ_to_Rec709_D65(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
275{
276 // linear sRGB == Rec709 with no gamma
277 // transpose and pad the conversion matrix to enable vectorization
279 { 3.2404542f, -0.9692660f, 0.0556434f, 0.0f },
280 { -1.5371385f, 1.8760108f, -0.2040259f, 0.0f },
281 { -0.4985314f, 0.0415560f, 1.0572252f, 0.0f },
282 };
283 // XYZ -> linear sRGB
285}
286
287
289__OMP_DECLARE_SIMD__(aligned(XYZ, sRGB:16))
291{
292 // XYZ -> linear sRGB
295 // linear sRGB -> gamma corrected sRGB
296 for(size_t c = 0; c < 3; c++)
297 sRGB[c] = rgb[c] <= 0.0031308f ? 12.92f * rgb[c] : (1.0f + 0.055f) * powf(rgb[c], 1.0f / 2.4f) - 0.055f;
298}
299
300
302__OMP_DECLARE_SIMD__(aligned(XYZ, sRGB:16))
303static inline __attribute__((always_inline)) void dt_XYZ_to_sRGB_clipped(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
304{
305 dt_aligned_pixel_t result;
307
309 sRGB[c] = CLIP(result[c]);
310}
311
312
313__OMP_DECLARE_SIMD__(aligned(sRGB, XYZ_D50: 16))
315{
316 // Conversion matrix from http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
317 // (transpose and pad the conversion matrix to enable vectorization)
318 static const dt_colormatrix_t M = {
319 { 0.4360747f, 0.2225045f, 0.0139322f, 0.0f },
320 { 0.3850649f, 0.7168786f, 0.0971045f, 0.0f },
321 { 0.1430804f, 0.0606169f, 0.7141733f, 0.0f }
322 };
324}
325__OMP_DECLARE_SIMD__(aligned(sRGB, RGB:16))
327{
328 // gamma corrected sRGB -> linear sRGB
329 for(int c = 0; c < 3; c++)
330 RGB[c] = sRGB[c] <= 0.04045f ? sRGB[c] / 12.92f : powf((sRGB[c] + 0.055f) / (1.0f + 0.055f), 2.4f);
331}
332
334static inline __attribute__((always_inline)) void dt_sRGB_to_XYZ(const dt_aligned_pixel_t sRGB, dt_aligned_pixel_t XYZ)
335{
336 dt_aligned_pixel_t rgb = { 0 };
338 // linear sRGB -> XYZ
340}
341
344{
345 // transpose and pad the conversion matrix to enable vectorization
346 static const dt_colormatrix_t xyz_to_rgb_transpose = {
347 { 1.3459433f, -0.5445989f, 0.0000000f, 0.0f },
348 { -0.2556075f, 1.5081673f, 0.0000000f, 0.0f },
349 { -0.0511118f, 0.0205351f, 1.2118128f, 0.0f }
350 };
352}
353
355static inline __attribute__((always_inline)) void dt_prophotorgb_to_XYZ(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t XYZ)
356{
357 // transpose and pad the conversion matrix to enable vectorization
358 static const dt_colormatrix_t rgb_to_xyz_transpose = {
359 // prophoto rgb
360 { 0.7976749f, 0.2880402f, 0.0000000f, 0.0f },
361 { 0.1351917f, 0.7118741f, 0.0000000f, 0.0f },
362 { 0.0313534f, 0.0000857f, 0.8252100f, 0.0f }
363 };
365}
366
367#if 0
368static const dt_colormatrix_t linear_sRGB_to_xyz_matrix =
369 { { 0.4360747f, 0.3850649f, 0.1430804f },
370 { 0.2225045f, 0.7168786f, 0.0606169f },
371 { 0.0139322f, 0.0971045f, 0.7141733f } };
372#endif
373
375 { { 0.4360747f, 0.2225045f, 0.0139322f },
376 { 0.3850649f, 0.7168786f, 0.0971045f },
377 { 0.1430804f, 0.0606169f, 0.7141733f } };
378
379static inline __attribute__((always_inline)) void dt_linearRGB_to_XYZ(const dt_aligned_pixel_t linearRGB, dt_aligned_pixel_t XYZ)
380{
382}
383
384#if 0
385static const dt_colormatrix_t xyz_to_srgb_matrix =
386 { { 3.1338561f, -1.6168667f, -0.4906146f },
387 { -0.9787684f, 1.9161415f, 0.0334540f },
388 { 0.0719453f, -0.2289914f, 1.4052427f } };
389#endif
390
392 { { 3.1338561f, -0.9787684f, 0.0719453f },
393 { -1.6168667f, 1.9161415f, -0.2289914f },
394 { -0.4906146f, 0.0334540f, 1.4052427f } };
395
396static inline __attribute__((always_inline)) void dt_XYZ_to_linearRGB(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t linearRGB)
397{
399}
400
401
403static inline __attribute__((always_inline)) void dt_Lab_to_prophotorgb(const dt_aligned_pixel_t Lab, dt_aligned_pixel_t rgb)
404{
405 dt_aligned_pixel_t XYZ = { 0.0f };
408}
409
411static inline __attribute__((always_inline)) void dt_prophotorgb_to_Lab(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t Lab)
412{
413 dt_aligned_pixel_t XYZ = { 0.0f };
416}
417
418
420static inline float _dt_RGB_2_Hue(const dt_aligned_pixel_t RGB, const float max, const float delta)
421{
422 float hue;
423 if(RGB[0] == max)
424 hue = (RGB[1] - RGB[2]) / delta;
425 else if(RGB[1] == max)
426 hue = 2.0f + (RGB[2] - RGB[0]) / delta;
427 else
428 hue = 4.0f + (RGB[0] - RGB[1]) / delta;
429
430 hue /= 6.0f;
431 if(hue < 0.0f) hue += 1.0f;
432 if(hue > 1.0f) hue -= 1.0f;
433 return hue;
434}
435
436__OMP_DECLARE_SIMD__(aligned(RGB: 16))
437static inline __attribute__((always_inline)) void _dt_Hue_2_RGB(dt_aligned_pixel_t RGB, const float H, const float C, const float min)
438{
439 const float h = H * 6.0f;
440 const float i = floorf(h);
441 const float f = h - i;
442 const float fc = f * C;
443 const float top = C + min;
444 const float inc = fc + min;
445 const float dec = top - fc;
446 const size_t i_idx = (size_t)i;
447 if(i_idx == 0)
448 {
449 RGB[0] = top;
450 RGB[1] = inc;
451 RGB[2] = min;
452 }
453 else if(i_idx == 1)
454 {
455 RGB[0] = dec;
456 RGB[1] = top;
457 RGB[2] = min;
458 }
459 else if(i_idx == 2)
460 {
461 RGB[0] = min;
462 RGB[1] = top;
463 RGB[2] = inc;
464 }
465 else if(i_idx == 3)
466 {
467 RGB[0] = min;
468 RGB[1] = dec;
469 RGB[2] = top;
470 }
471 else if(i_idx == 4)
472 {
473 RGB[0] = inc;
474 RGB[1] = min;
475 RGB[2] = top;
476 }
477 else
478 {
479 RGB[0] = top;
480 RGB[1] = min;
481 RGB[2] = dec;
482 }
483}
484
485
486__OMP_DECLARE_SIMD__(aligned(RGB, HSL: 16))
487static inline __attribute__((always_inline)) void dt_RGB_2_HSL(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t HSL)
488{
489 const float min = fminf(RGB[0], fminf(RGB[1], RGB[2]));
490 const float max = fmaxf(RGB[0], fmaxf(RGB[1], RGB[2]));
491 const float delta = max - min;
492
493 const float L = (max + min) / 2.0f;
494 float H, S;
495
496 if(fabsf(max) > 1e-6f && fabsf(delta) > 1e-6f)
497 {
498 if(L < 0.5f)
499 S = delta / (max + min);
500 else
501 S = delta / (2.0f - max - min);
503 }
504 else
505 {
506 H = 0.0f;
507 S = 0.0f;
508 }
509
510 HSL[0] = H;
511 HSL[1] = S;
512 HSL[2] = L;
513}
514
515__OMP_DECLARE_SIMD__(aligned(HSL, RGB: 16))
516static inline __attribute__((always_inline)) void dt_HSL_2_RGB(const dt_aligned_pixel_t HSL, dt_aligned_pixel_t RGB)
517{
518 // almost straight from https://en.wikipedia.org/wiki/HSL_and_HSV
519 const float L = HSL[2];
520 float C;
521 if(L < 0.5f)
522 C = L * HSL[1];
523 else
524 C = (1.0f - L) * HSL[1];
525 const float m = L - C;
526 _dt_Hue_2_RGB(RGB, HSL[0], 2.0f * C, m);
527}
528
529
530__OMP_DECLARE_SIMD__(aligned(RGB, HSV: 16))
531static inline __attribute__((always_inline)) void dt_RGB_2_HSV(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t HSV)
532{
533 const float min = fminf(RGB[0], fminf(RGB[1], RGB[2]));
534 const float max = fmaxf(RGB[0], fmaxf(RGB[1], RGB[2]));
535 const float delta = max - min;
536
537 const float V = max;
538 float S, H;
539
540 if(fabsf(max) > 1e-6f && fabsf(delta) > 1e-6f)
541 {
542 S = delta / max;
544 }
545 else
546 {
547 S = 0.0f;
548 H = 0.0f;
549 }
550
551 HSV[0] = H;
552 HSV[1] = S;
553 HSV[2] = V;
554}
555
556__OMP_DECLARE_SIMD__(aligned(HSV, RGB: 16))
557static inline __attribute__((always_inline)) void dt_HSV_2_RGB(const dt_aligned_pixel_t HSV, dt_aligned_pixel_t RGB)
558{
559 // almost straight from https://en.wikipedia.org/wiki/HSL_and_HSV
560 const float C = HSV[1] * HSV[2];
561 const float m = HSV[2] - C;
563}
564
565
566__OMP_DECLARE_SIMD__(aligned(RGB, HCV: 16))
567static inline __attribute__((always_inline)) void dt_RGB_2_HCV(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t HCV)
568{
569 const float min = fminf(RGB[0], fminf(RGB[1], RGB[2]));
570 const float max = fmaxf(RGB[0], fmaxf(RGB[1], RGB[2]));
571 const float delta = max - min;
572
573 const float V = max;
574 float C, H;
575
576 if(fabsf(max) > 1e-6f && fabsf(delta) > 1e-6f)
577 {
578 C = delta;
580 }
581 else
582 {
583 C = 0.0f;
584 H = 0.0f;
585 }
586
587 HCV[0] = H;
588 HCV[1] = C;
589 HCV[2] = V;
590}
591
593static inline __attribute__((always_inline)) void dt_Lab_2_LCH(const dt_aligned_pixel_t Lab, dt_aligned_pixel_t LCH)
594{
595 float var_H = atan2f(Lab[2], Lab[1]);
596
597 if(var_H > 0.0f)
598 var_H = var_H / (2.0f * DT_M_PI_F);
599 else
600 var_H = 1.0f - fabsf(var_H) / (2.0f * DT_M_PI_F);
601
602 LCH[0] = Lab[0];
603 LCH[1] = hypotf(Lab[1], Lab[2]);
604 LCH[2] = var_H;
605}
606
607
609static inline __attribute__((always_inline)) void dt_LCH_2_Lab(const dt_aligned_pixel_t LCH, dt_aligned_pixel_t Lab)
610{
611 Lab[0] = LCH[0];
612 Lab[1] = cosf(2.0f * DT_M_PI_F * LCH[2]) * LCH[1];
613 Lab[2] = sinf(2.0f * DT_M_PI_F * LCH[2]) * LCH[1];
614}
615
616__OMP_DECLARE_SIMD__(aligned(rgb:16))
617static inline __attribute__((always_inline)) float dt_camera_rgb_luminance(const dt_aligned_pixel_t rgb)
618{
619 return (rgb[0] * 0.2225045f + rgb[1] * 0.7168786f + rgb[2] * 0.0606169f);
620}
622static inline __attribute__((always_inline)) void dt_XYZ_D50_2_XYZ_D65(const dt_aligned_pixel_t XYZ_D50, dt_aligned_pixel_t XYZ_D65)
623{
624 // Bradford adaptation matrix from http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
625#if 0
626 static const dt_colormatrix_t M = {
627 { 0.9555766f, -0.0230393f, 0.0631636f, 0.0f },
628 { -0.0282895f, 1.0099416f, 0.0210077f, 0.0f },
629 { 0.0122982f, -0.0204830f, 1.3299098f, 0.0f },
630 };
631#endif
632 static const dt_colormatrix_t M_transposed = {
633 { 0.9555766f, -0.0282895f, 0.0122982f, 0.0f },
634 { -0.0230393f, 1.0099416f, -0.0204830f, 0.0f },
635 { 0.0631636f, 0.0210077f, 1.3299098f, 0.0f },
636 };
637
639 XYZ_D65[x] = M_transposed[0][x] * XYZ_D50[0] + M_transposed[1][x] * XYZ_D50[1] + M_transposed[2][x] * XYZ_D50[2];
640}
642static inline __attribute__((always_inline)) void dt_XYZ_D65_2_XYZ_D50(const dt_aligned_pixel_t XYZ_D65, dt_aligned_pixel_t XYZ_D50)
643{
644 // Bradford adaptation matrix from http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
645#if 0
646 static const dt_colormatrix_t M = {
647 { 1.0478112f, 0.0228866f, -0.0501270f, 0.0f },
648 { 0.0295424f, 0.9904844f, -0.0170491f, 0.0f },
649 { -0.0092345f, 0.0150436f, 0.7521316f, 0.0f },
650 };
651#endif
652 static const dt_colormatrix_t M_transposed = {
653 { 1.0478112f, 0.0295424f, -0.0092345f, 0.0f },
654 { 0.0228866f, 0.9904844f, 0.0150436f, 0.0f },
655 { -0.0501270f, -0.0170491f, 0.7521316f, 0.0f },
656 };
657
659 XYZ_D50[x] = M_transposed[0][x] * XYZ_D65[0] + M_transposed[1][x] * XYZ_D65[1] + M_transposed[2][x] * XYZ_D65[2];
660}
661
662
671static inline __attribute__((always_inline)) void dt_XYZ_2_JzAzBz(const dt_aligned_pixel_t XYZ_D65, dt_aligned_pixel_t JzAzBz)
672{
673 const float b = 1.15f;
674 const float g = 0.66f;
675 const float c1 = 0.8359375f; // 3424 / 2^12
676 const float c2 = 18.8515625f; // 2413 / 2^7
677 const float c3 = 18.6875f; // 2392 / 2^7
678 const float n = 0.159301758f; // 2610 / 2^14
679 const float p = 134.034375f; // 1.7 x 2523 / 2^5
680 const float d = -0.56f;
681 const float d0 = 1.6295499532821566e-11f;
682 static const dt_colormatrix_t M = {
683 { 0.41478972f, 0.579999f, 0.0146480f, 0.0f },
684 { -0.2015100f, 1.120649f, 0.0531008f, 0.0f },
685 { -0.0166008f, 0.264800f, 0.6684799f, 0.0f },
686 };
687#if 0
688 static const dt_colormatrix_t A = {
689 { 0.5f, 0.5f, 0.0f, 0.0f },
690 { 3.524000f, -4.066708f, 0.542708f, 0.0f },
691 { 0.199076f, 1.096799f, -1.295875f, 0.0f },
692 };
693#endif
695 { 0.5f, 3.524000f, 0.199076f, 0.0f },
696 { 0.5f, -4.066708f, 1.096799f, 0.0f },
697 { 0.0f, 0.542708f, -1.295875f, 0.0f },
698 };
699
700 dt_aligned_pixel_t XYZ = { 0.0f, 0.0f, 0.0f, 0.0f };
701 dt_aligned_pixel_t LMS = { 0.0f, 0.0f, 0.0f, 0.0f };
702
703 // XYZ -> X'Y'Z
704 XYZ[0] = b * XYZ_D65[0] - (b - 1.0f) * XYZ_D65[2];
705 XYZ[1] = g * XYZ_D65[1] - (g - 1.0f) * XYZ_D65[0];
706 XYZ[2] = XYZ_D65[2];
707
708 // X'Y'Z -> L'M'S'
709 __OMP_SIMD__(aligned(LMS, XYZ:16) aligned(M:64))
710 for(int i = 0; i < 3; i++)
711 {
712 LMS[i] = M[i][0] * XYZ[0] + M[i][1] * XYZ[1] + M[i][2] * XYZ[2];
713 LMS[i] = powf(fmaxf(LMS[i] / 10000.f, 0.0f), n);
714 LMS[i] = powf((c1 + c2 * LMS[i]) / (1.0f + c3 * LMS[i]), p);
715 }
716
717 // L'M'S' -> Izazbz
719 JzAzBz[c] = A_transposed[0][c] * LMS[0] + A_transposed[1][c] * LMS[1] + A_transposed[2][c] * LMS[2];
720 // Iz -> Jz
721 JzAzBz[0] = fmaxf(((1.0f + d) * JzAzBz[0]) / (1.0f + d * JzAzBz[0]) - d0, 0.f);
722}
723
724static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
725dt_XYZ_2_JzAzBz_simd(const dt_aligned_pixel_simd_t XYZ_D65)
726{
727 const float b = 1.15f;
728 const float g = 0.66f;
729 const float c1 = 0.8359375f; // 3424 / 2^12
730 const float c2 = 18.8515625f; // 2413 / 2^7
731 const float c3 = 18.6875f; // 2392 / 2^7
732 const float n = 0.159301758f; // 2610 / 2^14
733 const float p = 134.034375f; // 1.7 x 2523 / 2^5
734 const float d = -0.56f;
735 const float d0 = 1.6295499532821566e-11f;
736 static const dt_colormatrix_t M_transposed = {
737 { 0.41478972f, -0.2015100f, -0.0166008f, 0.0f },
738 { 0.5799990f, 1.1206490f, 0.2648000f, 0.0f },
739 { 0.0146480f, 0.0531008f, 0.6684799f, 0.0f },
740 };
741 static const dt_colormatrix_t A_transposed = {
742 { 0.5f, 3.524000f, 0.199076f, 0.0f },
743 { 0.5f, -4.066708f, 1.096799f, 0.0f },
744 { 0.0f, 0.542708f, -1.295875f, 0.0f },
745 };
746
747 const dt_aligned_pixel_simd_t XYZ = {
748 b * XYZ_D65[0] - (b - 1.0f) * XYZ_D65[2],
749 g * XYZ_D65[1] - (g - 1.0f) * XYZ_D65[0],
750 XYZ_D65[2],
751 0.f
752 };
753
754 dt_aligned_pixel_simd_t LMS = dt_mat3x4_mul_vec4(XYZ,
755 dt_colormatrix_row_to_simd(M_transposed, 0),
756 dt_colormatrix_row_to_simd(M_transposed, 1),
757 dt_colormatrix_row_to_simd(M_transposed, 2));
758 for(int i = 0; i < 3; i++)
759 {
760 LMS[i] = powf(fmaxf(LMS[i] / 10000.f, 0.0f), n);
761 LMS[i] = powf((c1 + c2 * LMS[i]) / (1.0f + c3 * LMS[i]), p);
762 }
763
764 dt_aligned_pixel_simd_t JzAzBz = dt_mat3x4_mul_vec4(LMS,
765 dt_colormatrix_row_to_simd(A_transposed, 0),
766 dt_colormatrix_row_to_simd(A_transposed, 1),
767 dt_colormatrix_row_to_simd(A_transposed, 2));
768 JzAzBz[0] = fmaxf(((1.0f + d) * JzAzBz[0]) / (1.0f + d * JzAzBz[0]) - d0, 0.f);
769 JzAzBz[3] = 0.f;
770 return JzAzBz;
771}
772
773__OMP_DECLARE_SIMD__(aligned(JzAzBz, JzCzhz: 16))
774static inline __attribute__((always_inline)) void dt_JzAzBz_2_JzCzhz(const dt_aligned_pixel_t JzAzBz, dt_aligned_pixel_t JzCzhz)
775{
776 float var_H = atan2f(JzAzBz[2], JzAzBz[1]) / (2.0f * DT_M_PI_F);
777 JzCzhz[0] = JzAzBz[0];
778 JzCzhz[1] = hypotf(JzAzBz[1], JzAzBz[2]);
779 JzCzhz[2] = var_H >= 0.0f ? var_H : 1.0f + var_H;
780}
781
782__OMP_DECLARE_SIMD__(aligned(JzCzhz, JzAzBz: 16))
783static inline __attribute__((always_inline)) void dt_JzCzhz_2_JzAzBz(const dt_aligned_pixel_t JzCzhz, dt_aligned_pixel_t JzAzBz)
784{
785 JzAzBz[0] = JzCzhz[0];
786 JzAzBz[1] = cosf(2.0f * DT_M_PI_F * JzCzhz[2]) * JzCzhz[1];
787 JzAzBz[2] = sinf(2.0f * DT_M_PI_F * JzCzhz[2]) * JzCzhz[1];
788}
789
791static inline __attribute__((always_inline)) void dt_JzAzBz_2_XYZ(const dt_aligned_pixel_t JzAzBz, dt_aligned_pixel_t XYZ_D65)
792{
793 const float b = 1.15f;
794 const float g = 0.66f;
795 const float c1 = 0.8359375f; // 3424 / 2^12
796 const float c2 = 18.8515625f; // 2413 / 2^7
797 const float c3 = 18.6875f; // 2392 / 2^7
798 const float n_inv = 1.0f / 0.159301758f; // 2610 / 2^14
799 const float p_inv = 1.0f / 134.034375f; // 1.7 x 2523 / 2^5
800 const float d = -0.56f;
801 const float d0 = 1.6295499532821566e-11f;
803 { 1.9242264357876067f, -1.0047923125953657f, 0.0376514040306180f, 0.0f },
804 { 0.3503167620949991f, 0.7264811939316552f, -0.0653844229480850f, 0.0f },
805 { -0.0909828109828475f, -0.3127282905230739f, 1.5227665613052603f, 0.0f },
806 };
808 { 1.0f, 0.1386050432715393f, 0.0580473161561189f, 0.0f },
809 { 1.0f, -0.1386050432715393f, -0.0580473161561189f, 0.0f },
810 { 1.0f, -0.0960192420263190f, -0.8118918960560390f, 0.0f },
811 };
812
813 dt_aligned_pixel_t XYZ = { 0.0f, 0.0f, 0.0f, 0.0f };
814 dt_aligned_pixel_t LMS = { 0.0f, 0.0f, 0.0f, 0.0f };
815 dt_aligned_pixel_t IzAzBz = { 0.0f, 0.0f, 0.0f, 0.0f };
816
817 IzAzBz[0] = JzAzBz[0] + d0;
818 IzAzBz[0] = fmaxf(IzAzBz[0] / (1.0f + d - d * IzAzBz[0]), 0.f);
819 IzAzBz[1] = JzAzBz[1];
820 IzAzBz[2] = JzAzBz[2];
821
822 // IzAzBz -> LMS
823 __OMP_SIMD__(aligned(LMS, IzAzBz:16) aligned(AI:64))
824 for(int i = 0; i < 3; i++)
825 {
826 LMS[i] = AI[i][0] * IzAzBz[0] + AI[i][1] * IzAzBz[1] + AI[i][2] * IzAzBz[2];
827 LMS[i] = powf(fmaxf(LMS[i], 0.0f), p_inv);
828 LMS[i] = 10000.f * powf(fmaxf((c1 - LMS[i]) / (c3 * LMS[i] - c2), 0.0f), n_inv);
829 }
830
831 // LMS -> X'Y'Z
832 __OMP_SIMD__(aligned(LMS, XYZ:16) aligned(MI:64))
833 for(int i = 0; i < 3; i++) XYZ[i] = MI[i][0] * LMS[0] + MI[i][1] * LMS[1] + MI[i][2] * LMS[2];
834
835 // X'Y'Z -> XYZ_D65
836 XYZ_D65[0] = (XYZ[0] + (b - 1.0f) * XYZ[2]) / b;
837 XYZ_D65[1] = (XYZ[1] + (g - 1.0f) * XYZ_D65[0]) / g;
838 XYZ_D65[2] = XYZ[2];
839}
840
841static const dt_colormatrix_t MI_transposed = {
842 { 1.9242264357876067f, 0.3503167620949991f, -0.0909828109828475f, 0.0f },
843 { -1.0047923125953657f, 0.7264811939316552f, -0.3127282905230739f, 0.0f },
844 { 0.0376514040306180f, -0.0653844229480850f, 1.5227665613052603f, 0.0f },
845};
846static const dt_colormatrix_t AI_transposed = {
847 { 1.0f, 1.0f, 1.0f, 0.0f },
848 { 0.1386050432715393f, -0.1386050432715393f, -0.0960192420263190f, 0.0f },
849 { 0.0580473161561189f, -0.0580473161561189f, -0.8118918960560390f, 0.0f },
850};
851
852static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
853dt_JzAzBz_2_XYZ_simd(const dt_aligned_pixel_simd_t JzAzBz)
854{
855 const float b = 1.15f;
856 const float g = 0.66f;
857 const float c1 = 0.8359375f; // 3424 / 2^12
858 const float c2 = 18.8515625f; // 2413 / 2^7
859 const float c3 = 18.6875f; // 2392 / 2^7
860 const float n_inv = 1.0f / 0.159301758f; // 2610 / 2^14
861 const float p_inv = 1.0f / 134.034375f; // 1.7 x 2523 / 2^5
862 const float d = -0.56f;
863 const float d0 = 1.6295499532821566e-11f;
864
865 dt_aligned_pixel_simd_t IzAzBz = JzAzBz;
866 IzAzBz[0] = JzAzBz[0] + d0;
867 IzAzBz[0] = fmaxf(IzAzBz[0] / (1.0f + d - d * IzAzBz[0]), 0.f);
868 IzAzBz[3] = 0.f;
869
870 dt_aligned_pixel_simd_t LMS = dt_mat3x4_mul_vec4(IzAzBz,
871 dt_colormatrix_row_to_simd(AI_transposed, 0),
872 dt_colormatrix_row_to_simd(AI_transposed, 1),
873 dt_colormatrix_row_to_simd(AI_transposed, 2));
874 for(int i = 0; i < 3; i++)
875 {
876 LMS[i] = powf(fmaxf(LMS[i], 0.0f), p_inv);
877 LMS[i] = 10000.f * powf(fmaxf((c1 - LMS[i]) / (c3 * LMS[i] - c2), 0.0f), n_inv);
878 }
879
880 const dt_aligned_pixel_simd_t XYZ = dt_mat3x4_mul_vec4(LMS,
881 dt_colormatrix_row_to_simd(MI_transposed, 0),
882 dt_colormatrix_row_to_simd(MI_transposed, 1),
883 dt_colormatrix_row_to_simd(MI_transposed, 2));
884
885 return (dt_aligned_pixel_simd_t){
886 (XYZ[0] + (b - 1.0f) * XYZ[2]) / b,
887 (XYZ[1] + (g - 1.0f) * ((XYZ[0] + (b - 1.0f) * XYZ[2]) / b)) / g,
888 XYZ[2],
889 0.f
890 };
891}
892
893// Convert CIE 1931 2° XYZ D65 to CIE 2006 LMS D65 (cone space)
894/*
895* The CIE 1931 XYZ 2° observer D65 is converted to CIE 2006 LMS D65 using the approximation by
896* Richard A. Kirk, Chromaticity coordinates for graphic arts based on CIE 2006 LMS
897* with even spacing of Munsell colours
898* https://doi.org/10.2352/issn.2169-2629.2019.27.38
899*/
900
901static const dt_colormatrix_t XYZ_D65_to_LMS_2006_D65
902 = { { 0.257085f, 0.859943f, -0.031061f, 0.f },
903 { -0.394427f, 1.175800f, 0.106423f, 0.f },
904 { 0.064856f, -0.076250f, 0.559067f, 0.f } };
905
906static const dt_colormatrix_t LMS_2006_D65_to_XYZ_D65
907 = { { 1.80794659f, -1.29971660f, 0.34785879f, 0.f },
908 { 0.61783960f, 0.39595453f, -0.04104687f, 0.f },
909 { -0.12546960f, 0.20478038f, 1.74274183f, 0.f } };
910
911
912__OMP_DECLARE_SIMD__(aligned(LMS, XYZ: 16))
913static inline __attribute__((always_inline)) void XYZ_to_LMS(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t LMS)
914{
915 dot_product(XYZ, XYZ_D65_to_LMS_2006_D65, LMS);
916}
917
918static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
919XYZ_to_LMS_simd(const dt_aligned_pixel_simd_t XYZ)
920{
921 static const dt_colormatrix_t XYZ_D65_to_LMS_2006_D65_transposed = {
922 { 0.257085f, -0.394427f, 0.064856f, 0.f },
923 { 0.859943f, 1.175800f, -0.076250f, 0.f },
924 { -0.031061f, 0.106423f, 0.559067f, 0.f },
925 };
926 return dt_mat3x4_mul_vec4(XYZ,
927 dt_colormatrix_row_to_simd(XYZ_D65_to_LMS_2006_D65_transposed, 0),
928 dt_colormatrix_row_to_simd(XYZ_D65_to_LMS_2006_D65_transposed, 1),
929 dt_colormatrix_row_to_simd(XYZ_D65_to_LMS_2006_D65_transposed, 2));
930}
931
932__OMP_DECLARE_SIMD__(aligned(XYZ, LMS: 16))
933static inline __attribute__((always_inline)) void LMS_to_XYZ(const dt_aligned_pixel_t LMS, dt_aligned_pixel_t XYZ)
934{
935 dot_product(LMS, LMS_2006_D65_to_XYZ_D65, XYZ);
936}
937
938static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
939LMS_to_XYZ_simd(const dt_aligned_pixel_simd_t LMS)
940{
941 static const dt_colormatrix_t LMS_2006_D65_to_XYZ_D65_transposed = {
942 { 1.80794659f, 0.61783960f, -0.12546960f, 0.f },
943 { -1.29971660f, 0.39595453f, 0.20478038f, 0.f },
944 { 0.34785879f, -0.04104687f, 1.74274183f, 0.f },
945 };
946 return dt_mat3x4_mul_vec4(LMS,
947 dt_colormatrix_row_to_simd(LMS_2006_D65_to_XYZ_D65_transposed, 0),
948 dt_colormatrix_row_to_simd(LMS_2006_D65_to_XYZ_D65_transposed, 1),
949 dt_colormatrix_row_to_simd(LMS_2006_D65_to_XYZ_D65_transposed, 2));
950}
951
952/*
953* Convert from CIE 2006 LMS D65 to Filmlight RGB defined in
954* Richard A. Kirk, Chromaticity coordinates for graphic arts based on CIE 2006 LMS
955* with even spacing of Munsell colours
956* https://doi.org/10.2352/issn.2169-2629.2019.27.38
957*/
958
959static const dt_colormatrix_t filmlightRGB_D65_to_LMS_D65
960 = { { 0.95f, 0.38f, 0.00f, 0.f },
961 { 0.05f, 0.62f, 0.03f, 0.f },
962 { 0.00f, 0.00f, 0.97f, 0.f } };
963
964static const dt_colormatrix_t LMS_D65_to_filmlightRGB_D65
965 = { { 1.0877193f, -0.66666667f, 0.02061856f, 0.f },
966 { -0.0877193f, 1.66666667f, -0.05154639f, 0.f },
967 { 0.f, 0.f, 1.03092784f, 0.f } };
968
969__OMP_DECLARE_SIMD__(aligned(LMS, RGB: 16))
971{
972 dot_product(RGB, filmlightRGB_D65_to_LMS_D65, LMS);
973}
974
975static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
976gradingRGB_to_LMS_simd(const dt_aligned_pixel_simd_t RGB)
977{
978 static const dt_colormatrix_t filmlightRGB_D65_to_LMS_D65_transposed
979 = { { 0.95f, 0.05f, 0.00f, 0.f },
980 { 0.38f, 0.62f, 0.00f, 0.f },
981 { 0.00f, 0.03f, 0.97f, 0.f } };
982 return dt_mat3x4_mul_vec4(RGB,
983 dt_colormatrix_row_to_simd(filmlightRGB_D65_to_LMS_D65_transposed, 0),
984 dt_colormatrix_row_to_simd(filmlightRGB_D65_to_LMS_D65_transposed, 1),
985 dt_colormatrix_row_to_simd(filmlightRGB_D65_to_LMS_D65_transposed, 2));
986}
987
988__OMP_DECLARE_SIMD__(aligned(LMS, RGB: 16))
990{
991 dot_product(LMS, LMS_D65_to_filmlightRGB_D65, RGB);
992}
993
994static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
995LMS_to_gradingRGB_simd(const dt_aligned_pixel_simd_t LMS)
996{
997 static const dt_colormatrix_t LMS_D65_to_filmlightRGB_D65_transposed
998 = { { 1.0877193f, -0.0877193f, 0.f, 0.f },
999 { -0.66666667f, 1.66666667f, 0.f, 0.f },
1000 { 0.02061856f, -0.05154639f, 1.03092784f, 0.f } };
1001 return dt_mat3x4_mul_vec4(LMS,
1002 dt_colormatrix_row_to_simd(LMS_D65_to_filmlightRGB_D65_transposed, 0),
1003 dt_colormatrix_row_to_simd(LMS_D65_to_filmlightRGB_D65_transposed, 1),
1004 dt_colormatrix_row_to_simd(LMS_D65_to_filmlightRGB_D65_transposed, 2));
1005}
1006
1007
1008/*
1009* Re-express the Filmlight RGB triplet as Yrg luminance/chromacity coordinates
1010*/
1011
1012__OMP_DECLARE_SIMD__(aligned(LMS, Yrg: 16))
1013static inline __attribute__((always_inline)) void LMS_to_Yrg(const dt_aligned_pixel_t LMS, dt_aligned_pixel_t Yrg)
1014{
1015 // compute luminance
1016 const float Y = 0.68990272f * LMS[0] + 0.34832189f * LMS[1];
1017
1018 // normalize LMS
1019 const float a = LMS[0] + LMS[1] + LMS[2];
1020 dt_aligned_pixel_t lms = { 0.f };
1021 for_four_channels(c, aligned(LMS, lms : 16)) lms[c] = (a == 0.f) ? 0.f : LMS[c] / a;
1022
1023 // convert to Filmlight rgb (normalized)
1024 dt_aligned_pixel_t rgb = { 0.f };
1025 LMS_to_gradingRGB(lms, rgb);
1026
1027 Yrg[0] = Y;
1028 Yrg[1] = rgb[0];
1029 Yrg[2] = rgb[1];
1030}
1031
1032static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1033LMS_to_Yrg_simd(const dt_aligned_pixel_simd_t LMS)
1034{
1035 const float Y = 0.68990272f * LMS[0] + 0.34832189f * LMS[1];
1036 const float a = LMS[0] + LMS[1] + LMS[2];
1037 const float inv_a = (a == 0.f) ? 0.f : 1.f / a;
1038 const dt_aligned_pixel_simd_t lms = { LMS[0] * inv_a, LMS[1] * inv_a, LMS[2] * inv_a, 0.f };
1039 const dt_aligned_pixel_simd_t rgb = LMS_to_gradingRGB_simd(lms);
1040 return (dt_aligned_pixel_simd_t){ Y, rgb[0], rgb[1], 0.f };
1041}
1042
1043__OMP_DECLARE_SIMD__(aligned(Yrg, LMS: 16))
1044static inline __attribute__((always_inline)) void Yrg_to_LMS(const dt_aligned_pixel_t Yrg, dt_aligned_pixel_t LMS)
1045{
1046 const float Y = Yrg[0];
1047
1048 // reform rgb (normalized) from chroma
1049 const float r = Yrg[1];
1050 const float g = Yrg[2];
1051 const float b = 1.f - r - g;
1052 const dt_aligned_pixel_t rgb = { r, g, b, 0.f };
1053
1054 // convert to lms (normalized)
1055 dt_aligned_pixel_t lms = { 0.f };
1056 gradingRGB_to_LMS(rgb, lms);
1057
1058 // denormalize to LMS
1059 const float denom = (0.68990272f * lms[0] + 0.34832189f * lms[1]);
1060 const float a = (denom == 0.f) ? 0.f : Y / denom;
1061 for_four_channels(c, aligned(lms, LMS:16)) LMS[c] = lms[c] * a;
1062}
1063
1064static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1065Yrg_to_LMS_simd(const dt_aligned_pixel_simd_t Yrg)
1066{
1067 const dt_aligned_pixel_simd_t rgb = { Yrg[1], Yrg[2], 1.f - Yrg[1] - Yrg[2], 0.f };
1068 const dt_aligned_pixel_simd_t lms = gradingRGB_to_LMS_simd(rgb);
1069 const float denom = 0.68990272f * lms[0] + 0.34832189f * lms[1];
1070 const float a = (denom == 0.f) ? 0.f : Yrg[0] / denom;
1071 return (dt_aligned_pixel_simd_t){ lms[0] * a, lms[1] * a, lms[2] * a, 0.f };
1072}
1073
1074/*
1075* Re-express Filmlight Yrg in polar coordinates Ych
1076*/
1077
1078__OMP_DECLARE_SIMD__(aligned(Ych, Yrg: 16))
1079static inline __attribute__((always_inline)) void Yrg_to_Ych(const dt_aligned_pixel_t Yrg, dt_aligned_pixel_t Ych)
1080{
1081 const float Y = Yrg[0];
1082 // Subtract white point. These are the r, g coordinates of
1083 // sRGB (D50 adapted) (1, 1, 1) taken through
1084 // XYZ D50 -> CAT16 D50->D65 adaptation -> LMS 2006
1085 // -> grading RGB conversion.
1086 const float r = Yrg[1] - 0.21902143f;
1087 const float g = Yrg[2] - 0.54371398f;
1088 const float c = hypotf(g, r);
1089 const float h = atan2f(g, r);
1090 Ych[0] = Y;
1091 Ych[1] = c;
1092 Ych[2] = h;
1093}
1094
1095static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1096Yrg_to_Ych_simd(const dt_aligned_pixel_simd_t Yrg)
1097{
1098 const float r = Yrg[1] - 0.21902143f;
1099 const float g = Yrg[2] - 0.54371398f;
1100 return (dt_aligned_pixel_simd_t){ Yrg[0], hypotf(g, r), atan2f(g, r), 0.f };
1101}
1102
1103__OMP_DECLARE_SIMD__(aligned(Ych, Yrg: 16))
1104static inline __attribute__((always_inline)) void Ych_to_Yrg(const dt_aligned_pixel_t Ych, dt_aligned_pixel_t Yrg)
1105{
1106 const float Y = Ych[0];
1107 const float c = Ych[1];
1108 const float h = Ych[2];
1109 const float r = c * cosf(h) + 0.21902143f;
1110 const float g = c * sinf(h) + 0.54371398f;
1111 Yrg[0] = Y;
1112 Yrg[1] = r;
1113 Yrg[2] = g;
1114}
1115
1116static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1117Ych_to_Yrg_simd(const dt_aligned_pixel_simd_t Ych)
1118{
1119 return (dt_aligned_pixel_simd_t){
1120 Ych[0],
1121 Ych[1] * cosf(Ych[2]) + 0.21902143f,
1122 Ych[1] * sinf(Ych[2]) + 0.54371398f,
1123 0.f
1124 };
1125}
1126
1127/*
1128* Filmlight RGB utils functions
1129*/
1130
1131__OMP_DECLARE_SIMD__(aligned(Ych, RGB: 16))
1132static inline __attribute__((always_inline)) void Ych_to_gradingRGB(const dt_aligned_pixel_t Ych, dt_aligned_pixel_t RGB)
1133{
1134 dt_aligned_pixel_t Yrg = { 0.f };
1135 dt_aligned_pixel_t LMS = { 0.f };
1136 Ych_to_Yrg(Ych, Yrg);
1137 Yrg_to_LMS(Yrg, LMS);
1139}
1140
1141static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1142Ych_to_gradingRGB_simd(const dt_aligned_pixel_simd_t Ych)
1143{
1144 return LMS_to_gradingRGB_simd(Yrg_to_LMS_simd(Ych_to_Yrg_simd(Ych)));
1145}
1146
1147
1148__OMP_DECLARE_SIMD__(aligned(Ych, RGB: 16))
1149static inline __attribute__((always_inline)) void gradingRGB_to_Ych(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t Ych)
1150{
1151 dt_aligned_pixel_t Yrg = { 0.f };
1152 dt_aligned_pixel_t LMS = { 0.f };
1154 LMS_to_Yrg(LMS, Yrg);
1155 Yrg_to_Ych(Yrg, Ych);
1156}
1157
1158static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1159gradingRGB_to_Ych_simd(const dt_aligned_pixel_simd_t RGB)
1160{
1161 return Yrg_to_Ych_simd(LMS_to_Yrg_simd(gradingRGB_to_LMS_simd(RGB)));
1162}
1163
1164
1165__OMP_DECLARE_SIMD__(aligned(Ych, XYZ: 16))
1166static inline __attribute__((always_inline)) void XYZ_to_Ych(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t Ych)
1167{
1168 // WARNING: XYZ needs to be chroma-adapted to D65 before
1169 dt_aligned_pixel_t Yrg = { 0.f };
1170 dt_aligned_pixel_t LMS = { 0.f };
1171 XYZ_to_LMS(XYZ, LMS);
1172 LMS_to_Yrg(LMS, Yrg);
1173 Yrg_to_Ych(Yrg, Ych);
1174}
1175
1176static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1177XYZ_to_Ych_simd(const dt_aligned_pixel_simd_t XYZ)
1178{
1179 return Yrg_to_Ych_simd(LMS_to_Yrg_simd(XYZ_to_LMS_simd(XYZ)));
1180}
1181
1182
1183__OMP_DECLARE_SIMD__(aligned(Ych, XYZ: 16))
1184static inline __attribute__((always_inline)) void Ych_to_XYZ(const dt_aligned_pixel_t Ych, dt_aligned_pixel_t XYZ)
1185{
1186 // WARNING: XYZ is output in D65
1187 dt_aligned_pixel_t Yrg = { 0.f };
1188 dt_aligned_pixel_t LMS = { 0.f };
1189 Ych_to_Yrg(Ych, Yrg);
1190 Yrg_to_LMS(Yrg, LMS);
1191 LMS_to_XYZ(LMS, XYZ);
1192}
1193
1194static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1195Ych_to_XYZ_simd(const dt_aligned_pixel_simd_t Ych)
1196{
1197 return LMS_to_XYZ_simd(Yrg_to_LMS_simd(Ych_to_Yrg_simd(Ych)));
1198}
1199
1200
1201static inline __attribute__((always_inline)) void gamut_check_Yrg(dt_aligned_pixel_t Ych)
1202{
1203 // Check if the color fits in Yrg and LMS cone space
1204 // clip chroma at constant hue and luminance otherwise
1205
1206 // Do a test conversion to Yrg
1207 dt_aligned_pixel_t Yrg = { 0.f };
1208 Ych_to_Yrg(Ych, Yrg);
1209
1210 // Gamut-clip chroma in Yrg at constant hue and luminance
1211 // e.g. find the max chroma value that fits in gamut at the current hue
1212 // taken from colorbalancergb.c
1213 const float D65_r = 0.21902143f;
1214 const float D65_g = 0.54371398f;
1215
1216 float max_c = Ych[1];
1217 const float cos_h = cosf(Ych[2]);
1218 const float sin_h = sinf(Ych[2]);
1219
1220 if(Yrg[1] < 0.f)
1221 {
1222 max_c = fminf(-D65_r / cos_h, max_c);
1223 }
1224 if(Yrg[2] < 0.f)
1225 {
1226 max_c = fminf(-D65_g / sin_h, max_c);
1227 }
1228 if(Yrg[1] + Yrg[2] > 1.f)
1229 {
1230 max_c = fminf((1.f - D65_r - D65_g) / (cos_h + sin_h), max_c);
1231 }
1232
1233 // Overwrite chroma with the sanitized value
1234 Ych[1] = max_c;
1235}
1236
1237static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1238gamut_check_Yrg_simd(const dt_aligned_pixel_simd_t Ych)
1239{
1240 const dt_aligned_pixel_simd_t Yrg = Ych_to_Yrg_simd(Ych);
1241 const float cos_h = cosf(Ych[2]);
1242 const float sin_h = sinf(Ych[2]);
1243 float max_c = Ych[1];
1244
1245 if(Yrg[1] < 0.f) max_c = fminf(-0.21902143f / cos_h, max_c);
1246 if(Yrg[2] < 0.f) max_c = fminf(-0.54371398f / sin_h, max_c);
1247 if(Yrg[1] + Yrg[2] > 1.f) max_c = fminf((1.f - 0.21902143f - 0.54371398f) / (cos_h + sin_h), max_c);
1248
1249 return (dt_aligned_pixel_simd_t){ Ych[0], max_c, Ych[2], 0.f };
1250}
1251
1260static inline float Y_to_dt_UCS_L_star(const float Y)
1261{
1262 // WARNING: L_star needs to be < 2.098883786377, meaning Y needs to be < 3.875766378407574e+19
1263 const float Y_hat = powf(Y, 0.631651345306265f);
1264 return 2.098883786377f * Y_hat / (Y_hat + 1.12426773749357f);
1265}
1266
1267static inline float dt_UCS_L_star_to_Y(const float L_star)
1268{
1269 // WARNING: L_star needs to be < 2.098883786377, meaning Y needs to be < 3.875766378407574e+19
1270 return powf((1.12426773749357f * L_star / (2.098883786377f - L_star)), 1.5831518565279648f);
1271}
1272
1273
1274__OMP_DECLARE_SIMD__(aligned(xyY: 16))
1275static inline __attribute__((always_inline)) void xyY_to_dt_UCS_UV(const dt_aligned_pixel_t xyY, float UV_star_prime[2])
1276{
1277
1278 const dt_aligned_pixel_t x_factors = { -0.783941002840055f, 0.745273540913283f, 0.318707282433486f, 0.f };
1279 const dt_aligned_pixel_t y_factors = { 0.277512987809202f, -0.205375866083878f, 2.16743692732158f, 0.f };
1280 const dt_aligned_pixel_t offsets = { 0.153836578598858f, -0.165478376301988f, 0.291320554395942f, 0.f };
1281
1282 dt_aligned_pixel_t UVD = { 0.f };
1283 for_each_channel(c, aligned(xyY, UVD, x_factors, y_factors, offsets))
1284 UVD[c] = x_factors[c] * xyY[0] + y_factors[c] * xyY[1] + offsets[c];
1285
1286 UVD[0] /= UVD[2];
1287 UVD[1] /= UVD[2];
1288
1289 float UV_star[2] = { 0.f };
1290 const float factors[2] = { 1.39656225667f, 1.4513954287f };
1291 const float half_values[2] = { 1.49217352929f, 1.52488637914f };
1292 for(int c = 0; c < 2; c++)
1293 UV_star[c] = factors[c] * UVD[c] / (fabsf(UVD[c]) + half_values[c]);
1294
1295 // The following is equivalent to a 2D matrix product
1296 UV_star_prime[0] = -1.124983854323892f * UV_star[0] - 0.980483721769325f * UV_star[1];
1297 UV_star_prime[1] = 1.86323315098672f * UV_star[0] + 1.971853092390862f * UV_star[1];
1298}
1299
1300
1301__OMP_DECLARE_SIMD__(aligned(JCH: 16) uniform(L_white))
1302static inline void dt_UCS_LUV_to_JCH(const float L_star, const float L_white, const float UV_star_prime[2], dt_aligned_pixel_t JCH)
1303{
1304 const float M2 = UV_star_prime[0] * UV_star_prime[0] + UV_star_prime[1] * UV_star_prime[1]; // square of colorfulness M
1305
1306 // should be JCH[0] = powf(L_star / L_white), cz) but we treat only the case where cz = 1
1307 JCH[0] = L_star / L_white;
1308 JCH[1] = 15.932993652962535f * powf(L_star, 0.6523997524738018f) * powf(M2, 0.6007557017508491f) / L_white;
1309 JCH[2] = atan2f(UV_star_prime[1], UV_star_prime[0]);
1310}
1311
1312
1313__OMP_DECLARE_SIMD__(aligned(xyY, JCH: 16) uniform(L_white))
1314static inline __attribute__((always_inline)) void xyY_to_dt_UCS_JCH(const dt_aligned_pixel_t xyY, const float L_white, dt_aligned_pixel_t JCH)
1315{
1316 /*
1317 input :
1318 * xyY in normalized CIE XYZ for the 2° 1931 observer adapted for D65
1319 * L_white the lightness of white as dt UCS L* lightness
1320 * cz = 1 for standard pre-print proofing conditions with average surround and n = 20 %
1321 (background = middle grey, white = perfect diffuse white)
1322 range : xy in [0; 1], Y normalized for perfect diffuse white = 1
1323 */
1324
1325 float UV_star_prime[2];
1326 xyY_to_dt_UCS_UV(xyY, UV_star_prime);
1327 dt_UCS_LUV_to_JCH(Y_to_dt_UCS_L_star(xyY[2]), L_white, UV_star_prime, JCH);
1328}
1329
1330static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1331xyY_to_dt_UCS_JCH_simd(const dt_aligned_pixel_simd_t xyY, const float L_white)
1332{
1333 dt_aligned_pixel_t xyY_a = { xyY[0], xyY[1], xyY[2], 0.f };
1334 dt_aligned_pixel_t JCH = { 0.f };
1335 xyY_to_dt_UCS_JCH(xyY_a, L_white, JCH);
1336 return dt_load_simd_aligned(JCH);
1337}
1338
1339
1340__OMP_DECLARE_SIMD__(aligned(xyY, JCH: 16) uniform(L_white))
1341static inline __attribute__((always_inline)) void dt_UCS_JCH_to_xyY(const dt_aligned_pixel_t JCH, const float L_white, dt_aligned_pixel_t xyY)
1342{
1343 /*
1344 input :
1345 * xyY in normalized CIE XYZ for the 2° 1931 observer adapted for D65
1346 * L_white the lightness of white as dt UCS L* lightness
1347 * cz = 1 for standard pre-print proofing conditions with average surround and n = 20 %
1348 (background = middle grey, white = perfect diffuse white)
1349 range : xy in [0; 1], Y normalized for perfect diffuse white = 1
1350 */
1351
1352 // should be L_star = powf(JCH[0], 1.f / cz) * L_white but we treat only the case where cz = 1
1353 const float L_star = JCH[0] * L_white;
1354 const float M = powf(JCH[1] * L_white / (15.932993652962535f * powf(L_star, 0.6523997524738018f)), 0.8322850678616855f);
1355
1356 const float U_star_prime = M * cosf(JCH[2]);
1357 const float V_star_prime = M * sinf(JCH[2]);
1358
1359 // The following is equivalent to a 2D matrix product
1360 const float UV_star[2] = { -5.037522385190711f * U_star_prime - 2.504856328185843f * V_star_prime,
1361 4.760029407436461f * U_star_prime + 2.874012963239247f * V_star_prime };
1362
1363 float UV[2] = { 0.f };
1364 const float factors[2] = { 1.39656225667f, 1.4513954287f };
1365 const float half_values[2] = { 1.49217352929f, 1.52488637914f };
1366 for(int c = 0; c < 2; c++)
1367 UV[c] = -half_values[c] * UV_star[c] / (fabsf(UV_star[c]) - factors[c]);
1368
1369 const dt_aligned_pixel_t U_factors = { 0.167171472114775f, -0.150959086409163f, 0.940254742367256f, 0.f };
1370 const dt_aligned_pixel_t V_factors = { 0.141299802443708f, -0.155185060382272f, 1.000000000000000f, 0.f };
1371 const dt_aligned_pixel_t offsets = { -0.00801531300850582f, -0.00843312433578007f, -0.0256325967652889f, 0.f };
1372
1373 dt_aligned_pixel_t xyD = { 0.f };
1374 for_each_channel(c, aligned(xyD, UV, U_factors, V_factors, offsets))
1375 xyD[c] = U_factors[c] * UV[0] + V_factors[c] * UV[1] + offsets[c];
1376
1377 xyY[0] = xyD[0] / xyD[2];
1378 xyY[1] = xyD[1] / xyD[2];
1379 xyY[2] = dt_UCS_L_star_to_Y(L_star);
1380}
1381
1382static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1383dt_UCS_JCH_to_xyY_simd(const dt_aligned_pixel_simd_t JCH, const float L_white)
1384{
1385 dt_aligned_pixel_t JCH_a = { JCH[0], JCH[1], JCH[2], 0.f };
1386 dt_aligned_pixel_t xyY = { 0.f };
1387 dt_UCS_JCH_to_xyY(JCH_a, L_white, xyY);
1388 return dt_load_simd_aligned(xyY);
1389}
1390
1391
1392static inline __attribute__((always_inline)) void dt_UCS_JCH_to_HSB(const dt_aligned_pixel_t JCH, dt_aligned_pixel_t HSB)
1393{
1394 HSB[2] = JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f);
1395 HSB[1] = (HSB[2] > 0.f) ? JCH[1] / HSB[2] : 0.f;
1396 HSB[0] = JCH[2];
1397}
1398
1399static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1400dt_UCS_JCH_to_HSB_simd(const dt_aligned_pixel_simd_t JCH)
1401{
1402 const float brightness = JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f);
1403 return (dt_aligned_pixel_simd_t){ JCH[2], (brightness > 0.f) ? JCH[1] / brightness : 0.f, brightness, 0.f };
1404}
1405
1406
1407static inline __attribute__((always_inline)) void dt_UCS_HSB_to_JCH(const dt_aligned_pixel_t HSB, dt_aligned_pixel_t JCH)
1408{
1409 JCH[2] = HSB[0];
1410 JCH[1] = HSB[1] * HSB[2];
1411 JCH[0] = HSB[2] / (powf(JCH[1], 1.33654221029386f) + 1.f);
1412}
1413
1414static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1415dt_UCS_HSB_to_JCH_simd(const dt_aligned_pixel_simd_t HSB)
1416{
1417 const float chroma = HSB[1] * HSB[2];
1418 return (dt_aligned_pixel_simd_t){
1419 HSB[2] / (powf(chroma, 1.33654221029386f) + 1.f),
1420 chroma,
1421 HSB[0],
1422 0.f
1423 };
1424}
1425
1426
1427static inline __attribute__((always_inline)) void dt_UCS_JCH_to_HCB(const dt_aligned_pixel_t JCH, dt_aligned_pixel_t HCB)
1428{
1429 HCB[2] = JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f);
1430 HCB[1] = JCH[1];
1431 HCB[0] = JCH[2];
1432}
1433
1434static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1435dt_UCS_JCH_to_HCB_simd(const dt_aligned_pixel_simd_t JCH)
1436{
1437 return (dt_aligned_pixel_simd_t){
1438 JCH[2],
1439 JCH[1],
1440 JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f),
1441 0.f
1442 };
1443}
1444
1445
1446static inline __attribute__((always_inline)) void dt_UCS_HCB_to_JCH(const dt_aligned_pixel_t HCB, dt_aligned_pixel_t JCH)
1447{
1448 JCH[2] = HCB[0];
1449 JCH[1] = HCB[1];
1450 JCH[0] = HCB[2] / (powf(HCB[1], 1.33654221029386f) + 1.f);
1451}
1452
1453static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1454dt_UCS_HCB_to_JCH_simd(const dt_aligned_pixel_simd_t HCB)
1455{
1456 return (dt_aligned_pixel_simd_t){
1457 HCB[2] / (powf(HCB[1], 1.33654221029386f) + 1.f),
1458 HCB[1],
1459 HCB[0],
1460 0.f
1461 };
1462}
1463
1464
1465static inline __attribute__((always_inline)) void dt_UCS_HSB_to_HPW(const dt_aligned_pixel_t HSB, dt_aligned_pixel_t HPW)
1466{
1467 HPW[2] = sqrtf(HSB[1] * HSB[1] + HSB[2] * HSB[2]);
1468 HPW[1] = (HPW[2] > 0.f) ? HSB[1] / HPW[2] : 0.f;
1469 HPW[0] = HSB[0];
1470}
1471
1472
1473static inline __attribute__((always_inline)) void dt_UCS_HPW_to_HSB(const dt_aligned_pixel_t HPW, dt_aligned_pixel_t HSB)
1474{
1475 HSB[0] = HPW[0];
1476 HSB[1] = HPW[1] * HPW[2];
1477 HSB[2] = fmaxf(sqrtf(HPW[2] * HPW[2] - HSB[1] * HSB[1]), 0.f);
1478}
1479
1480static inline void dt_UCS_HSB_to_XYZ(const dt_aligned_pixel_t HSB, const float L_w, dt_aligned_pixel_t XYZ)
1481{
1482 // Quick path
1483 dt_aligned_pixel_t JCH = { 0.f };
1484 dt_aligned_pixel_t xyY = { 0.f };
1485
1486 dt_UCS_HSB_to_JCH(HSB, JCH);
1487 dt_UCS_JCH_to_xyY(JCH, L_w, xyY);
1489}
1490
1491#undef DT_RESTRICT
1492
1493// clang-format off
1494// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1495// vim: shiftwidth=2 expandtab tabstop=2 cindent
1496// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1497// clang-format on
static float dt_camera_rgb_luminance(const float4 rgb)
Definition color_conversion.h:166
static float4 dt_UCS_JCH_to_HCB(const float4 JCH)
Definition colorspace.h:911
static float4 dt_xyY_to_XYZ(const float4 xyY)
Definition colorspace.h:645
static float4 dt_UCS_HSB_to_JCH(const float4 HSB)
Definition colorspace.h:901
static float4 gradingRGB_to_LMS(const float4 RGB)
Definition colorspace.h:509
static void xyY_to_dt_UCS_UV(const float4 xyY, float UV_star_prime[2])
Definition colorspace.h:802
static float4 gamut_check_Yrg(float4 Ych)
Definition colorspace.h:748
static float4 LMS_to_XYZ(const float4 LMS)
Definition colorspace.h:491
static float4 Yrg_to_Ych(const float4 Yrg)
Definition colorspace.h:574
static float4 Ych_to_Yrg(const float4 Ych)
Definition colorspace.h:589
static float4 LMS_to_gradingRGB(const float4 LMS)
Definition colorspace.h:519
static float4 dt_UCS_JCH_to_HSB(const float4 JCH)
Definition colorspace.h:891
static float dt_UCS_L_star_to_Y(const float L_star)
Definition colorspace.h:796
static float4 dt_UCS_JCH_to_xyY(const float4 JCH, const float L_white)
Definition colorspace.h:849
static float4 Yrg_to_LMS(const float4 Yrg)
Definition colorspace.h:550
static float4 LMS_to_Yrg(const float4 LMS)
Definition colorspace.h:534
static float Y_to_dt_UCS_L_star(const float Y)
Definition colorspace.h:789
static float4 xyY_to_dt_UCS_JCH(const float4 xyY, const float L_white)
Definition colorspace.h:823
static float4 dt_XYZ_to_xyY(const float4 XYZ)
Definition colorspace.h:635
static float4 XYZ_to_LMS(const float4 XYZ)
Definition colorspace.h:480
static float4 dt_UCS_HCB_to_JCH(const float4 HCB)
Definition colorspace.h:921
#define A(y, x)
Definition colorspaces.c:186
dt_aligned_pixel_t LMS
Definition colorspaces_inline_conversions.h:701
const float fz
Definition colorspaces_inline_conversions.h:101
const float i
Definition colorspaces_inline_conversions.h:440
static dt_aligned_pixel_t xyY
Definition colorspaces_inline_conversions.h:110
static dt_aligned_pixel_t HSV
Definition colorspaces_inline_conversions.h:532
dt_prophotorgb_to_XYZ(rgb, XYZ)
dt_sRGB_to_linear_sRGB(sRGB, rgb)
static dt_aligned_pixel_t rgb
Definition colorspaces_inline_conversions.h:344
dt_xyY_to_Luv(xyY, Luv)
dt_XYZ_to_Rec709_D50(XYZ, rgb)
static float cbrt_5f(float f)
Definition colorspaces_inline_conversions.h:50
dt_Lab_to_XYZ(Lab, XYZ)
const float L
Definition colorspaces_inline_conversions.h:493
_dt_Hue_2_RGB(RGB, HSL[0], 2.0f *C, m)
const float V
Definition colorspaces_inline_conversions.h:537
const float g
Definition colorspaces_inline_conversions.h:674
dt_uvY_to_xyY(uvY, xyY)
const float fx
Definition colorspaces_inline_conversions.h:100
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
const dt_aligned_pixel_t f
Definition colorspaces_inline_conversions.h:102
static dt_aligned_pixel_t LCH
Definition colorspaces_inline_conversions.h:594
static float cbrta_halleyf(const float a, const float R)
Definition colorspaces_inline_conversions.h:58
static dt_aligned_pixel_t HCV
Definition colorspaces_inline_conversions.h:568
dt_XYZ_to_sRGB(XYZ, result)
static dt_aligned_pixel_t uvY
Definition colorspaces_inline_conversions.h:143
static const dt_colormatrix_t sRGB_to_xyz_transposed
Definition colorspaces_inline_conversions.h:374
const float D50[2] DT_ALIGNED_PIXEL
Definition colorspaces_inline_conversions.h:179
static const dt_colormatrix_t A_transposed
Definition colorspaces_inline_conversions.h:694
dt_XYZ_to_prophotorgb(XYZ, rgb)
static const dt_aligned_pixel_t d50
Definition colorspaces_inline_conversions.h:74
static dt_aligned_pixel_t HSL
Definition colorspaces_inline_conversions.h:488
static float cbf(const float x)
Definition colorspaces_inline_conversions.h:157
const float threshold
Definition colorspaces_inline_conversions.h:176
const float p
Definition colorspaces_inline_conversions.h:679
const float d0
Definition colorspaces_inline_conversions.h:681
const float d
Definition colorspaces_inline_conversions.h:680
static const float const float const float min
Definition colorspaces_inline_conversions.h:438
const float n_inv
Definition colorspaces_inline_conversions.h:798
const dt_colormatrix_t MI
Definition colorspaces_inline_conversions.h:802
static dt_aligned_pixel_t XYZ
Definition colorspaces_inline_conversions.h:98
static float lab_f(const float x)
Definition colorspaces_inline_conversions.h:66
const float inc
Definition colorspaces_inline_conversions.h:444
const float m
Definition colorspaces_inline_conversions.h:525
static dt_aligned_pixel_t sRGB
Definition colorspaces_inline_conversions.h:260
static float lab_f_inv(const float x)
Definition colorspaces_inline_conversions.h:88
dt_aligned_pixel_t IzAzBz
Definition colorspaces_inline_conversions.h:815
dt_Lch_to_Luv(Lch, Luv)
const float max
Definition colorspaces_inline_conversions.h:490
static dt_aligned_pixel_t XYZ_D65
Definition colorspaces_inline_conversions.h:623
static dt_aligned_pixel_t Lab
Definition colorspaces_inline_conversions.h:78
dt_Luv_to_xyY(Luv, xyY)
static dt_aligned_pixel_t XYZ_D50
Definition colorspaces_inline_conversions.h:315
const float p_inv
Definition colorspaces_inline_conversions.h:799
const dt_colormatrix_t AI
Definition colorspaces_inline_conversions.h:807
const dt_colormatrix_t dt_aligned_pixel_t out
Definition colorspaces_inline_conversions.h:42
const float c3
Definition colorspaces_inline_conversions.h:677
const float n
Definition colorspaces_inline_conversions.h:678
dt_Luv_to_Lch(Luv, Lch)
const size_t i_idx
Definition colorspaces_inline_conversions.h:446
dt_Rec709_to_XYZ_D50(rgb, XYZ)
dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0), dt_colormatrix_row_to_simd(matrix, 1), dt_colormatrix_row_to_simd(matrix, 2)))
const float c2
Definition colorspaces_inline_conversions.h:676
static const float const float C
Definition colorspaces_inline_conversions.h:437
static const dt_colormatrix_t xyz_to_srgb_transposed
Definition colorspaces_inline_conversions.h:391
const float top
Definition colorspaces_inline_conversions.h:443
dt_XYZ_to_Lab(XYZ, Lab)
static const int row
Definition colorspaces_inline_conversions.h:35
for(size_t c=0;c< 3;c++) sRGB[c]
static dt_aligned_pixel_t JzAzBz
Definition colorspaces_inline_conversions.h:672
const float c1
Definition colorspaces_inline_conversions.h:675
static dt_aligned_pixel_t Luv
Definition colorspaces_inline_conversions.h:165
static float _dt_RGB_2_Hue(const dt_aligned_pixel_t RGB, const float max, const float delta)
Definition colorspaces_inline_conversions.h:420
static dt_aligned_pixel_t RGB
Definition colorspaces_inline_conversions.h:327
static dt_aligned_pixel_t linearRGB
Definition colorspaces_inline_conversions.h:397
const dt_colormatrix_t matrix
Definition colorspaces_inline_conversions.h:40
const float fc
Definition colorspaces_inline_conversions.h:442
static dt_aligned_pixel_t Lch
Definition colorspaces_inline_conversions.h:188
const float delta
Definition colorspaces_inline_conversions.h:491
dt_xyY_to_uvY(xyY, uvY)
float S
Definition colorspaces_inline_conversions.h:494
const float dec
Definition colorspaces_inline_conversions.h:445
static const dt_colormatrix_t M
Definition colorspaces_inline_conversions.h:682
else var_H
Definition colorspaces_inline_conversions.h:600
static const float H
Definition colorspaces_inline_conversions.h:437
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define __OMP_SIMD__(...)
Definition darktable.h:262
#define for_each_channel(_var,...)
Definition darktable.h:662
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
#define __OMP_DECLARE_SIMD__(...)
Definition darktable.h:263
#define for_four_channels(_var,...)
Definition darktable.h:664
static const float x
Definition iop_profile.h:235
#define R
#define DT_M_PI_F
Definition math.h:52
#define CLIP(x)
Definition math.h:81
#define M_PI
Definition math.h:45
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:33
c
Definition derive_filmic_v6_gamut_mapping.py:35
lms
Definition derive_filmic_v6_gamut_mapping.py:51
Yrg
Definition derive_filmic_v6_gamut_mapping.py:38
Y
Definition derive_filmic_v6_gamut_mapping.py:35
tuple b
Definition extract_wb.py:190
float dt_aligned_pixel_t[4]
Definition noiseprofile.c:28
const float r
Definition src/develop/noise_generator.h:101