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