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