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#ifdef __SSE2__
31#include "common/sse.h"
32#endif
33
34#include "common/matrices.h"
35#include "common/math.h"
36
37#ifdef __SSE2__
38static inline __m128 lab_f_inv_m(const __m128 x)
39{
40 const __m128 epsilon = _mm_set1_ps(0.20689655172413796f); // cbrtf(216.0f/24389.0f);
41 const __m128 kappa_rcp_x16 = _mm_set1_ps(16.0f * 27.0f / 24389.0f);
42 const __m128 kappa_rcp_x116 = _mm_set1_ps(116.0f * 27.0f / 24389.0f);
43
44 // x > epsilon
45 const __m128 res_big = x * x * x;
46 // x <= epsilon
47 const __m128 res_small = kappa_rcp_x116 * x - kappa_rcp_x16;
48
49 // blend results according to whether each component is > epsilon or not
50 const __m128 mask = _mm_cmpgt_ps(x, epsilon);
51 return _mm_or_ps(_mm_and_ps(mask, res_big), _mm_andnot_ps(mask, res_small));
52}
53
55static inline __m128 dt_Lab_to_XYZ_sse2(const __m128 Lab)
56{
57 const __m128 d50 = _mm_set_ps(0.0f, 0.8249f, 1.0f, 0.9642f);
58 const __m128 coef = _mm_set_ps(0.0f, -1.0f / 200.0f, 1.0f / 116.0f, 1.0f / 500.0f);
59 const __m128 offset = _mm_set1_ps(0.137931034f);
60
61 // last component ins shuffle taken from 1st component of Lab to make sure it is not nan, so it will become
62 // 0.0f in f
63 const __m128 f = _mm_shuffle_ps(Lab, Lab, _MM_SHUFFLE(0, 2, 0, 1)) * coef;
64
65 return d50 * lab_f_inv_m(f + _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 3, 1)) + offset);
66}
67
68static inline __m128 lab_f_m_sse2(const __m128 x)
69{
70 const __m128 epsilon = _mm_set1_ps(216.0f / 24389.0f);
71 const __m128 kappa = _mm_set1_ps(24389.0f / 27.0f);
72
73 // calculate as if x > epsilon : result = cbrtf(x)
74 // approximate cbrtf(x):
75 const __m128 a = _mm_castsi128_ps(
76 _mm_add_epi32(_mm_cvtps_epi32(_mm_div_ps(_mm_cvtepi32_ps(_mm_castps_si128(x)), _mm_set1_ps(3.0f))),
77 _mm_set1_epi32(709921077)));
78 const __m128 a3 = a * a * a;
79 const __m128 res_big = a * (a3 + x + x) / (a3 + a3 + x);
80
81 // calculate as if x <= epsilon : result = (kappa*x+16)/116
82 const __m128 res_small = (kappa * x + _mm_set1_ps(16.0f)) / _mm_set1_ps(116.0f);
83
84 // blend results according to whether each component is > epsilon or not
85 const __m128 mask = _mm_cmpgt_ps(x, epsilon);
86 return _mm_or_ps(_mm_and_ps(mask, res_big), _mm_andnot_ps(mask, res_small));
87}
88
90static inline __m128 dt_XYZ_to_Lab_sse2(const __m128 XYZ)
91{
92 const __m128 d50_inv = _mm_set_ps(1.0f, 0.8249f, 1.0f, 0.9642f);
93 const __m128 coef = _mm_set_ps(0.0f, 200.0f, 500.0f, 116.0f);
94 const __m128 f = lab_f_m_sse2(XYZ / d50_inv);
95 // because d50_inv.z is 0.0f, lab_f(0) == 16/116, so Lab[0] = 116*f[0] - 16 equal to 116*(f[0]-f[3])
96 return coef * (_mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 1, 0, 1)) - _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 2, 1, 3)));
97}
98
100// see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices
101static inline __m128 dt_XYZ_to_sRGB_sse2(__m128 XYZ)
102{
103 // XYZ -> sRGB matrix, D65
104 const __m128 xyz_to_srgb_0 = _mm_setr_ps(3.1338561f, -0.9787684f, 0.0719453f, 0.0f);
105 const __m128 xyz_to_srgb_1 = _mm_setr_ps(-1.6168667f, 1.9161415f, -0.2289914f, 0.0f);
106 const __m128 xyz_to_srgb_2 = _mm_setr_ps(-0.4906146f, 0.0334540f, 1.4052427f, 0.0f);
107
108 __m128 rgb
109 = xyz_to_srgb_0 * _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(0, 0, 0, 0)) +
110 xyz_to_srgb_1 * _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(1, 1, 1, 1)) +
111 xyz_to_srgb_2 * _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(2, 2, 2, 2));
112
113 // linear sRGB -> gamma corrected sRGB
114 __m128 mask = _mm_cmple_ps(rgb, _mm_set1_ps(0.0031308));
115 __m128 rgb0 = _mm_set1_ps(12.92) * rgb;
116 __m128 rgb1 = _mm_set1_ps(1.0 + 0.055) * _mm_pow_ps1(rgb, 1.0 / 2.4) - _mm_set1_ps(0.055);
117 return _mm_or_ps(_mm_and_ps(mask, rgb0), _mm_andnot_ps(mask, rgb1));
118}
119
120static inline __m128 dt_sRGB_to_XYZ_sse2(__m128 rgb)
121{
122 // sRGB -> XYZ matrix, D65
123 const __m128 srgb_to_xyz_0 = _mm_setr_ps(0.4360747f, 0.2225045f, 0.0139322f, 0.0f);
124 const __m128 srgb_to_xyz_1 = _mm_setr_ps(0.3850649f, 0.7168786f, 0.0971045f, 0.0f);
125 const __m128 srgb_to_xyz_2 = _mm_setr_ps(0.1430804f, 0.0606169f, 0.7141733f, 0.0f);
126
127 // gamma corrected sRGB -> linear sRGB
128 __m128 mask = _mm_cmple_ps(rgb, _mm_set1_ps(0.04045));
129 __m128 rgb0 = _mm_div_ps(rgb, _mm_set1_ps(12.92));
130 __m128 rgb1 = _mm_pow_ps1(_mm_div_ps(_mm_add_ps(rgb, _mm_set1_ps(0.055)), _mm_set1_ps(1 + 0.055)), 2.4);
131 rgb = _mm_or_ps(_mm_and_ps(mask, rgb0), _mm_andnot_ps(mask, rgb1));
132
133 __m128 XYZ
134 = srgb_to_xyz_0 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(0, 0, 0, 0)) +
135 srgb_to_xyz_1 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(1, 1, 1, 1)) +
136 srgb_to_xyz_2 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(2, 2, 2, 2));
137 return XYZ;
138}
139
141// see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices
142static inline __m128 dt_XYZ_to_prophotoRGB_sse2(__m128 XYZ)
143{
144 // XYZ -> prophotoRGB matrix, D50
145 const __m128 xyz_to_rgb_0 = _mm_setr_ps( 1.3459433f, -0.5445989f, 0.0000000f, 0.0f);
146 const __m128 xyz_to_rgb_1 = _mm_setr_ps(-0.2556075f, 1.5081673f, 0.0000000f, 0.0f);
147 const __m128 xyz_to_rgb_2 = _mm_setr_ps(-0.0511118f, 0.0205351f, 1.2118128f, 0.0f);
148
149 __m128 rgb
150 = xyz_to_rgb_0 * _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(0, 0, 0, 0)) +
151 xyz_to_rgb_1 * _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(1, 1, 1, 1)) +
152 xyz_to_rgb_2 * _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(2, 2, 2, 2));
153 return rgb;
154}
155
157// see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices
158static inline __m128 dt_prophotoRGB_to_XYZ_sse2(__m128 rgb)
159{
160 // prophotoRGB -> XYZ matrix, D50
161 const __m128 rgb_to_xyz_0 = _mm_setr_ps(0.7976749f, 0.2880402f, 0.0000000f, 0.0f);
162 const __m128 rgb_to_xyz_1 = _mm_setr_ps(0.1351917f, 0.7118741f, 0.0000000f, 0.0f);
163 const __m128 rgb_to_xyz_2 = _mm_setr_ps(0.0313534f, 0.0000857f, 0.8252100f, 0.0f);
164
165 __m128 XYZ
166 = rgb_to_xyz_0 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(0, 0, 0, 0)) +
167 rgb_to_xyz_1 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(1, 1, 1, 1)) +
168 rgb_to_xyz_2 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(2, 2, 2, 2));
169 return XYZ;
170}
171#endif
172
173static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
174dt_colormatrix_row_to_simd(const dt_colormatrix_t matrix, const int row)
175{
176 return (dt_aligned_pixel_simd_t){ matrix[row][0], matrix[row][1], matrix[row][2], matrix[row][3] };
177}
178
179#ifdef _OPENMP
180#pragma omp declare simd aligned(in,out:16) aligned(matrix:64) uniform(matrix)
181#endif
182static inline __attribute__((always_inline)) void dt_apply_transposed_color_matrix(const dt_aligned_pixel_t in, const dt_colormatrix_t matrix,
183 dt_aligned_pixel_t out)
184{
185 const dt_aligned_pixel_simd_t vin = dt_load_simd_aligned(in);
186 dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0),
187 dt_colormatrix_row_to_simd(matrix, 1),
188 dt_colormatrix_row_to_simd(matrix, 2)));
189}
190
191#ifdef _OPENMP
192#pragma omp declare simd simdlen(4)
193#endif
194static inline float cbrt_5f(float f)
195{
196 uint32_t * const p = (uint32_t *)&f;
197 *p = *p / 3 + 709921077;
198 return f;
199}
200
201#ifdef _OPENMP
202#pragma omp declare simd simdlen(4)
203#endif
204static inline float cbrta_halleyf(const float a, const float R)
205{
206 const float a3 = a * a * a;
207 const float b = a * (a3 + R + R) / (a3 + a3 + R);
208 return b;
209}
210
211#ifdef _OPENMP
212#pragma omp declare simd simdlen(4)
213#endif
214static inline float lab_f(const float x)
215{
216 const float epsilon = 216.0f / 24389.0f;
217 const float kappa = 24389.0f / 27.0f;
218 return (x > epsilon) ? cbrta_halleyf(cbrt_5f(x), x) : (kappa * x + 16.0f) / 116.0f;
219}
220
222static const dt_aligned_pixel_t d50 = { 0.9642f, 1.0f, 0.8249f };
223
224#ifdef _OPENMP
225#pragma omp declare simd aligned(Lab, XYZ:16) uniform(Lab, XYZ)
226#endif
227static inline __attribute__((always_inline)) void dt_XYZ_to_Lab(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t Lab)
228{
229 dt_aligned_pixel_t f;
231 f[i] = lab_f(XYZ[i] / d50[i]);
232 Lab[0] = 116.0f * f[1] - 16.0f;
233 Lab[1] = 500.0f * (f[0] - f[1]);
234 Lab[2] = 200.0f * (f[1] - f[2]);
235}
236
237#ifdef _OPENMP
238#pragma omp declare simd simdlen(4)
239#endif
240static inline float lab_f_inv(const float x)
241{
242 const float epsilon = 0.20689655172413796f; // cbrtf(216.0f/24389.0f);
243 const float kappa = 24389.0f / 27.0f;
244 return (x > epsilon) ? x * x * x : (116.0f * x - 16.0f) / kappa;
245}
246
248#ifdef _OPENMP
249#pragma omp declare simd aligned(Lab, XYZ:16) uniform(Lab, XYZ)
250#endif
251static inline __attribute__((always_inline)) void dt_Lab_to_XYZ(const dt_aligned_pixel_t Lab, dt_aligned_pixel_t XYZ)
252{
253 const float fy = (Lab[0] + 16.0f) / 116.0f;
254 const float fx = Lab[1] / 500.0f + fy;
255 const float fz = fy - Lab[2] / 200.0f;
256 const dt_aligned_pixel_t f = { fx, fy, fz };
258 XYZ[c] = d50[c] * lab_f_inv(f[c]);
259}
260
261
262#ifdef _OPENMP
263#pragma omp declare simd aligned(xyY, XYZ:16)
264#endif
265static inline __attribute__((always_inline)) void dt_XYZ_to_xyY(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t xyY)
266{
267 const float sum = XYZ[0] + XYZ[1] + XYZ[2];
268 xyY[0] = XYZ[0] / sum;
269 xyY[1] = XYZ[1] / sum;
270 xyY[2] = XYZ[1];
271}
272
273static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
274dt_XYZ_to_xyY_simd(const dt_aligned_pixel_simd_t XYZ)
275{
276 const float sum = XYZ[0] + XYZ[1] + XYZ[2];
277 return (dt_aligned_pixel_simd_t){ XYZ[0] / sum, XYZ[1] / sum, XYZ[1], 0.f };
278}
279
280
281#ifdef _OPENMP
282#pragma omp declare simd aligned(xyY, XYZ:16)
283#endif
284static inline __attribute__((always_inline)) void dt_xyY_to_XYZ(const dt_aligned_pixel_t xyY, dt_aligned_pixel_t XYZ)
285{
286 XYZ[0] = xyY[2] * xyY[0] / xyY[1];
287 XYZ[1] = xyY[2];
288 XYZ[2] = xyY[2] * (1.f - xyY[0] - xyY[1]) / xyY[1];
289}
290
291static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
292dt_xyY_to_XYZ_simd(const dt_aligned_pixel_simd_t xyY)
293{
294 const float y_over_x = xyY[2] / xyY[1];
295 return (dt_aligned_pixel_simd_t){ y_over_x * xyY[0], xyY[2], y_over_x * (1.f - xyY[0] - xyY[1]), 0.f };
296}
297
298
299#ifdef _OPENMP
300#pragma omp declare simd aligned(xyY, uvY:16)
301#endif
302static inline __attribute__((always_inline)) void dt_xyY_to_uvY(const dt_aligned_pixel_t xyY, dt_aligned_pixel_t uvY)
303{
304 // This is the linear part of the chromaticity transform from CIE L*u*v* e.g. u'v'.
305 // See https://en.wikipedia.org/wiki/CIELUV
306 // It rescales the chromaticity diagram xyY in a more perceptual way,
307 // but it is still not hue-linear and not perfectly perceptual.
308 // As such, it is the only radiometricly-accurate representation of hue non-linearity in human vision system.
309 // Use it for "hue preserving" (as much as possible) gamut mapping in scene-referred space
310 const float denominator = -2.f * xyY[0] + 12.f * xyY[1] + 3.f;
311 uvY[0] = 4.f * xyY[0] / denominator; // u'
312 uvY[1] = 9.f * xyY[1] / denominator; // v'
313 uvY[2] = xyY[2]; // Y
314}
315
316#ifdef _OPENMP
317#pragma omp declare simd
318#endif
319static inline float cbf(const float x)
320{
321 return x * x * x;
322}
323
324
325#ifdef _OPENMP
326#pragma omp declare simd aligned(xyY, Luv:16)
327#endif
328static inline __attribute__((always_inline)) void dt_xyY_to_Luv(const dt_aligned_pixel_t xyY, dt_aligned_pixel_t Luv)
329{
330 // This is the second, non-linear, part of the the 1976 CIE L*u*v* transform.
331 // See https://en.wikipedia.org/wiki/CIELUV
332 // It is intended to provide perceptual hue-linear-ish controls and settings for more intuitive GUI.
333 // Don't ever use it for pixel-processing, it sucks, it's old, it kills kittens and makes your mother cry.
334 // Seriously, don't.
335 // You need to convert Luv parameters to XYZ or RGB and properly process pixels in RGB or XYZ or related spaces.
336 dt_aligned_pixel_t uvY;
338
339 // We assume Yn == 1 == peak luminance
340 const float threshold = cbf(6.0f / 29.0f);
341 Luv[0] = (uvY[2] <= threshold) ? cbf(29.0f / 3.0f) * uvY[2] : 116.0f * cbrtf(uvY[2]) - 16.f;
342
343 const float D50[2] DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
344 Luv[1] = 13.f * Luv[0] * (uvY[0] - D50[0]); // u*
345 Luv[2] = 13.f * Luv[0] * (uvY[1] - D50[1]); // v*
346
347 // Output is in [0; 100] for all channels
348}
349
350
351static inline __attribute__((always_inline)) void dt_Luv_to_Lch(const dt_aligned_pixel_t Luv, dt_aligned_pixel_t Lch)
352{
353 Lch[0] = Luv[0]; // L stays L
354 Lch[1] = hypotf(Luv[2], Luv[1]); // chroma radius
355 Lch[2] = atan2f(Luv[2], Luv[1]); // hue angle
356 Lch[2] = (Lch[2] < 0.f) ? 2.f * M_PI + Lch[2] : Lch[2]; // ensure angle is positive modulo 2 pi
357}
358
359
360static inline __attribute__((always_inline)) void dt_xyY_to_Lch(const dt_aligned_pixel_t xyY, dt_aligned_pixel_t Lch)
361{
362 dt_aligned_pixel_t Luv;
365}
366
367
368#ifdef _OPENMP
369#pragma omp declare simd aligned(uvY, xyY:16)
370#endif
371static inline __attribute__((always_inline)) void dt_uvY_to_xyY(const dt_aligned_pixel_t uvY, dt_aligned_pixel_t xyY)
372{
373 // This is the linear part of chromaticity transform from CIE L*u*v* e.g. u'v'.
374 // See https://en.wikipedia.org/wiki/CIELUV
375 // It rescales the chromaticity diagram xyY in a more perceptual way,
376 // but it is still not hue-linear and not perfectly perceptual.
377 // As such, it is the only radiometricly-accurate representation of hue non-linearity in human vision system.
378 // Use it for "hue preserving" (as much as possible) gamut mapping in scene-referred space
379 const float denominator = 6.0f * uvY[0] - 16.f * uvY[1] + 12.0f;
380 xyY[0] = 9.f * uvY[0] / denominator; // x
381 xyY[1] = 4.f * uvY[1] / denominator; // y
382 xyY[2] = uvY[2]; // Y
383}
384
385
386#ifdef _OPENMP
387#pragma omp declare simd aligned(xyY, Luv:16)
388#endif
389static inline __attribute__((always_inline)) void dt_Luv_to_xyY(const dt_aligned_pixel_t Luv, dt_aligned_pixel_t xyY)
390{
391 // This is the second, non-linear, part of the the 1976 CIE L*u*v* transform.
392 // See https://en.wikipedia.org/wiki/CIELUV
393 // It is intended to provide perceptual hue-linear-ish controls and settings for more intuitive GUI.
394 // Don't ever use it for pixel-processing, it sucks, it's old, it kills kittens and makes your mother cry.
395 // Seriously, don't.
396 // You need to convert Luv parameters to XYZ or RGB and properly process pixels in RGB or XYZ or related spaces.
397 dt_aligned_pixel_t uvY;
398
399 // We assume Yn == 1 == peak luminance
400 static const float threshold = 8.0f;
401 uvY[2] = (Luv[0] <= threshold) ? Luv[0] * cbf(3.f / 29.f) : cbf((Luv[0] + 16.f) / 116.f);
402
403 static const float D50[2] DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
404 uvY[0] = Luv[1] / (Luv[0] * 13.f) + D50[0]; // u' = u* / 13 L + u_n
405 uvY[1] = Luv[2] / (Luv[0] * 13.f) + D50[1]; // v' = v* / 13 L + v_n
406
408 // Output is normalized for all channels
409}
410
411static inline __attribute__((always_inline)) void dt_Lch_to_Luv(const dt_aligned_pixel_t Lch, dt_aligned_pixel_t Luv)
412{
413 Luv[0] = Lch[0]; // L stays L
414 Luv[1] = Lch[1] * cosf(Lch[2]); // radius * cos(angle)
415 Luv[2] = Lch[1] * sinf(Lch[2]); // radius * sin(angle)
416}
417
418static inline __attribute__((always_inline)) void dt_Lch_to_xyY(const dt_aligned_pixel_t Lch, dt_aligned_pixel_t xyY)
419{
420 dt_aligned_pixel_t Luv;
423}
424
426#ifdef _OPENMP
427#pragma omp declare simd
428#endif
429static inline __attribute__((always_inline)) void dt_XYZ_to_Rec709_D50(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
430{
431 // transpose and pad the conversion matrix to enable vectorization
432 static const dt_colormatrix_t xyz_to_srgb_matrix_transposed =
433 { { 3.1338561f, -0.9787684f, 0.0719453f, 0.0f },
434 { -1.6168667f, 1.9161415f, -0.2289914f, 0.0f },
435 { -0.4906146f, 0.0334540f, 1.4052427f, 0.0f } };
436
437 // XYZ -> linear sRGB
438 dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB);
439}
440
441
443#ifdef _OPENMP
444#pragma omp declare simd
445#endif
446static inline __attribute__((always_inline)) void dt_XYZ_to_Rec709_D65(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
447{
448 // linear sRGB == Rec709 with no gamma
449 // transpose and pad the conversion matrix to enable vectorization
451 { 3.2404542f, -0.9692660f, 0.0556434f, 0.0f },
452 { -1.5371385f, 1.8760108f, -0.2040259f, 0.0f },
453 { -0.4985314f, 0.0415560f, 1.0572252f, 0.0f },
454 };
455 // XYZ -> linear sRGB
457}
458
459
461#ifdef _OPENMP
462#pragma omp declare simd aligned(XYZ, sRGB)
463#endif
464static inline __attribute__((always_inline)) void dt_XYZ_to_sRGB(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
465{
466 // XYZ -> linear sRGB
467 dt_aligned_pixel_t rgb;
469 // linear sRGB -> gamma corrected sRGB
470 for(size_t c = 0; c < 3; c++)
471 sRGB[c] = rgb[c] <= 0.0031308f ? 12.92f * rgb[c] : (1.0f + 0.055f) * powf(rgb[c], 1.0f / 2.4f) - 0.055f;
472}
473
474
476#ifdef _OPENMP
477#pragma omp declare simd aligned(XYZ, sRGB)
478#endif
479static inline __attribute__((always_inline)) void dt_XYZ_to_sRGB_clipped(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
480{
481 dt_aligned_pixel_t result;
483
485 sRGB[c] = CLIP(result[c]);
486}
487
488
489#ifdef _OPENMP
490#pragma omp declare simd aligned(sRGB, XYZ_D50: 16)
491#endif
492static inline __attribute__((always_inline)) void dt_Rec709_to_XYZ_D50(const dt_aligned_pixel_t sRGB, dt_aligned_pixel_t XYZ_D50)
493{
494 // Conversion matrix from http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
495 // (transpose and pad the conversion matrix to enable vectorization)
496 static const dt_colormatrix_t M = {
497 { 0.4360747f, 0.2225045f, 0.0139322f, 0.0f },
498 { 0.3850649f, 0.7168786f, 0.0971045f, 0.0f },
499 { 0.1430804f, 0.0606169f, 0.7141733f, 0.0f }
500 };
502}
503
504
505#ifdef _OPENMP
506#pragma omp declare simd aligned(sRGB, RGB)
507#endif
508static inline __attribute__((always_inline)) void dt_sRGB_to_linear_sRGB(const dt_aligned_pixel_t sRGB, dt_aligned_pixel_t RGB)
509{
510 // gamma corrected sRGB -> linear sRGB
511 for(int c = 0; c < 3; c++)
512 RGB[c] = sRGB[c] <= 0.04045f ? sRGB[c] / 12.92f : powf((sRGB[c] + 0.055f) / (1.0f + 0.055f), 2.4f);
513}
514
515#ifdef _OPENMP
516#pragma omp declare simd aligned(sRGB, XYZ)
517#endif
518static inline __attribute__((always_inline)) void dt_sRGB_to_XYZ(const dt_aligned_pixel_t sRGB, dt_aligned_pixel_t XYZ)
519{
520 dt_aligned_pixel_t rgb = { 0 };
522 // linear sRGB -> XYZ
524}
525
526#ifdef _OPENMP
527#pragma omp declare simd aligned(XYZ,rgb)
528#endif
529static inline __attribute__((always_inline)) void dt_XYZ_to_prophotorgb(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t rgb)
530{
531 // transpose and pad the conversion matrix to enable vectorization
532 static const dt_colormatrix_t xyz_to_rgb_transpose = {
533 { 1.3459433f, -0.5445989f, 0.0000000f, 0.0f },
534 { -0.2556075f, 1.5081673f, 0.0000000f, 0.0f },
535 { -0.0511118f, 0.0205351f, 1.2118128f, 0.0f }
536 };
538}
539
540#ifdef _OPENMP
541#pragma omp declare simd aligned(rgb, XYZ)
542#endif
543static inline __attribute__((always_inline)) void dt_prophotorgb_to_XYZ(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t XYZ)
544{
545 // transpose and pad the conversion matrix to enable vectorization
546 static const dt_colormatrix_t rgb_to_xyz_transpose = {
547 // prophoto rgb
548 { 0.7976749f, 0.2880402f, 0.0000000f, 0.0f },
549 { 0.1351917f, 0.7118741f, 0.0000000f, 0.0f },
550 { 0.0313534f, 0.0000857f, 0.8252100f, 0.0f }
551 };
553}
554
555#ifdef __SSE2__
557// see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices
558static inline __m128 dt_XYZ_to_RGB_sse2(__m128 XYZ)
559{
560 // XYZ -> sRGB matrix, D65
561 const __m128 xyz_to_srgb_0 = _mm_setr_ps(3.1338561f, -0.9787684f, 0.0719453f, 0.0f);
562 const __m128 xyz_to_srgb_1 = _mm_setr_ps(-1.6168667f, 1.9161415f, -0.2289914f, 0.0f);
563 const __m128 xyz_to_srgb_2 = _mm_setr_ps(-0.4906146f, 0.0334540f, 1.4052427f, 0.0f);
564
565 __m128 rgb
566 = _mm_add_ps(_mm_mul_ps(xyz_to_srgb_0, _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(0, 0, 0, 0))),
567 _mm_add_ps(_mm_mul_ps(xyz_to_srgb_1, _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(1, 1, 1, 1))),
568 _mm_mul_ps(xyz_to_srgb_2, _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(2, 2, 2, 2)))));
569
570 return rgb;
571}
572
573static inline __m128 dt_RGB_to_XYZ_sse2(__m128 rgb)
574{
575 // sRGB -> XYZ matrix, D65
576 const __m128 srgb_to_xyz_0 = _mm_setr_ps(0.4360747f, 0.2225045f, 0.0139322f, 0.0f);
577 const __m128 srgb_to_xyz_1 = _mm_setr_ps(0.3850649f, 0.7168786f, 0.0971045f, 0.0f);
578 const __m128 srgb_to_xyz_2 = _mm_setr_ps(0.1430804f, 0.0606169f, 0.7141733f, 0.0f);
579
580 __m128 XYZ
581 = ((srgb_to_xyz_0 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(0, 0, 0, 0)))
582 + (srgb_to_xyz_1 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(1, 1, 1, 1)))
583 + (srgb_to_xyz_2 * _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(2, 2, 2, 2))));
584 return XYZ;
585}
586#endif
587
588#if 0
589static const dt_colormatrix_t linear_sRGB_to_xyz_matrix =
590 { { 0.4360747f, 0.3850649f, 0.1430804f },
591 { 0.2225045f, 0.7168786f, 0.0606169f },
592 { 0.0139322f, 0.0971045f, 0.7141733f } };
593#endif
594
596 { { 0.4360747f, 0.2225045f, 0.0139322f },
597 { 0.3850649f, 0.7168786f, 0.0971045f },
598 { 0.1430804f, 0.0606169f, 0.7141733f } };
599
600static inline __attribute__((always_inline)) void dt_linearRGB_to_XYZ(const dt_aligned_pixel_t linearRGB, dt_aligned_pixel_t XYZ)
601{
603}
604
605#if 0
606static const dt_colormatrix_t xyz_to_srgb_matrix =
607 { { 3.1338561f, -1.6168667f, -0.4906146f },
608 { -0.9787684f, 1.9161415f, 0.0334540f },
609 { 0.0719453f, -0.2289914f, 1.4052427f } };
610#endif
611
613 { { 3.1338561f, -0.9787684f, 0.0719453f },
614 { -1.6168667f, 1.9161415f, -0.2289914f },
615 { -0.4906146f, 0.0334540f, 1.4052427f } };
616
617static inline __attribute__((always_inline)) void dt_XYZ_to_linearRGB(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t linearRGB)
618{
620}
621
622
623#ifdef _OPENMP
624#pragma omp declare simd aligned(Lab, rgb)
625#endif
626static inline __attribute__((always_inline)) void dt_Lab_to_prophotorgb(const dt_aligned_pixel_t Lab, dt_aligned_pixel_t rgb)
627{
628 dt_aligned_pixel_t XYZ = { 0.0f };
631}
632
633#ifdef _OPENMP
634#pragma omp declare simd aligned(rgb, Lab)
635#endif
636static inline __attribute__((always_inline)) void dt_prophotorgb_to_Lab(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t Lab)
637{
638 dt_aligned_pixel_t XYZ = { 0.0f };
641}
642
643
644#ifdef _OPENMP
645#pragma omp declare simd
646#endif
647static inline float _dt_RGB_2_Hue(const dt_aligned_pixel_t RGB, const float max, const float delta)
648{
649 float hue;
650 if(RGB[0] == max)
651 hue = (RGB[1] - RGB[2]) / delta;
652 else if(RGB[1] == max)
653 hue = 2.0f + (RGB[2] - RGB[0]) / delta;
654 else
655 hue = 4.0f + (RGB[0] - RGB[1]) / delta;
656
657 hue /= 6.0f;
658 if(hue < 0.0f) hue += 1.0f;
659 if(hue > 1.0f) hue -= 1.0f;
660 return hue;
661}
662
663#ifdef _OPENMP
664#pragma omp declare simd aligned(RGB: 16)
665#endif
666static inline __attribute__((always_inline)) void _dt_Hue_2_RGB(dt_aligned_pixel_t RGB, const float H, const float C, const float min)
667{
668 const float h = H * 6.0f;
669 const float i = floorf(h);
670 const float f = h - i;
671 const float fc = f * C;
672 const float top = C + min;
673 const float inc = fc + min;
674 const float dec = top - fc;
675 const size_t i_idx = (size_t)i;
676 if(i_idx == 0)
677 {
678 RGB[0] = top;
679 RGB[1] = inc;
680 RGB[2] = min;
681 }
682 else if(i_idx == 1)
683 {
684 RGB[0] = dec;
685 RGB[1] = top;
686 RGB[2] = min;
687 }
688 else if(i_idx == 2)
689 {
690 RGB[0] = min;
691 RGB[1] = top;
692 RGB[2] = inc;
693 }
694 else if(i_idx == 3)
695 {
696 RGB[0] = min;
697 RGB[1] = dec;
698 RGB[2] = top;
699 }
700 else if(i_idx == 4)
701 {
702 RGB[0] = inc;
703 RGB[1] = min;
704 RGB[2] = top;
705 }
706 else
707 {
708 RGB[0] = top;
709 RGB[1] = min;
710 RGB[2] = dec;
711 }
712}
713
714
715#ifdef _OPENMP
716#pragma omp declare simd aligned(RGB, HSL: 16)
717#endif
718static inline __attribute__((always_inline)) void dt_RGB_2_HSL(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t HSL)
719{
720 const float min = fminf(RGB[0], fminf(RGB[1], RGB[2]));
721 const float max = fmaxf(RGB[0], fmaxf(RGB[1], RGB[2]));
722 const float delta = max - min;
723
724 const float L = (max + min) / 2.0f;
725 float H, S;
726
727 if(fabsf(max) > 1e-6f && fabsf(delta) > 1e-6f)
728 {
729 if(L < 0.5f)
730 S = delta / (max + min);
731 else
732 S = delta / (2.0f - max - min);
734 }
735 else
736 {
737 H = 0.0f;
738 S = 0.0f;
739 }
740
741 HSL[0] = H;
742 HSL[1] = S;
743 HSL[2] = L;
744}
745
746#ifdef _OPENMP
747#pragma omp declare simd aligned(HSL, RGB: 16)
748#endif
749static inline __attribute__((always_inline)) void dt_HSL_2_RGB(const dt_aligned_pixel_t HSL, dt_aligned_pixel_t RGB)
750{
751 // almost straight from https://en.wikipedia.org/wiki/HSL_and_HSV
752 const float L = HSL[2];
753 float C;
754 if(L < 0.5f)
755 C = L * HSL[1];
756 else
757 C = (1.0f - L) * HSL[1];
758 const float m = L - C;
759 _dt_Hue_2_RGB(RGB, HSL[0], 2.0f * C, m);
760}
761
762
763#ifdef _OPENMP
764#pragma omp declare simd aligned(RGB, HSV: 16)
765#endif
766static inline __attribute__((always_inline)) void dt_RGB_2_HSV(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t HSV)
767{
768 const float min = fminf(RGB[0], fminf(RGB[1], RGB[2]));
769 const float max = fmaxf(RGB[0], fmaxf(RGB[1], RGB[2]));
770 const float delta = max - min;
771
772 const float V = max;
773 float S, H;
774
775 if(fabsf(max) > 1e-6f && fabsf(delta) > 1e-6f)
776 {
777 S = delta / max;
779 }
780 else
781 {
782 S = 0.0f;
783 H = 0.0f;
784 }
785
786 HSV[0] = H;
787 HSV[1] = S;
788 HSV[2] = V;
789}
790
791#ifdef _OPENMP
792#pragma omp declare simd aligned(HSV, RGB: 16)
793#endif
794static inline __attribute__((always_inline)) void dt_HSV_2_RGB(const dt_aligned_pixel_t HSV, dt_aligned_pixel_t RGB)
795{
796 // almost straight from https://en.wikipedia.org/wiki/HSL_and_HSV
797 const float C = HSV[1] * HSV[2];
798 const float m = HSV[2] - C;
800}
801
802
803#ifdef _OPENMP
804#pragma omp declare simd aligned(RGB, HCV: 16)
805#endif
806static inline __attribute__((always_inline)) void dt_RGB_2_HCV(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t HCV)
807{
808 const float min = fminf(RGB[0], fminf(RGB[1], RGB[2]));
809 const float max = fmaxf(RGB[0], fmaxf(RGB[1], RGB[2]));
810 const float delta = max - min;
811
812 const float V = max;
813 float C, H;
814
815 if(fabsf(max) > 1e-6f && fabsf(delta) > 1e-6f)
816 {
817 C = delta;
819 }
820 else
821 {
822 C = 0.0f;
823 H = 0.0f;
824 }
825
826 HCV[0] = H;
827 HCV[1] = C;
828 HCV[2] = V;
829}
830
831#ifdef _OPENMP
832#pragma omp declare simd
833#endif
834static inline __attribute__((always_inline)) void dt_Lab_2_LCH(const dt_aligned_pixel_t Lab, dt_aligned_pixel_t LCH)
835{
836 float var_H = atan2f(Lab[2], Lab[1]);
837
838 if(var_H > 0.0f)
839 var_H = var_H / (2.0f * DT_M_PI_F);
840 else
841 var_H = 1.0f - fabsf(var_H) / (2.0f * DT_M_PI_F);
842
843 LCH[0] = Lab[0];
844 LCH[1] = hypotf(Lab[1], Lab[2]);
845 LCH[2] = var_H;
846}
847
848
849#ifdef _OPENMP
850#pragma omp declare simd
851#endif
852static inline __attribute__((always_inline)) void dt_LCH_2_Lab(const dt_aligned_pixel_t LCH, dt_aligned_pixel_t Lab)
853{
854 Lab[0] = LCH[0];
855 Lab[1] = cosf(2.0f * DT_M_PI_F * LCH[2]) * LCH[1];
856 Lab[2] = sinf(2.0f * DT_M_PI_F * LCH[2]) * LCH[1];
857}
858
859static inline float dt_camera_rgb_luminance(const dt_aligned_pixel_t rgb)
860{
861 return (rgb[0] * 0.2225045f + rgb[1] * 0.7168786f + rgb[2] * 0.0606169f);
862}
863
864
865#ifdef _OPENMP
866#pragma omp declare simd aligned(XYZ_D50, XYZ_D65: 16)
867#endif
868static inline __attribute__((always_inline)) void dt_XYZ_D50_2_XYZ_D65(const dt_aligned_pixel_t XYZ_D50, dt_aligned_pixel_t XYZ_D65)
869{
870 // Bradford adaptation matrix from http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
871#if 0
872 static const dt_colormatrix_t M = {
873 { 0.9555766f, -0.0230393f, 0.0631636f, 0.0f },
874 { -0.0282895f, 1.0099416f, 0.0210077f, 0.0f },
875 { 0.0122982f, -0.0204830f, 1.3299098f, 0.0f },
876 };
877#endif
878 static const dt_colormatrix_t M_transposed = {
879 { 0.9555766f, -0.0282895f, 0.0122982f, 0.0f },
880 { -0.0230393f, 1.0099416f, -0.0204830f, 0.0f },
881 { 0.0631636f, 0.0210077f, 1.3299098f, 0.0f },
882 };
883
885 XYZ_D65[x] = M_transposed[0][x] * XYZ_D50[0] + M_transposed[1][x] * XYZ_D50[1] + M_transposed[2][x] * XYZ_D50[2];
886}
887
888#ifdef _OPENMP
889#pragma omp declare simd aligned(XYZ_D50, XYZ_D65: 16)
890#endif
891static inline __attribute__((always_inline)) void dt_XYZ_D65_2_XYZ_D50(const dt_aligned_pixel_t XYZ_D65, dt_aligned_pixel_t XYZ_D50)
892{
893 // Bradford adaptation matrix from http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
894#if 0
895 static const dt_colormatrix_t M = {
896 { 1.0478112f, 0.0228866f, -0.0501270f, 0.0f },
897 { 0.0295424f, 0.9904844f, -0.0170491f, 0.0f },
898 { -0.0092345f, 0.0150436f, 0.7521316f, 0.0f },
899 };
900#endif
901 static const dt_colormatrix_t M_transposed = {
902 { 1.0478112f, 0.0295424f, -0.0092345f, 0.0f },
903 { 0.0228866f, 0.9904844f, 0.0150436f, 0.0f },
904 { -0.0501270f, -0.0170491f, 0.7521316f, 0.0f },
905 };
906
908 XYZ_D50[x] = M_transposed[0][x] * XYZ_D65[0] + M_transposed[1][x] * XYZ_D65[1] + M_transposed[2][x] * XYZ_D65[2];
909}
910
911
919#ifdef _OPENMP
920#pragma omp declare simd aligned(XYZ_D65, JzAzBz: 16)
921#endif
922static inline __attribute__((always_inline)) void dt_XYZ_2_JzAzBz(const dt_aligned_pixel_t XYZ_D65, dt_aligned_pixel_t JzAzBz)
923{
924 const float b = 1.15f;
925 const float g = 0.66f;
926 const float c1 = 0.8359375f; // 3424 / 2^12
927 const float c2 = 18.8515625f; // 2413 / 2^7
928 const float c3 = 18.6875f; // 2392 / 2^7
929 const float n = 0.159301758f; // 2610 / 2^14
930 const float p = 134.034375f; // 1.7 x 2523 / 2^5
931 const float d = -0.56f;
932 const float d0 = 1.6295499532821566e-11f;
933 static const dt_colormatrix_t M = {
934 { 0.41478972f, 0.579999f, 0.0146480f, 0.0f },
935 { -0.2015100f, 1.120649f, 0.0531008f, 0.0f },
936 { -0.0166008f, 0.264800f, 0.6684799f, 0.0f },
937 };
938#if 0
939 static const dt_colormatrix_t A = {
940 { 0.5f, 0.5f, 0.0f, 0.0f },
941 { 3.524000f, -4.066708f, 0.542708f, 0.0f },
942 { 0.199076f, 1.096799f, -1.295875f, 0.0f },
943 };
944#endif
946 { 0.5f, 3.524000f, 0.199076f, 0.0f },
947 { 0.5f, -4.066708f, 1.096799f, 0.0f },
948 { 0.0f, 0.542708f, -1.295875f, 0.0f },
949 };
950
951 dt_aligned_pixel_t XYZ = { 0.0f, 0.0f, 0.0f, 0.0f };
952 dt_aligned_pixel_t LMS = { 0.0f, 0.0f, 0.0f, 0.0f };
953
954 // XYZ -> X'Y'Z
955 XYZ[0] = b * XYZ_D65[0] - (b - 1.0f) * XYZ_D65[2];
956 XYZ[1] = g * XYZ_D65[1] - (g - 1.0f) * XYZ_D65[0];
957 XYZ[2] = XYZ_D65[2];
958
959 // X'Y'Z -> L'M'S'
960#ifdef _OPENMP
961#pragma omp simd aligned(LMS, XYZ:16) aligned(M:64)
962#endif
963 for(int i = 0; i < 3; i++)
964 {
965 LMS[i] = M[i][0] * XYZ[0] + M[i][1] * XYZ[1] + M[i][2] * XYZ[2];
966 LMS[i] = powf(fmaxf(LMS[i] / 10000.f, 0.0f), n);
967 LMS[i] = powf((c1 + c2 * LMS[i]) / (1.0f + c3 * LMS[i]), p);
968 }
969
970 // L'M'S' -> Izazbz
972 JzAzBz[c] = A_transposed[0][c] * LMS[0] + A_transposed[1][c] * LMS[1] + A_transposed[2][c] * LMS[2];
973 // Iz -> Jz
974 JzAzBz[0] = fmaxf(((1.0f + d) * JzAzBz[0]) / (1.0f + d * JzAzBz[0]) - d0, 0.f);
975}
976
977static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
978dt_XYZ_2_JzAzBz_simd(const dt_aligned_pixel_simd_t XYZ_D65)
979{
980 const float b = 1.15f;
981 const float g = 0.66f;
982 const float c1 = 0.8359375f; // 3424 / 2^12
983 const float c2 = 18.8515625f; // 2413 / 2^7
984 const float c3 = 18.6875f; // 2392 / 2^7
985 const float n = 0.159301758f; // 2610 / 2^14
986 const float p = 134.034375f; // 1.7 x 2523 / 2^5
987 const float d = -0.56f;
988 const float d0 = 1.6295499532821566e-11f;
989 static const dt_colormatrix_t M_transposed = {
990 { 0.41478972f, -0.2015100f, -0.0166008f, 0.0f },
991 { 0.5799990f, 1.1206490f, 0.2648000f, 0.0f },
992 { 0.0146480f, 0.0531008f, 0.6684799f, 0.0f },
993 };
994 static const dt_colormatrix_t A_transposed = {
995 { 0.5f, 3.524000f, 0.199076f, 0.0f },
996 { 0.5f, -4.066708f, 1.096799f, 0.0f },
997 { 0.0f, 0.542708f, -1.295875f, 0.0f },
998 };
999
1000 const dt_aligned_pixel_simd_t XYZ = {
1001 b * XYZ_D65[0] - (b - 1.0f) * XYZ_D65[2],
1002 g * XYZ_D65[1] - (g - 1.0f) * XYZ_D65[0],
1003 XYZ_D65[2],
1004 0.f
1005 };
1006
1007 dt_aligned_pixel_simd_t LMS = dt_mat3x4_mul_vec4(XYZ,
1008 dt_colormatrix_row_to_simd(M_transposed, 0),
1009 dt_colormatrix_row_to_simd(M_transposed, 1),
1010 dt_colormatrix_row_to_simd(M_transposed, 2));
1011 for(int i = 0; i < 3; i++)
1012 {
1013 LMS[i] = powf(fmaxf(LMS[i] / 10000.f, 0.0f), n);
1014 LMS[i] = powf((c1 + c2 * LMS[i]) / (1.0f + c3 * LMS[i]), p);
1015 }
1016
1017 dt_aligned_pixel_simd_t JzAzBz = dt_mat3x4_mul_vec4(LMS,
1018 dt_colormatrix_row_to_simd(A_transposed, 0),
1019 dt_colormatrix_row_to_simd(A_transposed, 1),
1020 dt_colormatrix_row_to_simd(A_transposed, 2));
1021 JzAzBz[0] = fmaxf(((1.0f + d) * JzAzBz[0]) / (1.0f + d * JzAzBz[0]) - d0, 0.f);
1022 JzAzBz[3] = 0.f;
1023 return JzAzBz;
1024}
1025
1026#ifdef _OPENMP
1027#pragma omp declare simd aligned(JzAzBz, JzCzhz: 16)
1028#endif
1029static inline __attribute__((always_inline)) void dt_JzAzBz_2_JzCzhz(const dt_aligned_pixel_t JzAzBz, dt_aligned_pixel_t JzCzhz)
1030{
1031 float var_H = atan2f(JzAzBz[2], JzAzBz[1]) / (2.0f * DT_M_PI_F);
1032 JzCzhz[0] = JzAzBz[0];
1033 JzCzhz[1] = hypotf(JzAzBz[1], JzAzBz[2]);
1034 JzCzhz[2] = var_H >= 0.0f ? var_H : 1.0f + var_H;
1035}
1036
1037#ifdef _OPENMP
1038#pragma omp declare simd aligned(JzCzhz, JzAzBz: 16)
1039#endif
1040static inline __attribute__((always_inline)) void dt_JzCzhz_2_JzAzBz(const dt_aligned_pixel_t JzCzhz, dt_aligned_pixel_t JzAzBz)
1041{
1042 JzAzBz[0] = JzCzhz[0];
1043 JzAzBz[1] = cosf(2.0f * DT_M_PI_F * JzCzhz[2]) * JzCzhz[1];
1044 JzAzBz[2] = sinf(2.0f * DT_M_PI_F * JzCzhz[2]) * JzCzhz[1];
1045}
1046
1047#ifdef _OPENMP
1048#pragma omp declare simd aligned(JzAzBz, XYZ_D65: 16)
1049#endif
1050static inline __attribute__((always_inline)) void dt_JzAzBz_2_XYZ(const dt_aligned_pixel_t JzAzBz, dt_aligned_pixel_t XYZ_D65)
1051{
1052 const float b = 1.15f;
1053 const float g = 0.66f;
1054 const float c1 = 0.8359375f; // 3424 / 2^12
1055 const float c2 = 18.8515625f; // 2413 / 2^7
1056 const float c3 = 18.6875f; // 2392 / 2^7
1057 const float n_inv = 1.0f / 0.159301758f; // 2610 / 2^14
1058 const float p_inv = 1.0f / 134.034375f; // 1.7 x 2523 / 2^5
1059 const float d = -0.56f;
1060 const float d0 = 1.6295499532821566e-11f;
1062 { 1.9242264357876067f, -1.0047923125953657f, 0.0376514040306180f, 0.0f },
1063 { 0.3503167620949991f, 0.7264811939316552f, -0.0653844229480850f, 0.0f },
1064 { -0.0909828109828475f, -0.3127282905230739f, 1.5227665613052603f, 0.0f },
1065 };
1067 { 1.0f, 0.1386050432715393f, 0.0580473161561189f, 0.0f },
1068 { 1.0f, -0.1386050432715393f, -0.0580473161561189f, 0.0f },
1069 { 1.0f, -0.0960192420263190f, -0.8118918960560390f, 0.0f },
1070 };
1071
1072 dt_aligned_pixel_t XYZ = { 0.0f, 0.0f, 0.0f, 0.0f };
1073 dt_aligned_pixel_t LMS = { 0.0f, 0.0f, 0.0f, 0.0f };
1074 dt_aligned_pixel_t IzAzBz = { 0.0f, 0.0f, 0.0f, 0.0f };
1075
1076 IzAzBz[0] = JzAzBz[0] + d0;
1077 IzAzBz[0] = fmaxf(IzAzBz[0] / (1.0f + d - d * IzAzBz[0]), 0.f);
1078 IzAzBz[1] = JzAzBz[1];
1079 IzAzBz[2] = JzAzBz[2];
1080
1081 // IzAzBz -> LMS
1082#ifdef _OPENMP
1083#pragma omp simd aligned(LMS, IzAzBz:16) aligned(AI:64)
1084#endif
1085 for(int i = 0; i < 3; i++)
1086 {
1087 LMS[i] = AI[i][0] * IzAzBz[0] + AI[i][1] * IzAzBz[1] + AI[i][2] * IzAzBz[2];
1088 LMS[i] = powf(fmaxf(LMS[i], 0.0f), p_inv);
1089 LMS[i] = 10000.f * powf(fmaxf((c1 - LMS[i]) / (c3 * LMS[i] - c2), 0.0f), n_inv);
1090 }
1091
1092 // LMS -> X'Y'Z
1093#ifdef _OPENMP
1094#pragma omp simd aligned(LMS, XYZ:16) aligned(MI:64)
1095#endif
1096 for(int i = 0; i < 3; i++) XYZ[i] = MI[i][0] * LMS[0] + MI[i][1] * LMS[1] + MI[i][2] * LMS[2];
1097
1098 // X'Y'Z -> XYZ_D65
1099 XYZ_D65[0] = (XYZ[0] + (b - 1.0f) * XYZ[2]) / b;
1100 XYZ_D65[1] = (XYZ[1] + (g - 1.0f) * XYZ_D65[0]) / g;
1101 XYZ_D65[2] = XYZ[2];
1102}
1103
1105 { 1.9242264357876067f, 0.3503167620949991f, -0.0909828109828475f, 0.0f },
1106 { -1.0047923125953657f, 0.7264811939316552f, -0.3127282905230739f, 0.0f },
1107 { 0.0376514040306180f, -0.0653844229480850f, 1.5227665613052603f, 0.0f },
1108};
1110 { 1.0f, 1.0f, 1.0f, 0.0f },
1111 { 0.1386050432715393f, -0.1386050432715393f, -0.0960192420263190f, 0.0f },
1112 { 0.0580473161561189f, -0.0580473161561189f, -0.8118918960560390f, 0.0f },
1113};
1114
1115static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1116dt_JzAzBz_2_XYZ_simd(const dt_aligned_pixel_simd_t JzAzBz)
1117{
1118 const float b = 1.15f;
1119 const float g = 0.66f;
1120 const float c1 = 0.8359375f; // 3424 / 2^12
1121 const float c2 = 18.8515625f; // 2413 / 2^7
1122 const float c3 = 18.6875f; // 2392 / 2^7
1123 const float n_inv = 1.0f / 0.159301758f; // 2610 / 2^14
1124 const float p_inv = 1.0f / 134.034375f; // 1.7 x 2523 / 2^5
1125 const float d = -0.56f;
1126 const float d0 = 1.6295499532821566e-11f;
1127
1128 dt_aligned_pixel_simd_t IzAzBz = JzAzBz;
1129 IzAzBz[0] = JzAzBz[0] + d0;
1130 IzAzBz[0] = fmaxf(IzAzBz[0] / (1.0f + d - d * IzAzBz[0]), 0.f);
1131 IzAzBz[3] = 0.f;
1132
1133 dt_aligned_pixel_simd_t LMS = dt_mat3x4_mul_vec4(IzAzBz,
1134 dt_colormatrix_row_to_simd(AI_transposed, 0),
1135 dt_colormatrix_row_to_simd(AI_transposed, 1),
1136 dt_colormatrix_row_to_simd(AI_transposed, 2));
1137 for(int i = 0; i < 3; i++)
1138 {
1139 LMS[i] = powf(fmaxf(LMS[i], 0.0f), p_inv);
1140 LMS[i] = 10000.f * powf(fmaxf((c1 - LMS[i]) / (c3 * LMS[i] - c2), 0.0f), n_inv);
1141 }
1142
1143 const dt_aligned_pixel_simd_t XYZ = dt_mat3x4_mul_vec4(LMS,
1144 dt_colormatrix_row_to_simd(MI_transposed, 0),
1145 dt_colormatrix_row_to_simd(MI_transposed, 1),
1146 dt_colormatrix_row_to_simd(MI_transposed, 2));
1147
1148 return (dt_aligned_pixel_simd_t){
1149 (XYZ[0] + (b - 1.0f) * XYZ[2]) / b,
1150 (XYZ[1] + (g - 1.0f) * ((XYZ[0] + (b - 1.0f) * XYZ[2]) / b)) / g,
1151 XYZ[2],
1152 0.f
1153 };
1154}
1155
1156// Convert CIE 1931 2° XYZ D65 to CIE 2006 LMS D65 (cone space)
1157/*
1158* The CIE 1931 XYZ 2° observer D65 is converted to CIE 2006 LMS D65 using the approximation by
1159* Richard A. Kirk, Chromaticity coordinates for graphic arts based on CIE 2006 LMS
1160* with even spacing of Munsell colours
1161* https://doi.org/10.2352/issn.2169-2629.2019.27.38
1162*/
1163
1165 = { { 0.257085f, 0.859943f, -0.031061f, 0.f },
1166 { -0.394427f, 1.175800f, 0.106423f, 0.f },
1167 { 0.064856f, -0.076250f, 0.559067f, 0.f } };
1168
1170 = { { 1.80794659f, -1.29971660f, 0.34785879f, 0.f },
1171 { 0.61783960f, 0.39595453f, -0.04104687f, 0.f },
1172 { -0.12546960f, 0.20478038f, 1.74274183f, 0.f } };
1173
1174
1175#ifdef _OPENMP
1176#pragma omp declare simd aligned(LMS, XYZ: 16)
1177#endif
1178static inline __attribute__((always_inline)) void XYZ_to_LMS(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t LMS)
1179{
1181}
1182
1183static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1184XYZ_to_LMS_simd(const dt_aligned_pixel_simd_t XYZ)
1185{
1186 static const dt_colormatrix_t XYZ_D65_to_LMS_2006_D65_transposed = {
1187 { 0.257085f, -0.394427f, 0.064856f, 0.f },
1188 { 0.859943f, 1.175800f, -0.076250f, 0.f },
1189 { -0.031061f, 0.106423f, 0.559067f, 0.f },
1190 };
1191 return dt_mat3x4_mul_vec4(XYZ,
1192 dt_colormatrix_row_to_simd(XYZ_D65_to_LMS_2006_D65_transposed, 0),
1193 dt_colormatrix_row_to_simd(XYZ_D65_to_LMS_2006_D65_transposed, 1),
1194 dt_colormatrix_row_to_simd(XYZ_D65_to_LMS_2006_D65_transposed, 2));
1195}
1196
1197#ifdef _OPENMP
1198#pragma omp declare simd aligned(XYZ, LMS: 16)
1199#endif
1200static inline __attribute__((always_inline)) void LMS_to_XYZ(const dt_aligned_pixel_t LMS, dt_aligned_pixel_t XYZ)
1201{
1203}
1204
1205static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1206LMS_to_XYZ_simd(const dt_aligned_pixel_simd_t LMS)
1207{
1208 static const dt_colormatrix_t LMS_2006_D65_to_XYZ_D65_transposed = {
1209 { 1.80794659f, 0.61783960f, -0.12546960f, 0.f },
1210 { -1.29971660f, 0.39595453f, 0.20478038f, 0.f },
1211 { 0.34785879f, -0.04104687f, 1.74274183f, 0.f },
1212 };
1213 return dt_mat3x4_mul_vec4(LMS,
1214 dt_colormatrix_row_to_simd(LMS_2006_D65_to_XYZ_D65_transposed, 0),
1215 dt_colormatrix_row_to_simd(LMS_2006_D65_to_XYZ_D65_transposed, 1),
1216 dt_colormatrix_row_to_simd(LMS_2006_D65_to_XYZ_D65_transposed, 2));
1217}
1218
1219/*
1220* Convert from CIE 2006 LMS D65 to Filmlight RGB defined in
1221* Richard A. Kirk, Chromaticity coordinates for graphic arts based on CIE 2006 LMS
1222* with even spacing of Munsell colours
1223* https://doi.org/10.2352/issn.2169-2629.2019.27.38
1224*/
1225
1227 = { { 0.95f, 0.38f, 0.00f, 0.f },
1228 { 0.05f, 0.62f, 0.03f, 0.f },
1229 { 0.00f, 0.00f, 0.97f, 0.f } };
1230
1232 = { { 1.0877193f, -0.66666667f, 0.02061856f, 0.f },
1233 { -0.0877193f, 1.66666667f, -0.05154639f, 0.f },
1234 { 0.f, 0.f, 1.03092784f, 0.f } };
1235
1236#ifdef _OPENMP
1237#pragma omp declare simd aligned(LMS, RGB: 16)
1238#endif
1239static inline __attribute__((always_inline)) void gradingRGB_to_LMS(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t LMS)
1240{
1242}
1243
1244static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1245gradingRGB_to_LMS_simd(const dt_aligned_pixel_simd_t RGB)
1246{
1247 static const dt_colormatrix_t filmlightRGB_D65_to_LMS_D65_transposed
1248 = { { 0.95f, 0.05f, 0.00f, 0.f },
1249 { 0.38f, 0.62f, 0.00f, 0.f },
1250 { 0.00f, 0.03f, 0.97f, 0.f } };
1251 return dt_mat3x4_mul_vec4(RGB,
1252 dt_colormatrix_row_to_simd(filmlightRGB_D65_to_LMS_D65_transposed, 0),
1253 dt_colormatrix_row_to_simd(filmlightRGB_D65_to_LMS_D65_transposed, 1),
1254 dt_colormatrix_row_to_simd(filmlightRGB_D65_to_LMS_D65_transposed, 2));
1255}
1256
1257#ifdef _OPENMP
1258#pragma omp declare simd aligned(LMS, RGB: 16)
1259#endif
1260static inline __attribute__((always_inline)) void LMS_to_gradingRGB(const dt_aligned_pixel_t LMS, dt_aligned_pixel_t RGB)
1261{
1263}
1264
1265static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1266LMS_to_gradingRGB_simd(const dt_aligned_pixel_simd_t LMS)
1267{
1268 static const dt_colormatrix_t LMS_D65_to_filmlightRGB_D65_transposed
1269 = { { 1.0877193f, -0.0877193f, 0.f, 0.f },
1270 { -0.66666667f, 1.66666667f, 0.f, 0.f },
1271 { 0.02061856f, -0.05154639f, 1.03092784f, 0.f } };
1272 return dt_mat3x4_mul_vec4(LMS,
1273 dt_colormatrix_row_to_simd(LMS_D65_to_filmlightRGB_D65_transposed, 0),
1274 dt_colormatrix_row_to_simd(LMS_D65_to_filmlightRGB_D65_transposed, 1),
1275 dt_colormatrix_row_to_simd(LMS_D65_to_filmlightRGB_D65_transposed, 2));
1276}
1277
1278
1279/*
1280* Re-express the Filmlight RGB triplet as Yrg luminance/chromacity coordinates
1281*/
1282
1283#ifdef _OPENMP
1284#pragma omp declare simd aligned(LMS, Yrg: 16)
1285#endif
1286static inline __attribute__((always_inline)) void LMS_to_Yrg(const dt_aligned_pixel_t LMS, dt_aligned_pixel_t Yrg)
1287{
1288 // compute luminance
1289 const float Y = 0.68990272f * LMS[0] + 0.34832189f * LMS[1];
1290
1291 // normalize LMS
1292 const float a = LMS[0] + LMS[1] + LMS[2];
1293 dt_aligned_pixel_t lms = { 0.f };
1294 for_four_channels(c, aligned(LMS, lms : 16)) lms[c] = (a == 0.f) ? 0.f : LMS[c] / a;
1295
1296 // convert to Filmlight rgb (normalized)
1297 dt_aligned_pixel_t rgb = { 0.f };
1299
1300 Yrg[0] = Y;
1301 Yrg[1] = rgb[0];
1302 Yrg[2] = rgb[1];
1303}
1304
1305static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1306LMS_to_Yrg_simd(const dt_aligned_pixel_simd_t LMS)
1307{
1308 const float Y = 0.68990272f * LMS[0] + 0.34832189f * LMS[1];
1309 const float a = LMS[0] + LMS[1] + LMS[2];
1310 const float inv_a = (a == 0.f) ? 0.f : 1.f / a;
1311 const dt_aligned_pixel_simd_t lms = { LMS[0] * inv_a, LMS[1] * inv_a, LMS[2] * inv_a, 0.f };
1312 const dt_aligned_pixel_simd_t rgb = LMS_to_gradingRGB_simd(lms);
1313 return (dt_aligned_pixel_simd_t){ Y, rgb[0], rgb[1], 0.f };
1314}
1315
1316#ifdef _OPENMP
1317#pragma omp declare simd aligned(Yrg, LMS: 16)
1318#endif
1319static inline __attribute__((always_inline)) void Yrg_to_LMS(const dt_aligned_pixel_t Yrg, dt_aligned_pixel_t LMS)
1320{
1321 const float Y = Yrg[0];
1322
1323 // reform rgb (normalized) from chroma
1324 const float r = Yrg[1];
1325 const float g = Yrg[2];
1326 const float b = 1.f - r - g;
1327 const dt_aligned_pixel_t rgb = { r, g, b, 0.f };
1328
1329 // convert to lms (normalized)
1330 dt_aligned_pixel_t lms = { 0.f };
1332
1333 // denormalize to LMS
1334 const float denom = (0.68990272f * lms[0] + 0.34832189f * lms[1]);
1335 const float a = (denom == 0.f) ? 0.f : Y / denom;
1336 for_four_channels(c, aligned(lms, LMS:16)) LMS[c] = lms[c] * a;
1337}
1338
1339static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1340Yrg_to_LMS_simd(const dt_aligned_pixel_simd_t Yrg)
1341{
1342 const dt_aligned_pixel_simd_t rgb = { Yrg[1], Yrg[2], 1.f - Yrg[1] - Yrg[2], 0.f };
1343 const dt_aligned_pixel_simd_t lms = gradingRGB_to_LMS_simd(rgb);
1344 const float denom = 0.68990272f * lms[0] + 0.34832189f * lms[1];
1345 const float a = (denom == 0.f) ? 0.f : Yrg[0] / denom;
1346 return (dt_aligned_pixel_simd_t){ lms[0] * a, lms[1] * a, lms[2] * a, 0.f };
1347}
1348
1349/*
1350* Re-express Filmlight Yrg in polar coordinates Ych
1351*/
1352
1353#ifdef _OPENMP
1354#pragma omp declare simd aligned(Ych, Yrg: 16)
1355#endif
1356static inline __attribute__((always_inline)) void Yrg_to_Ych(const dt_aligned_pixel_t Yrg, dt_aligned_pixel_t Ych)
1357{
1358 const float Y = Yrg[0];
1359 // Subtract white point. These are the r, g coordinates of
1360 // sRGB (D50 adapted) (1, 1, 1) taken through
1361 // XYZ D50 -> CAT16 D50->D65 adaptation -> LMS 2006
1362 // -> grading RGB conversion.
1363 const float r = Yrg[1] - 0.21902143f;
1364 const float g = Yrg[2] - 0.54371398f;
1365 const float c = hypotf(g, r);
1366 const float h = atan2f(g, r);
1367 Ych[0] = Y;
1368 Ych[1] = c;
1369 Ych[2] = h;
1370}
1371
1372static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1373Yrg_to_Ych_simd(const dt_aligned_pixel_simd_t Yrg)
1374{
1375 const float r = Yrg[1] - 0.21902143f;
1376 const float g = Yrg[2] - 0.54371398f;
1377 return (dt_aligned_pixel_simd_t){ Yrg[0], hypotf(g, r), atan2f(g, r), 0.f };
1378}
1379
1380#ifdef _OPENMP
1381#pragma omp declare simd aligned(Ych, Yrg: 16)
1382#endif
1383static inline __attribute__((always_inline)) void Ych_to_Yrg(const dt_aligned_pixel_t Ych, dt_aligned_pixel_t Yrg)
1384{
1385 const float Y = Ych[0];
1386 const float c = Ych[1];
1387 const float h = Ych[2];
1388 const float r = c * cosf(h) + 0.21902143f;
1389 const float g = c * sinf(h) + 0.54371398f;
1390 Yrg[0] = Y;
1391 Yrg[1] = r;
1392 Yrg[2] = g;
1393}
1394
1395static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1396Ych_to_Yrg_simd(const dt_aligned_pixel_simd_t Ych)
1397{
1398 return (dt_aligned_pixel_simd_t){
1399 Ych[0],
1400 Ych[1] * cosf(Ych[2]) + 0.21902143f,
1401 Ych[1] * sinf(Ych[2]) + 0.54371398f,
1402 0.f
1403 };
1404}
1405
1406/*
1407* Filmlight RGB utils functions
1408*/
1409
1410#ifdef _OPENMP
1411#pragma omp declare simd aligned(Ych, RGB: 16)
1412#endif
1413static inline __attribute__((always_inline)) void Ych_to_gradingRGB(const dt_aligned_pixel_t Ych, dt_aligned_pixel_t RGB)
1414{
1415 dt_aligned_pixel_t Yrg = { 0.f };
1416 dt_aligned_pixel_t LMS = { 0.f };
1420}
1421
1422static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1423Ych_to_gradingRGB_simd(const dt_aligned_pixel_simd_t Ych)
1424{
1425 return LMS_to_gradingRGB_simd(Yrg_to_LMS_simd(Ych_to_Yrg_simd(Ych)));
1426}
1427
1428
1429#ifdef _OPENMP
1430#pragma omp declare simd aligned(Ych, RGB: 16)
1431#endif
1432static inline __attribute__((always_inline)) void gradingRGB_to_Ych(const dt_aligned_pixel_t RGB, dt_aligned_pixel_t Ych)
1433{
1434 dt_aligned_pixel_t Yrg = { 0.f };
1435 dt_aligned_pixel_t LMS = { 0.f };
1439}
1440
1441static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1442gradingRGB_to_Ych_simd(const dt_aligned_pixel_simd_t RGB)
1443{
1444 return Yrg_to_Ych_simd(LMS_to_Yrg_simd(gradingRGB_to_LMS_simd(RGB)));
1445}
1446
1447
1448#ifdef _OPENMP
1449#pragma omp declare simd aligned(Ych, XYZ: 16)
1450#endif
1451static inline __attribute__((always_inline)) void XYZ_to_Ych(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t Ych)
1452{
1453 // WARNING: XYZ needs to be chroma-adapted to D65 before
1454 dt_aligned_pixel_t Yrg = { 0.f };
1455 dt_aligned_pixel_t LMS = { 0.f };
1457 LMS_to_Yrg(LMS, Yrg);
1458 Yrg_to_Ych(Yrg, Ych);
1459}
1460
1461static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1462XYZ_to_Ych_simd(const dt_aligned_pixel_simd_t XYZ)
1463{
1464 return Yrg_to_Ych_simd(LMS_to_Yrg_simd(XYZ_to_LMS_simd(XYZ)));
1465}
1466
1467
1468#ifdef _OPENMP
1469#pragma omp declare simd aligned(Ych, XYZ: 16)
1470#endif
1471static inline __attribute__((always_inline)) void Ych_to_XYZ(const dt_aligned_pixel_t Ych, dt_aligned_pixel_t XYZ)
1472{
1473 // WARNING: XYZ is output in D65
1474 dt_aligned_pixel_t Yrg = { 0.f };
1475 dt_aligned_pixel_t LMS = { 0.f };
1476 Ych_to_Yrg(Ych, Yrg);
1477 Yrg_to_LMS(Yrg, LMS);
1479}
1480
1481static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1482Ych_to_XYZ_simd(const dt_aligned_pixel_simd_t Ych)
1483{
1484 return LMS_to_XYZ_simd(Yrg_to_LMS_simd(Ych_to_Yrg_simd(Ych)));
1485}
1486
1487
1488static inline __attribute__((always_inline)) void gamut_check_Yrg(dt_aligned_pixel_t Ych)
1489{
1490 // Check if the color fits in Yrg and LMS cone space
1491 // clip chroma at constant hue and luminance otherwise
1492
1493 // Do a test conversion to Yrg
1494 dt_aligned_pixel_t Yrg = { 0.f };
1495 Ych_to_Yrg(Ych, Yrg);
1496
1497 // Gamut-clip chroma in Yrg at constant hue and luminance
1498 // e.g. find the max chroma value that fits in gamut at the current hue
1499 // taken from colorbalancergb.c
1500 const float D65_r = 0.21902143f;
1501 const float D65_g = 0.54371398f;
1502
1503 float max_c = Ych[1];
1504 const float cos_h = cosf(Ych[2]);
1505 const float sin_h = sinf(Ych[2]);
1506
1507 if(Yrg[1] < 0.f)
1508 {
1509 max_c = fminf(-D65_r / cos_h, max_c);
1510 }
1511 if(Yrg[2] < 0.f)
1512 {
1513 max_c = fminf(-D65_g / sin_h, max_c);
1514 }
1515 if(Yrg[1] + Yrg[2] > 1.f)
1516 {
1517 max_c = fminf((1.f - D65_r - D65_g) / (cos_h + sin_h), max_c);
1518 }
1519
1520 // Overwrite chroma with the sanitized value
1521 Ych[1] = max_c;
1522}
1523
1524static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1525gamut_check_Yrg_simd(const dt_aligned_pixel_simd_t Ych)
1526{
1527 const dt_aligned_pixel_simd_t Yrg = Ych_to_Yrg_simd(Ych);
1528 const float cos_h = cosf(Ych[2]);
1529 const float sin_h = sinf(Ych[2]);
1530 float max_c = Ych[1];
1531
1532 if(Yrg[1] < 0.f) max_c = fminf(-0.21902143f / cos_h, max_c);
1533 if(Yrg[2] < 0.f) max_c = fminf(-0.54371398f / sin_h, max_c);
1534 if(Yrg[1] + Yrg[2] > 1.f) max_c = fminf((1.f - 0.21902143f - 0.54371398f) / (cos_h + sin_h), max_c);
1535
1536 return (dt_aligned_pixel_simd_t){ Ych[0], max_c, Ych[2], 0.f };
1537}
1538
1547static inline float Y_to_dt_UCS_L_star(const float Y)
1548{
1549 // WARNING: L_star needs to be < 2.098883786377, meaning Y needs to be < 3.875766378407574e+19
1550 const float Y_hat = powf(Y, 0.631651345306265f);
1551 return 2.098883786377f * Y_hat / (Y_hat + 1.12426773749357f);
1552}
1553
1554static inline float dt_UCS_L_star_to_Y(const float L_star)
1555{
1556 // WARNING: L_star needs to be < 2.098883786377, meaning Y needs to be < 3.875766378407574e+19
1557 return powf((1.12426773749357f * L_star / (2.098883786377f - L_star)), 1.5831518565279648f);
1558}
1559
1560
1561#ifdef _OPENMP
1562#pragma omp declare simd aligned(xyY: 16)
1563#endif
1564static inline __attribute__((always_inline)) void xyY_to_dt_UCS_UV(const dt_aligned_pixel_t xyY, float UV_star_prime[2])
1565{
1566
1567 const dt_aligned_pixel_t x_factors = { -0.783941002840055f, 0.745273540913283f, 0.318707282433486f, 0.f };
1568 const dt_aligned_pixel_t y_factors = { 0.277512987809202f, -0.205375866083878f, 2.16743692732158f, 0.f };
1569 const dt_aligned_pixel_t offsets = { 0.153836578598858f, -0.165478376301988f, 0.291320554395942f, 0.f };
1570
1571 dt_aligned_pixel_t UVD = { 0.f };
1572 for_each_channel(c, aligned(xyY, UVD, x_factors, y_factors, offsets))
1573 UVD[c] = x_factors[c] * xyY[0] + y_factors[c] * xyY[1] + offsets[c];
1574
1575 UVD[0] /= UVD[2];
1576 UVD[1] /= UVD[2];
1577
1578 float UV_star[2] = { 0.f };
1579 const float factors[2] = { 1.39656225667f, 1.4513954287f };
1580 const float half_values[2] = { 1.49217352929f, 1.52488637914f };
1581 for(int c = 0; c < 2; c++)
1582 UV_star[c] = factors[c] * UVD[c] / (fabsf(UVD[c]) + half_values[c]);
1583
1584 // The following is equivalent to a 2D matrix product
1585 UV_star_prime[0] = -1.124983854323892f * UV_star[0] - 0.980483721769325f * UV_star[1];
1586 UV_star_prime[1] = 1.86323315098672f * UV_star[0] + 1.971853092390862f * UV_star[1];
1587
1588}
1589
1590
1591#ifdef _OPENMP
1592#pragma omp declare simd aligned(xyY, JCH: 16)
1593#endif
1594static 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)
1595{
1596 /*
1597 input :
1598 * xyY in normalized CIE XYZ for the 2° 1931 observer adapted for D65
1599 * L_white the lightness of white as dt UCS L* lightness
1600 * cz = 1 for standard pre-print proofing conditions with average surround and n = 20 %
1601 (background = middle grey, white = perfect diffuse white)
1602 range : xy in [0; 1], Y normalized for perfect diffuse white = 1
1603 */
1604
1605 float UV_star_prime[2];
1607
1608 const float L_star = Y_to_dt_UCS_L_star(xyY[2]);
1609 const float M2 = UV_star_prime[0] * UV_star_prime[0] + UV_star_prime[1] * UV_star_prime[1]; // square of colorfulness M
1610
1611 // should be JCH[0] = powf(L_star / L_white), cz) but we treat only the case where cz = 1
1612 JCH[0] = L_star / L_white;
1613 JCH[1] = 15.932993652962535f * powf(L_star, 0.6523997524738018f) * powf(M2, 0.6007557017508491f) / L_white;
1614 JCH[2] = atan2f(UV_star_prime[1], UV_star_prime[0]);
1615}
1616
1617static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1618xyY_to_dt_UCS_JCH_simd(const dt_aligned_pixel_simd_t xyY, const float L_white)
1619{
1620 dt_aligned_pixel_t xyY_a = { xyY[0], xyY[1], xyY[2], 0.f };
1621 dt_aligned_pixel_t JCH = { 0.f };
1624}
1625
1626
1627#ifdef _OPENMP
1628#pragma omp declare simd aligned(xyY, JCH: 16)
1629#endif
1630static 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)
1631{
1632 /*
1633 input :
1634 * xyY in normalized CIE XYZ for the 2° 1931 observer adapted for D65
1635 * L_white the lightness of white as dt UCS L* lightness
1636 * cz = 1 for standard pre-print proofing conditions with average surround and n = 20 %
1637 (background = middle grey, white = perfect diffuse white)
1638 range : xy in [0; 1], Y normalized for perfect diffuse white = 1
1639 */
1640
1641 // should be L_star = powf(JCH[0], 1.f / cz) * L_white but we treat only the case where cz = 1
1642 const float L_star = JCH[0] * L_white;
1643 const float M = powf(JCH[1] * L_white / (15.932993652962535f * powf(L_star, 0.6523997524738018f)), 0.8322850678616855f);
1644
1645 const float U_star_prime = M * cosf(JCH[2]);
1646 const float V_star_prime = M * sinf(JCH[2]);
1647
1648 // The following is equivalent to a 2D matrix product
1649 const float UV_star[2] = { -5.037522385190711f * U_star_prime - 2.504856328185843f * V_star_prime,
1650 4.760029407436461f * U_star_prime + 2.874012963239247f * V_star_prime };
1651
1652 float UV[2] = { 0.f };
1653 const float factors[2] = { 1.39656225667f, 1.4513954287f };
1654 const float half_values[2] = { 1.49217352929f, 1.52488637914f };
1655 for(int c = 0; c < 2; c++)
1656 UV[c] = -half_values[c] * UV_star[c] / (fabsf(UV_star[c]) - factors[c]);
1657
1658 const dt_aligned_pixel_t U_factors = { 0.167171472114775f, -0.150959086409163f, 0.940254742367256f, 0.f };
1659 const dt_aligned_pixel_t V_factors = { 0.141299802443708f, -0.155185060382272f, 1.000000000000000f, 0.f };
1660 const dt_aligned_pixel_t offsets = { -0.00801531300850582f, -0.00843312433578007f, -0.0256325967652889f, 0.f };
1661
1662 dt_aligned_pixel_t xyD = { 0.f };
1664 xyD[c] = U_factors[c] * UV[0] + V_factors[c] * UV[1] + offsets[c];
1665
1666 xyY[0] = xyD[0] / xyD[2];
1667 xyY[1] = xyD[1] / xyD[2];
1669}
1670
1671static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1672dt_UCS_JCH_to_xyY_simd(const dt_aligned_pixel_simd_t JCH, const float L_white)
1673{
1674 dt_aligned_pixel_t JCH_a = { JCH[0], JCH[1], JCH[2], 0.f };
1675 dt_aligned_pixel_t xyY = { 0.f };
1678}
1679
1680
1681static inline __attribute__((always_inline)) void dt_UCS_JCH_to_HSB(const dt_aligned_pixel_t JCH, dt_aligned_pixel_t HSB)
1682{
1683 HSB[2] = JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f);
1684 HSB[1] = (HSB[2] > 0.f) ? JCH[1] / HSB[2] : 0.f;
1685 HSB[0] = JCH[2];
1686}
1687
1688static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1689dt_UCS_JCH_to_HSB_simd(const dt_aligned_pixel_simd_t JCH)
1690{
1691 const float brightness = JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f);
1692 return (dt_aligned_pixel_simd_t){ JCH[2], (brightness > 0.f) ? JCH[1] / brightness : 0.f, brightness, 0.f };
1693}
1694
1695
1696static inline __attribute__((always_inline)) void dt_UCS_HSB_to_JCH(const dt_aligned_pixel_t HSB, dt_aligned_pixel_t JCH)
1697{
1698 JCH[2] = HSB[0];
1699 JCH[1] = HSB[1] * HSB[2];
1700 JCH[0] = HSB[2] / (powf(JCH[1], 1.33654221029386f) + 1.f);
1701}
1702
1703static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1704dt_UCS_HSB_to_JCH_simd(const dt_aligned_pixel_simd_t HSB)
1705{
1706 const float chroma = HSB[1] * HSB[2];
1707 return (dt_aligned_pixel_simd_t){
1708 HSB[2] / (powf(chroma, 1.33654221029386f) + 1.f),
1709 chroma,
1710 HSB[0],
1711 0.f
1712 };
1713}
1714
1715
1716static inline __attribute__((always_inline)) void dt_UCS_JCH_to_HCB(const dt_aligned_pixel_t JCH, dt_aligned_pixel_t HCB)
1717{
1718 HCB[2] = JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f);
1719 HCB[1] = JCH[1];
1720 HCB[0] = JCH[2];
1721}
1722
1723static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1724dt_UCS_JCH_to_HCB_simd(const dt_aligned_pixel_simd_t JCH)
1725{
1726 return (dt_aligned_pixel_simd_t){
1727 JCH[2],
1728 JCH[1],
1729 JCH[0] * (powf(JCH[1], 1.33654221029386f) + 1.f),
1730 0.f
1731 };
1732}
1733
1734
1735static inline __attribute__((always_inline)) void dt_UCS_HCB_to_JCH(const dt_aligned_pixel_t HCB, dt_aligned_pixel_t JCH)
1736{
1737 JCH[2] = HCB[0];
1738 JCH[1] = HCB[1];
1739 JCH[0] = HCB[2] / (powf(HCB[1], 1.33654221029386f) + 1.f);
1740}
1741
1742static inline __attribute__((always_inline)) dt_aligned_pixel_simd_t
1743dt_UCS_HCB_to_JCH_simd(const dt_aligned_pixel_simd_t HCB)
1744{
1745 return (dt_aligned_pixel_simd_t){
1746 HCB[2] / (powf(HCB[1], 1.33654221029386f) + 1.f),
1747 HCB[1],
1748 HCB[0],
1749 0.f
1750 };
1751}
1752
1753
1754static inline __attribute__((always_inline)) void dt_UCS_HSB_to_HPW(const dt_aligned_pixel_t HSB, dt_aligned_pixel_t HPW)
1755{
1756 HPW[2] = sqrtf(HSB[1] * HSB[1] + HSB[2] * HSB[2]);
1757 HPW[1] = (HPW[2] > 0.f) ? HSB[1] / HPW[2] : 0.f;
1758 HPW[0] = HSB[0];
1759}
1760
1761
1762static inline __attribute__((always_inline)) void dt_UCS_HPW_to_HSB(const dt_aligned_pixel_t HPW, dt_aligned_pixel_t HSB)
1763{
1764 HSB[0] = HPW[0];
1765 HSB[1] = HPW[1] * HPW[2];
1766 HSB[2] = fmaxf(sqrtf(HPW[2] * HPW[2] - HSB[1] * HSB[1]), 0.f);
1767}
1768
1769#undef DT_RESTRICT
1770
1771// clang-format off
1772// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1773// vim: shiftwidth=2 expandtab tabstop=2 cindent
1774// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1775// clang-format on
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 gamut_check_Yrg(float4 Ych)
Definition colorspace.h:748
static float4 dt_UCS_JCH_to_HSB(const float4 JCH)
Definition colorspace.h:891
static float4 dt_XYZ_to_xyY(const float4 XYZ)
Definition colorspace.h:635
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:952
static dt_aligned_pixel_t HPW
Definition colorspaces_inline_conversions.h:1755
static dt_aligned_pixel_t Ych
Definition colorspaces_inline_conversions.h:1357
const float U_star_prime
Definition colorspaces_inline_conversions.h:1645
const float fz
Definition colorspaces_inline_conversions.h:255
dt_aligned_pixel_t UVD
Definition colorspaces_inline_conversions.h:1571
static float UV_star_prime[2]
Definition colorspaces_inline_conversions.h:1565
static const float L_white
Definition colorspaces_inline_conversions.h:1594
const float i
Definition colorspaces_inline_conversions.h:669
float UV_star[2]
Definition colorspaces_inline_conversions.h:1578
dt_UCS_JCH_to_xyY(JCH_a, L_white, xyY)
LMS_to_Yrg(LMS, Yrg)
dt_aligned_pixel_t lms
Definition colorspaces_inline_conversions.h:1293
static dt_aligned_pixel_t HSV
Definition colorspaces_inline_conversions.h:767
dt_prophotorgb_to_XYZ(rgb, XYZ)
static dt_aligned_pixel_t xyY
Definition colorspaces_inline_conversions.h:266
const float L_star
Definition colorspaces_inline_conversions.h:1608
dt_sRGB_to_linear_sRGB(sRGB, rgb)
LMS_to_XYZ(LMS, XYZ)
dt_xyY_to_Luv(xyY, Luv)
const float factors[2]
Definition colorspaces_inline_conversions.h:1579
dt_XYZ_to_Rec709_D50(XYZ, rgb)
gradingRGB_to_LMS(rgb, lms)
const float h
Definition colorspaces_inline_conversions.h:1366
static float cbrt_5f(float f)
Definition colorspaces_inline_conversions.h:194
const float c
Definition colorspaces_inline_conversions.h:1365
dt_Lab_to_XYZ(Lab, XYZ)
const float L
Definition colorspaces_inline_conversions.h:724
const dt_aligned_pixel_t V_factors
Definition colorspaces_inline_conversions.h:1659
_dt_Hue_2_RGB(RGB, HSL[0], 2.0f *C, m)
const float V
Definition colorspaces_inline_conversions.h:772
const float g
Definition colorspaces_inline_conversions.h:925
dt_uvY_to_xyY(uvY, xyY)
LMS_to_gradingRGB(lms, rgb)
float UV[2]
Definition colorspaces_inline_conversions.h:1652
const float fx
Definition colorspaces_inline_conversions.h:254
const float denom
Definition colorspaces_inline_conversions.h:1334
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
const dt_aligned_pixel_t f
Definition colorspaces_inline_conversions.h:256
static dt_aligned_pixel_t LCH
Definition colorspaces_inline_conversions.h:835
static float cbrta_halleyf(const float a, const float R)
Definition colorspaces_inline_conversions.h:204
static dt_aligned_pixel_t HCV
Definition colorspaces_inline_conversions.h:807
dt_XYZ_to_sRGB(XYZ, result)
const dt_aligned_pixel_t offsets
Definition colorspaces_inline_conversions.h:1569
Ych_to_Yrg(Ych, Yrg)
XYZ_to_LMS(XYZ, LMS)
static dt_aligned_pixel_t uvY
Definition colorspaces_inline_conversions.h:303
static float dt_camera_rgb_luminance(const dt_aligned_pixel_t rgb)
Definition colorspaces_inline_conversions.h:859
static const dt_colormatrix_t sRGB_to_xyz_transposed
Definition colorspaces_inline_conversions.h:595
const float D50[2] DT_ALIGNED_PIXEL
Definition colorspaces_inline_conversions.h:343
static const dt_colormatrix_t A_transposed
Definition colorspaces_inline_conversions.h:945
static dt_aligned_pixel_t Yrg
Definition colorspaces_inline_conversions.h:1287
const float half_values[2]
Definition colorspaces_inline_conversions.h:1580
dt_XYZ_to_prophotorgb(XYZ, rgb)
static const dt_aligned_pixel_t d50
Definition colorspaces_inline_conversions.h:222
static dt_aligned_pixel_t HSL
Definition colorspaces_inline_conversions.h:719
static float cbf(const float x)
Definition colorspaces_inline_conversions.h:319
const float threshold
Definition colorspaces_inline_conversions.h:340
const float p
Definition colorspaces_inline_conversions.h:930
static const dt_colormatrix_t filmlightRGB_D65_to_LMS_D65
Definition colorspaces_inline_conversions.h:1227
static const float dt_aligned_pixel_t JCH
Definition colorspaces_inline_conversions.h:1595
const float d0
Definition colorspaces_inline_conversions.h:932
const float d
Definition colorspaces_inline_conversions.h:931
static dt_aligned_pixel_t HSB
Definition colorspaces_inline_conversions.h:1682
xyY_to_dt_UCS_UV(xyY, UV_star_prime)
static const float const float const float min
Definition colorspaces_inline_conversions.h:667
static const dt_colormatrix_t M
Definition colorspaces_inline_conversions.h:933
const float n_inv
Definition colorspaces_inline_conversions.h:1057
const dt_colormatrix_t MI
Definition colorspaces_inline_conversions.h:1061
static float lab_f(const float x)
Definition colorspaces_inline_conversions.h:214
static const dt_colormatrix_t AI_transposed
Definition colorspaces_inline_conversions.h:1109
static const dt_colormatrix_t LMS_D65_to_filmlightRGB_D65
Definition colorspaces_inline_conversions.h:1232
const float inc
Definition colorspaces_inline_conversions.h:673
const float m
Definition colorspaces_inline_conversions.h:758
static dt_aligned_pixel_t sRGB
Definition colorspaces_inline_conversions.h:430
static float lab_f_inv(const float x)
Definition colorspaces_inline_conversions.h:240
dt_aligned_pixel_t xyD
Definition colorspaces_inline_conversions.h:1662
const float r
Definition colorspaces_inline_conversions.h:1324
dt_aligned_pixel_t IzAzBz
Definition colorspaces_inline_conversions.h:1074
static float dt_UCS_L_star_to_Y(const float L_star)
Definition colorspaces_inline_conversions.h:1554
dt_Lch_to_Luv(Lch, Luv)
const float max
Definition colorspaces_inline_conversions.h:721
static dt_aligned_pixel_t XYZ_D65
Definition colorspaces_inline_conversions.h:869
const float b
Definition colorspaces_inline_conversions.h:1326
static dt_aligned_pixel_t Lab
Definition colorspaces_inline_conversions.h:228
dt_Luv_to_xyY(Luv, xyY)
Yrg_to_LMS(Yrg, LMS)
static dt_aligned_pixel_t HCB
Definition colorspaces_inline_conversions.h:1717
const float a
Definition colorspaces_inline_conversions.h:1292
Yrg_to_Ych(Yrg, Ych)
static dt_aligned_pixel_t XYZ_D50
Definition colorspaces_inline_conversions.h:493
const float p_inv
Definition colorspaces_inline_conversions.h:1058
const dt_colormatrix_t AI
Definition colorspaces_inline_conversions.h:1066
const dt_aligned_pixel_t y_factors
Definition colorspaces_inline_conversions.h:1568
static const dt_colormatrix_t dt_aligned_pixel_t out
Definition colorspaces_inline_conversions.h:184
const float c3
Definition colorspaces_inline_conversions.h:928
static const dt_colormatrix_t XYZ_D65_to_LMS_2006_D65
Definition colorspaces_inline_conversions.h:1165
const float n
Definition colorspaces_inline_conversions.h:929
dt_Luv_to_Lch(Luv, Lch)
const size_t i_idx
Definition colorspaces_inline_conversions.h:675
dt_Rec709_to_XYZ_D50(rgb, XYZ)
const float M2
Definition colorspaces_inline_conversions.h:1609
static const dt_colormatrix_t LMS_2006_D65_to_XYZ_D65
Definition colorspaces_inline_conversions.h:1170
static dt_aligned_pixel_t XYZ
Definition colorspaces_inline_conversions.h:252
static dt_aligned_pixel_t JzCzhz
Definition colorspaces_inline_conversions.h:1030
static float Y_to_dt_UCS_L_star(const float Y)
Definition colorspaces_inline_conversions.h:1547
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:927
static const dt_colormatrix_t MI_transposed
Definition colorspaces_inline_conversions.h:1104
static const float const float C
Definition colorspaces_inline_conversions.h:666
static const dt_colormatrix_t xyz_to_srgb_transposed
Definition colorspaces_inline_conversions.h:612
const float top
Definition colorspaces_inline_conversions.h:672
dt_XYZ_to_Lab(XYZ, Lab)
static const int row
Definition colorspaces_inline_conversions.h:175
static dt_aligned_pixel_t JzAzBz
Definition colorspaces_inline_conversions.h:923
const float c1
Definition colorspaces_inline_conversions.h:926
static dt_aligned_pixel_t rgb
Definition colorspaces_inline_conversions.h:530
static dt_aligned_pixel_t Luv
Definition colorspaces_inline_conversions.h:329
xyY_to_dt_UCS_JCH(xyY_a, L_white, JCH)
const float V_star_prime
Definition colorspaces_inline_conversions.h:1646
static float _dt_RGB_2_Hue(const dt_aligned_pixel_t RGB, const float max, const float delta)
Definition colorspaces_inline_conversions.h:647
static dt_aligned_pixel_t RGB
Definition colorspaces_inline_conversions.h:509
static dt_aligned_pixel_t linearRGB
Definition colorspaces_inline_conversions.h:618
static const dt_colormatrix_t matrix
Definition colorspaces_inline_conversions.h:182
const float fc
Definition colorspaces_inline_conversions.h:671
static dt_aligned_pixel_t Lch
Definition colorspaces_inline_conversions.h:352
return dt_load_simd_aligned(JCH)
const float delta
Definition colorspaces_inline_conversions.h:722
dt_xyY_to_uvY(xyY, uvY)
float S
Definition colorspaces_inline_conversions.h:725
const dt_aligned_pixel_t U_factors
Definition colorspaces_inline_conversions.h:1658
const float dec
Definition colorspaces_inline_conversions.h:674
else var_H
Definition colorspaces_inline_conversions.h:841
static const float H
Definition colorspaces_inline_conversions.h:666
#define for_each_channel(_var,...)
Definition darktable.h:582
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Multi-tap smudge source sample with directional jitter.
Definition darktable.h:448
#define for_four_channels(_var,...)
Definition darktable.h:584
static const float x
Definition iop_profile.h:239
#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
static void dot_product(const dt_aligned_pixel_t v_in, const dt_colormatrix_t M, dt_aligned_pixel_t v_out)
Definition matrices.h:204
a3
Definition derive_filmic_v6_gamut_mapping.py:62
Y
Definition derive_filmic_v6_gamut_mapping.py:35
mask
Definition dtstyle_to_xmp.py:79
static __m128 _mm_pow_ps1(__m128 x, float y)
Definition sse.h:127