Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorspace.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 copyright (c) 2009--2012 johannes hanika.
4 copyright (c) 2011--2013 Ulrich Pegelow
5
6 darktable is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 darktable is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with darktable. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21
22#include "common.h"
23
24static inline float4 matrix_dot(const float4 vector, const float4 matrix[3])
25{
26 float4 output;
27 const float4 vector_copy = { vector.x, vector.y, vector.z, 0.f };
28 output.x = dot(vector_copy, matrix[0]);
29 output.y = dot(vector_copy, matrix[1]);
30 output.z = dot(vector_copy, matrix[2]);
31 output.w = vector.w;
32 return output;
33}
34
35
36static inline float4 matrix_product(const float4 xyz, constant const float *const matrix)
37{
38 const float R = matrix[0] * xyz.x + matrix[1] * xyz.y + matrix[2] * xyz.z;
39 const float G = matrix[3] * xyz.x + matrix[4] * xyz.y + matrix[5] * xyz.z;
40 const float B = matrix[6] * xyz.x + matrix[7] * xyz.y + matrix[8] * xyz.z;
41 const float a = xyz.w;
42 return (float4)(R, G, B, a);
43}
44
45// same as above but with 4xfloat padded matrix
46static inline float4 matrix_product_float4(const float4 xyz, constant const float *const matrix)
47{
48 const float R = matrix[0] * xyz.x + matrix[1] * xyz.y + matrix[2] * xyz.z;
49 const float G = matrix[4] * xyz.x + matrix[5] * xyz.y + matrix[6] * xyz.z;
50 const float B = matrix[8] * xyz.x + matrix[9] * xyz.y + matrix[10] * xyz.z;
51 const float a = xyz.w;
52 return (float4)(R, G, B, a);
53}
54
55static inline float4 Lab_2_LCH(float4 Lab)
56{
57 float H = atan2(Lab.z, Lab.y);
58
59 H = (H > 0.0f) ? H / (2.0f*M_PI_F) : 1.0f - fabs(H) / (2.0f*M_PI_F);
60
61 const float L = Lab.x;
62 const float C = sqrt(Lab.y*Lab.y + Lab.z*Lab.z);
63
64 return (float4)(L, C, H, Lab.w);
65}
66
67
68static inline float4 LCH_2_Lab(float4 LCH)
69{
70 const float L = LCH.x;
71 const float a = cos(2.0f*M_PI_F*LCH.z) * LCH.y;
72 const float b = sin(2.0f*M_PI_F*LCH.z) * LCH.y;
73
74 return (float4)(L, a, b, LCH.w);
75}
76
77
78static inline float4 lab_f(float4 x)
79{
80 const float4 epsilon = 216.0f / 24389.0f;
81 const float4 kappa = 24389.0f / 27.0f;
82 return (x > epsilon) ? native_powr(x, (float4)(1.0f/3.0f)) : (kappa * x + (float4)16.0f) / ((float4)116.0f);
83}
84
85
86static inline float4 XYZ_to_Lab(float4 xyz)
87{
88 float4 lab;
89 const float4 d50 = (float4)(0.9642f, 1.0f, 0.8249f, 1.0f);
90 xyz = lab_f(xyz / d50);
91 lab.x = 116.0f * xyz.y - 16.0f;
92 lab.y = 500.0f * (xyz.x - xyz.y);
93 lab.z = 200.0f * (xyz.y - xyz.z);
94
95 return lab;
96}
97
98
99static inline float4 lab_f_inv(float4 x)
100{
101 const float4 epsilon = 0.206896551f;
102 const float4 kappa = 24389.0f / 27.0f;
103 return (x > epsilon) ? x*x*x : ((float4)116.0f * x - (float4)16.0f)/kappa;
104}
105
106
107static inline float4 Lab_to_XYZ(float4 Lab)
108{
109 const float4 d50 = (float4)(0.9642f, 1.0f, 0.8249f, 0.0f);
110 float4 f;
111 f.y = (Lab.x + 16.0f)/116.0f;
112 f.x = Lab.y/500.0f + f.y;
113 f.z = f.y - Lab.z/200.0f;
114 return d50 * lab_f_inv(f);
115}
116
117static inline float4 prophotorgb_to_XYZ(float4 rgb)
118{
119 const float rgb_to_xyz[3][3] = { // prophoto rgb
120 {0.7976749f, 0.1351917f, 0.0313534f},
121 {0.2880402f, 0.7118741f, 0.0000857f},
122 {0.0000000f, 0.0000000f, 0.8252100f},
123 };
124 float4 XYZ = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
125 XYZ.x += rgb_to_xyz[0][0] * rgb.x;
126 XYZ.x += rgb_to_xyz[0][1] * rgb.y;
127 XYZ.x += rgb_to_xyz[0][2] * rgb.z;
128 XYZ.y += rgb_to_xyz[1][0] * rgb.x;
129 XYZ.y += rgb_to_xyz[1][1] * rgb.y;
130 XYZ.y += rgb_to_xyz[1][2] * rgb.z;
131 XYZ.z += rgb_to_xyz[2][0] * rgb.x;
132 XYZ.z += rgb_to_xyz[2][1] * rgb.y;
133 XYZ.z += rgb_to_xyz[2][2] * rgb.z;
134 return XYZ;
135}
136
137static inline float4 XYZ_to_prophotorgb(float4 XYZ)
138{
139 const float xyz_to_rgb[3][3] = { // prophoto rgb d50
140 { 1.3459433f, -0.2556075f, -0.0511118f},
141 {-0.5445989f, 1.5081673f, 0.0205351f},
142 { 0.0000000f, 0.0000000f, 1.2118128f},
143 };
144 float4 rgb = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
145 rgb.x += xyz_to_rgb[0][0] * XYZ.x;
146 rgb.x += xyz_to_rgb[0][1] * XYZ.y;
147 rgb.x += xyz_to_rgb[0][2] * XYZ.z;
148 rgb.y += xyz_to_rgb[1][0] * XYZ.x;
149 rgb.y += xyz_to_rgb[1][1] * XYZ.y;
150 rgb.y += xyz_to_rgb[1][2] * XYZ.z;
151 rgb.z += xyz_to_rgb[2][0] * XYZ.x;
152 rgb.z += xyz_to_rgb[2][1] * XYZ.y;
153 rgb.z += xyz_to_rgb[2][2] * XYZ.z;
154 return rgb;
155}
156
157static inline float4 Lab_to_prophotorgb(float4 Lab)
158{
159 const float4 XYZ = Lab_to_XYZ(Lab);
160 return XYZ_to_prophotorgb(XYZ);
161}
162
163static inline float4 prophotorgb_to_Lab(float4 rgb)
164{
165 const float4 XYZ = prophotorgb_to_XYZ(rgb);
166 return XYZ_to_Lab(XYZ);
167}
168
169static inline float4 RGB_2_HSL(const float4 RGB)
170{
171 float H, S, L;
172
173 // assumes that each channel is scaled to [0; 1]
174 const float R = RGB.x;
175 const float G = RGB.y;
176 const float B = RGB.z;
177
178 const float var_Min = fmin(R, fmin(G, B));
179 const float var_Max = fmax(R, fmax(G, B));
180 const float del_Max = var_Max - var_Min;
181
182 L = (var_Max + var_Min) / 2.0f;
183
184 if (del_Max < 1e-6f)
185 {
186 H = 0.0f;
187 S = 0.0f;
188 }
189 else
190 {
191 if (L < 0.5f) S = del_Max / (var_Max + var_Min);
192 else S = del_Max / (2.0f - var_Max - var_Min);
193
194 const float del_R = (((var_Max - R) / 6.0f) + (del_Max / 2.0f)) / del_Max;
195 const float del_G = (((var_Max - G) / 6.0f) + (del_Max / 2.0f)) / del_Max;
196 const float del_B = (((var_Max - B) / 6.0f) + (del_Max / 2.0f)) / del_Max;
197
198 if (R == var_Max) H = del_B - del_G;
199 else if (G == var_Max) H = (1.0f / 3.0f) + del_R - del_B;
200 else if (B == var_Max) H = (2.0f / 3.0f) + del_G - del_R;
201
202 if (H < 0.0f) H += 1.0f;
203 if (H > 1.0f) H -= 1.0f;
204 }
205
206 return (float4)(H, S, L, RGB.w);
207}
208
209
210
211static inline float Hue_2_RGB(float v1, float v2, float vH)
212{
213 if (vH < 0.0f) vH += 1.0f;
214 if (vH > 1.0f) vH -= 1.0f;
215 if ((6.0f * vH) < 1.0f) return (v1 + (v2 - v1) * 6.0f * vH);
216 if ((2.0f * vH) < 1.0f) return (v2);
217 if ((3.0f * vH) < 2.0f) return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
218 return (v1);
219}
220
221
222
223static inline float4 HSL_2_RGB(const float4 HSL)
224{
225 float R, G, B;
226
227 const float H = HSL.x;
228 const float S = HSL.y;
229 const float L = HSL.z;
230
231 float var_1, var_2;
232
233 if (S < 1e-6f)
234 {
235 R = B = G = L;
236 }
237 else
238 {
239 if (L < 0.5f) var_2 = L * (1.0f + S);
240 else var_2 = (L + S) - (S * L);
241
242 var_1 = 2.0f * L - var_2;
243
244 R = Hue_2_RGB(var_1, var_2, H + (1.0f / 3.0f));
245 G = Hue_2_RGB(var_1, var_2, H);
246 B = Hue_2_RGB(var_1, var_2, H - (1.0f / 3.0f));
247 }
248
249 // returns RGB scaled to [0; 1] for each channel
250 return (float4)(R, G, B, HSL.w);
251}
252
253static inline float4 RGB_2_HSV(const float4 RGB)
254{
255 float4 HSV;
256
257 const float minv = fmin(RGB.x, fmin(RGB.y, RGB.z));
258 const float maxv = fmax(RGB.x, fmax(RGB.y, RGB.z));
259 const float delta = maxv - minv;
260
261 HSV.z = maxv;
262 HSV.w = RGB.w;
263
264 if(fabs(maxv) > 1e-6f && fabs(delta) > 1e-6f)
265 {
266 HSV.y = delta / maxv;
267 }
268 else
269 {
270 HSV.x = 0.0f;
271 HSV.y = 0.0f;
272 return HSV;
273 }
274
275 if (RGB.x == maxv)
276 HSV.x = (RGB.y - RGB.z) / delta;
277 else if (RGB.y == maxv)
278 HSV.x = 2.0f + (RGB.z - RGB.x) / delta;
279 else
280 HSV.x = 4.0f + (RGB.x - RGB.y) / delta;
281
282 HSV.x /= 6.0f;
283
284 if(HSV.x < 0)
285 HSV.x += 1.0f;
286
287 return HSV;
288}
289
290static inline float4 HSV_2_RGB(const float4 HSV)
291{
292 float4 RGB;
293
294 if (fabs(HSV.y) < 1e-6f)
295 {
296 RGB.x = RGB.y = RGB.z = HSV.z;
297 RGB.w = HSV.w;
298 return RGB;
299 }
300
301 const int i = floor(6.0f*HSV.x);
302 const float v = HSV.z;
303 const float w = HSV.w;
304 const float p = v * (1.0f - HSV.y);
305 const float q = v * (1.0f - HSV.y * (6.0f*HSV.x - i));
306 const float t = v * (1.0f - HSV.y * (1.0f - (6.0f*HSV.x - i)));
307
308 switch (i)
309 {
310 case 0:
311 RGB = (float4)(v, t, p, w);
312 break;
313 case 1:
314 RGB = (float4)(q, v, p, w);
315 break;
316 case 2:
317 RGB = (float4)(p, v, t, w);
318 break;
319 case 3:
320 RGB = (float4)(p, q, v, w);
321 break;
322 case 4:
323 RGB = (float4)(t, p, v, w);
324 break;
325 case 5:
326 default:
327 RGB = (float4)(v, p, q, w);
328 break;
329 }
330 return RGB;
331}
332
333
334// XYZ -> sRGB matrix, D65
335static inline float4 XYZ_to_sRGB(float4 XYZ)
336{
337 float4 sRGB;
338
339 sRGB.x = 3.1338561f * XYZ.x - 1.6168667f * XYZ.y - 0.4906146f * XYZ.z;
340 sRGB.y = -0.9787684f * XYZ.x + 1.9161415f * XYZ.y + 0.0334540f * XYZ.z;
341 sRGB.z = 0.0719453f * XYZ.x - 0.2289914f * XYZ.y + 1.4052427f * XYZ.z;
342 sRGB.w = XYZ.w;
343
344 return sRGB;
345}
346
347
348// sRGB -> XYZ matrix, D65
349static inline float4 sRGB_to_XYZ(float4 sRGB)
350{
351 float4 XYZ;
352
353 XYZ.x = 0.4360747f * sRGB.x + 0.3850649f * sRGB.y + 0.1430804f * sRGB.z;
354 XYZ.y = 0.2225045f * sRGB.x + 0.7168786f * sRGB.y + 0.0606169f * sRGB.z;
355 XYZ.z = 0.0139322f * sRGB.x + 0.0971045f * sRGB.y + 0.7141733f * sRGB.z;
356 XYZ.w = sRGB.w;
357
358 return XYZ;
359}
360
361
362static inline float4 XYZ_to_JzAzBz(float4 XYZ_D65)
363{
364 const float4 M[3] = { { 0.41478972f, 0.579999f, 0.0146480f, 0.0f },
365 { -0.2015100f, 1.120649f, 0.0531008f, 0.0f },
366 { -0.0166008f, 0.264800f, 0.6684799f, 0.0f } };
367
368 const float4 A[3] = { { 0.5f, 0.5f, 0.0f, 0.0f },
369 { 3.524000f, -4.066708f, 0.542708f, 0.0f },
370 { 0.199076f, 1.096799f, -1.295875f, 0.0f } };
371
372 float4 temp1, temp2;
373 // XYZ -> X'Y'Z
374 temp1.x = 1.15f * XYZ_D65.x - 0.15f * XYZ_D65.z;
375 temp1.y = 0.66f * XYZ_D65.y + 0.34f * XYZ_D65.x;
376 temp1.z = XYZ_D65.z;
377 temp1.w = 0.f;
378 // X'Y'Z -> LMS
379 temp2.x = dot(M[0], temp1);
380 temp2.y = dot(M[1], temp1);
381 temp2.z = dot(M[2], temp1);
382 temp2.w = 0.f;
383 // LMS -> L'M'S'
384 temp2 = native_powr(fmax(temp2 / 10000.f, 0.0f), 0.159301758f);
385 temp2 = native_powr((0.8359375f + 18.8515625f * temp2) / (1.0f + 18.6875f * temp2), 134.034375f);
386 // L'M'S' -> Izazbz
387 temp1.x = dot(A[0], temp2);
388 temp1.y = dot(A[1], temp2);
389 temp1.z = dot(A[2], temp2);
390 // Iz -> Jz
391 temp1.x = fmax(0.44f * temp1.x / (1.0f - 0.56f * temp1.x) - 1.6295499532821566e-11f, 0.f);
392 return temp1;
393}
394
395
396static inline float4 JzAzBz_2_XYZ(const float4 JzAzBz)
397{
398 const float b = 1.15f;
399 const float g = 0.66f;
400 const float c1 = 0.8359375f; // 3424 / 2^12
401 const float c2 = 18.8515625f; // 2413 / 2^7
402 const float c3 = 18.6875f; // 2392 / 2^7
403 const float n_inv = 1.0f / 0.159301758f; // 2610 / 2^14
404 const float p_inv = 1.0f / 134.034375f; // 1.7 x 2523 / 2^5
405 const float d = -0.56f;
406 const float d0 = 1.6295499532821566e-11f;
407 const float4 MI[3] = { { 1.9242264357876067f, -1.0047923125953657f, 0.0376514040306180f, 0.0f },
408 { 0.3503167620949991f, 0.7264811939316552f, -0.0653844229480850f, 0.0f },
409 { -0.0909828109828475f, -0.3127282905230739f, 1.5227665613052603f, 0.0f } };
410 const float4 AI[3] = { { 1.0f, 0.1386050432715393f, 0.0580473161561189f, 0.0f },
411 { 1.0f, -0.1386050432715393f, -0.0580473161561189f, 0.0f },
412 { 1.0f, -0.0960192420263190f, -0.8118918960560390f, 0.0f } };
413
414 float4 XYZ, LMS, IzAzBz;
415 // Jz -> Iz
416 IzAzBz = JzAzBz;
417 IzAzBz.x += d0;
418 IzAzBz.x = fmax(IzAzBz.x / (1.0f + d - d * IzAzBz.x), 0.f);
419 // IzAzBz -> L'M'S'
420 LMS.x = dot(AI[0], IzAzBz);
421 LMS.y = dot(AI[1], IzAzBz);
422 LMS.z = dot(AI[2], IzAzBz);
423 LMS.w = 0.f;
424 // L'M'S' -> LMS
425 LMS = native_powr(fmax(LMS, 0.0f), p_inv);
426 LMS = 10000.f * native_powr(fmax((c1 - LMS) / (c3 * LMS - c2), 0.0f), n_inv);
427 // LMS -> X'Y'Z
428 XYZ.x = dot(MI[0], LMS);
429 XYZ.y = dot(MI[1], LMS);
430 XYZ.z = dot(MI[2], LMS);
431 XYZ.w = 0.f;
432 // X'Y'Z -> XYZ_D65
433 float4 XYZ_D65;
434 XYZ_D65.x = (XYZ.x + (b - 1.0f) * XYZ.z) / b;
435 XYZ_D65.y = (XYZ.y + (g - 1.0f) * XYZ_D65.x) / g;
436 XYZ_D65.z = XYZ.z;
437 XYZ_D65.w = JzAzBz.w;
438 return XYZ_D65;
439}
440
441
442static inline float4 JzAzBz_to_JzCzhz(float4 JzAzBz)
443{
444 const float h = atan2(JzAzBz.z, JzAzBz.y) / (2.0f * M_PI_F);
445 float4 JzCzhz;
446 JzCzhz.x = JzAzBz.x;
447 JzCzhz.y = native_sqrt(JzAzBz.y * JzAzBz.y + JzAzBz.z * JzAzBz.z);
448 JzCzhz.z = (h >= 0.0f) ? h : 1.0f + h;
449 JzCzhz.w = JzAzBz.w;
450 return JzCzhz;
451}
452
453
454// Convert CIE 1931 2° XYZ D65 to CIE 2006 LMS D65 (cone space)
455/*
456* The CIE 1931 XYZ 2° observer D65 is converted to CIE 2006 LMS D65 using the approximation by
457* Richard A. Kirk, Chromaticity coordinates for graphic arts based on CIE 2006 LMS
458* with even spacing of Munsell colours
459* https://doi.org/10.2352/issn.2169-2629.2019.27.38
460*/
461
462static inline float4 XYZ_to_LMS(const float4 XYZ)
463{
464 const float4 XYZ_D65_to_LMS_2006_D65[3]
465 = { { 0.257085f, 0.859943f, -0.031061f, 0.f },
466 { -0.394427f, 1.175800f, 0.106423f, 0.f },
467 { 0.064856f, -0.076250f, 0.559067f, 0.f } };
468
470}
471
472
473static inline float4 LMS_to_XYZ(const float4 LMS)
474{
475 const float4 LMS_2006_D65_to_XYZ_D65[3]
476 = { { 1.80794659f, -1.29971660f, 0.34785879f, 0.f },
477 { 0.61783960f, 0.39595453f, -0.04104687f, 0.f },
478 { -0.12546960f, 0.20478038f, 1.74274183f, 0.f } };
479
481}
482
483
484/*
485* Convert from CIE 2006 LMS D65 to Filmlight RGB defined in
486* Richard A. Kirk, Chromaticity coordinates for graphic arts based on CIE 2006 LMS
487* with even spacing of Munsell colours
488* https://doi.org/10.2352/issn.2169-2629.2019.27.38
489*/
490
491static inline float4 gradingRGB_to_LMS(const float4 RGB)
492{
493 const float4 filmlightRGB_D65_to_LMS_D65[3]
494 = { { 0.95f, 0.38f, 0.00f, 0.f },
495 { 0.05f, 0.62f, 0.03f, 0.f },
496 { 0.00f, 0.00f, 0.97f, 0.f } };
497
499}
500
501static inline float4 LMS_to_gradingRGB(const float4 LMS)
502{
503 const float4 LMS_D65_to_filmlightRGB_D65[3]
504 = { { 1.0877193f, -0.66666667f, 0.02061856f, 0.f },
505 { -0.0877193f, 1.66666667f, -0.05154639f, 0.f },
506 { 0.f, 0.f, 1.03092784f, 0.f } };
507
509}
510
511
512/*
513* Re-express the Filmlight RGB triplet as Yrg luminance/chromacity coordinates
514*/
515
516static inline float4 LMS_to_Yrg(const float4 LMS)
517{
518 // compute luminance
519 const float Y = 0.68990272f * LMS.x + 0.34832189f * LMS.y;
520
521 // normalize LMS
522 const float a = LMS.x + LMS.y + LMS.z;
523 const float4 lms = (a == 0.f) ? 0.f : LMS / a;
524
525 // convert to Filmlight rgb (normalized)
526 const float4 rgb = LMS_to_gradingRGB(lms);
527
528 return (float4)(Y, rgb.x, rgb.y, LMS.w);
529}
530
531
532static inline float4 Yrg_to_LMS(const float4 Yrg)
533{
534 const float Y = Yrg.x;
535
536 // reform rgb (normalized) from chroma
537 const float r = Yrg.y;
538 const float g = Yrg.z;
539 const float b = 1.f - r - g;
540 const float4 rgb = { r, g, b, 0.f };
541
542 // convert to lms (normalized)
543 const float4 lms = gradingRGB_to_LMS(rgb);
544
545 // denormalize to LMS
546 const float denom = (0.68990272f * lms.x + 0.34832189f * lms.y);
547 const float a = (denom == 0.f) ? 0.f : Y / denom;
548 return lms * a;
549}
550
551
552/*
553* Re-express Filmlight Yrg in polar coordinates Ych
554*/
555
556static inline float4 Yrg_to_Ych(const float4 Yrg)
557{
558 const float Y = Yrg.x;
559 // Subtract white point. These are the r, g coordinates of
560 // sRGB (D50 adapted) (1, 1, 1) taken through
561 // XYZ D50 -> CAT16 D50->D65 adaptation -> LMS 2006
562 // -> grading RGB conversion.
563 const float r = Yrg.y - 0.21902143f;
564 const float g = Yrg.z - 0.54371398f;
565 const float c = hypot(g, r);
566 const float h = atan2(g, r);
567 return (float4)(Y, c, h, Yrg.w);
568}
569
570
571static inline float4 Ych_to_Yrg(const float4 Ych)
572{
573 const float Y = Ych.x;
574 const float c = Ych.y;
575 const float h = Ych.z;
576 const float r = c * native_cos(h) + 0.21902143f;
577 const float g = c * native_sin(h) + 0.54371398f;
578 return (float4)(Y, r, g, Ych.w);
579}
580
581
582static inline float4 dt_xyY_to_uvY(const float4 xyY)
583{
584 // This is the linear part of the chromaticity transform from CIE L*u*v* e.g. u'v'.
585 // See https://en.wikipedia.org/wiki/CIELUV
586 // It rescales the chromaticity diagram xyY in a more perceptual way,
587 // but it is still not hue-linear and not perfectly perceptual.
588 // As such, it is the only radiometricly-accurate representation of hue non-linearity in human vision system.
589 // Use it for "hue preserving" (as much as possible) gamut mapping in scene-referred space
590 const float denominator = -2.f * xyY.x + 12.f * xyY.y + 3.f;
591 float4 uvY;
592 uvY.x = 4.f * xyY.x / denominator; // u'
593 uvY.y = 9.f * xyY.y / denominator; // v'
594 uvY.z = xyY.z; // Y
595 uvY.w = xyY.w;
596 return uvY;
597}
598
599
600static inline float4 dt_uvY_to_xyY(const float4 uvY)
601{
602 // This is the linear part of chromaticity transform from CIE L*u*v* e.g. u'v'.
603 // See https://en.wikipedia.org/wiki/CIELUV
604 // It rescales the chromaticity diagram xyY in a more perceptual way,
605 // but it is still not hue-linear and not perfectly perceptual.
606 // As such, it is the only radiometricly-accurate representation of hue non-linearity in human vision system.
607 // Use it for "hue preserving" (as much as possible) gamut mapping in scene-referred space
608 const float denominator = 6.0f * uvY.x - 16.f * uvY.y + 12.0f;
609 float4 xyY;
610 xyY.x = 9.f * uvY.x / denominator; // x
611 xyY.y = 4.f * uvY.y / denominator; // y
612 xyY.z = uvY.z; // Y
613 xyY.w = uvY.w;
614 return xyY;
615}
616
617static inline float4 dt_XYZ_to_xyY(const float4 XYZ)
618{
619 float4 xyY;
620 const float sum = XYZ.x + XYZ.y + XYZ.z;
621 xyY.xy = XYZ.xy / sum;
622 xyY.z = XYZ.y;
623 xyY.w = XYZ.w;
624 return xyY;
625}
626
627static inline float4 dt_xyY_to_XYZ(const float4 xyY)
628{
629 float4 XYZ;
630 XYZ.x = xyY.z * xyY.x / xyY.y;
631 XYZ.y = xyY.z;
632 XYZ.z = xyY.z * (1.f - xyY.x - xyY.y) / xyY.y;
633 XYZ.w = xyY.w;
634 return XYZ;
635}
636
637// port src/common/chromatic_adaptation.h
638
639static inline float4 convert_XYZ_to_bradford_LMS(const float4 XYZ)
640{
641 // Warning : needs XYZ normalized with Y - you need to downscale before
642 const float4 XYZ_to_Bradford_LMS[3] = { { 0.8951f, 0.2664f, -0.1614f, 0.f },
643 { -0.7502f, 1.7135f, 0.0367f, 0.f },
644 { 0.0389f, -0.0685f, 1.0296f, 0.f } };
645
646 return matrix_dot(XYZ, XYZ_to_Bradford_LMS);
647}
648
649static inline float4 convert_bradford_LMS_to_XYZ(const float4 LMS)
650{
651 // Warning : output XYZ normalized with Y - you need to upscale later
652 const float4 Bradford_LMS_to_XYZ[3] = { { 0.9870f, -0.1471f, 0.1600f, 0.f },
653 { 0.4323f, 0.5184f, 0.0493f, 0.f },
654 { -0.0085f, 0.0400f, 0.9685f, 0.f } };
655
656 return matrix_dot(LMS, Bradford_LMS_to_XYZ);
657}
658
659static inline float4 convert_XYZ_to_CAT16_LMS(const float4 XYZ)
660{
661 // Warning : needs XYZ normalized with Y - you need to downscale before
662 const float4 XYZ_to_CAT16_LMS[3] = { { 0.401288f, 0.650173f, -0.051461f, 0.f },
663 { -0.250268f, 1.204414f, 0.045854f, 0.f },
664 { -0.002079f, 0.048952f, 0.953127f, 0.f } };
665
666 return matrix_dot(XYZ, XYZ_to_CAT16_LMS);
667}
668
669static inline float4 convert_CAT16_LMS_to_XYZ(const float4 LMS)
670{
671 // Warning : output XYZ normalized with Y - you need to upscale later
672 const float4 CAT16_LMS_to_XYZ[3] = { { 1.862068f, -1.011255f, 0.149187f, 0.f },
673 { 0.38752f , 0.621447f, -0.008974f, 0.f },
674 { -0.015841f, -0.034123f, 1.049964f, 0.f } };
675
676 return matrix_dot(LMS, CAT16_LMS_to_XYZ);
677}
678
679static inline void bradford_adapt_D50(float4 *lms_in,
680 const float4 origin_illuminant,
681 const float p, const int full)
682{
683 // Bradford chromatic adaptation from origin to target D50 illuminant in LMS space
684 // p = powf(origin_illuminant[2] / D50[2], 0.0834f) needs to be precomputed for performance,
685 // since it is independent from current pixel values
686 // origin illuminant need also to be precomputed to LMS
687
688 // Precomputed D50 primaries in Bradford LMS for ICC transforms
689 const float4 D50 = { 0.996078f, 1.020646f, 0.818155f, 0.f };
690
691 if(full)
692 {
693 float4 temp = *lms_in / origin_illuminant;
694
695 // use linear Bradford if B is negative
696 temp.z = (temp.z > 0.f) ? native_powr(temp.z, p) : temp.z;
697
698 *lms_in = D50 * temp;
699 }
700 else
701 *lms_in *= D50 / origin_illuminant;
702}
703
704static inline void CAT16_adapt_D50(float4 *lms_in,
705 const float4 origin_illuminant,
706 const float D, const int full)
707{
708 // CAT16 chromatic adaptation from origin to target D50 illuminant in LMS space
709 // D is the coefficient of adaptation, depending of the surround lighting
710 // origin illuminant need also to be precomputed to LMS
711
712 // Precomputed D50 primaries in CAT16 LMS for ICC transforms
713 const float4 D50 = { 0.994535f, 1.000997f, 0.833036f, 0.f };
714
715 if(full) *lms_in *= D50 / origin_illuminant;
716 else *lms_in *= (D * D50 / origin_illuminant + 1.f - D);
717}
718
719static inline void XYZ_adapt_D50(float4 *lms_in,
720 const float4 origin_illuminant)
721{
722 // XYZ chromatic adaptation from origin to target D65 illuminant in XYZ space
723 // origin illuminant need also to be precomputed to XYZ
724
725 // Precomputed D50 primaries in XYZ for camera WB adjustment
726 const float4 D50 = { 0.9642119944211994f, 1.0f, 0.8251882845188288f, 0.f };
727 *lms_in *= D50 / origin_illuminant;
728}
729
730static inline float4 gamut_check_Yrg(float4 Ych)
731{
732 // Do a test conversion to Yrg
733 float4 Yrg = Ych_to_Yrg(Ych);
734
735 // Gamut-clip in Yrg at constant hue and luminance
736 // e.g. find the max chroma value that fits in gamut at the current hue
737 const float D65_r = 0.21902143f;
738 const float D65_g = 0.54371398f;
739 float max_c = Ych.y;
740 const float cos_h = native_cos(Ych.z);
741 const float sin_h = native_sin(Ych.z);
742
743 if(Yrg.y < 0.f)
744 {
745 max_c = fmin(-D65_r / cos_h, max_c);
746 }
747 if(Yrg.z < 0.f)
748 {
749 max_c = fmin(-D65_g / sin_h, max_c);
750 }
751 if(Yrg.y + Yrg.z > 1.f)
752 {
753 max_c = fmin((1.f - D65_r - D65_g) / (cos_h + sin_h), max_c);
754 }
755
756 // Overwrite chroma with the sanitized value and
757 Ych.y = max_c;
758
759 return Ych;
760}
761
762
771static inline float Y_to_dt_UCS_L_star(const float Y)
772{
773 // WARNING: L_star needs to be < 2.098883786377, meaning Y needs to be < 3.875766378407574e+19
774 const float Y_hat = native_powr(Y, 0.631651345306265f);
775 return 2.098883786377f * Y_hat / (Y_hat + 1.12426773749357f);
776}
777
778static inline float dt_UCS_L_star_to_Y(const float L_star)
779{
780 // WARNING: L_star needs to be < 2.098883786377, meaning Y needs to be < 3.875766378407574e+19
781 return native_powr((1.12426773749357f * L_star / (2.098883786377f - L_star)), 1.5831518565279648f);
782}
783
784static inline void xyY_to_dt_UCS_UV(const float4 xyY, float UV_star_prime[2])
785{
786 float4 x_factors = { -0.783941002840055f, 0.745273540913283f, 0.318707282433486f, 0.f };
787 float4 y_factors = { 0.277512987809202f, -0.205375866083878f, 2.16743692732158f, 0.f };
788 float4 offsets = { 0.153836578598858f, -0.165478376301988f, 0.291320554395942f, 0.f };
789
790 float4 UVD = x_factors * xyY.x + y_factors * xyY.y + offsets;
791 UVD.xy /= UVD.z;
792
793 float UV_star[2] = { 0.f };
794 const float factors[2] = { 1.39656225667f, 1.4513954287f };
795 const float half_values[2] = { 1.49217352929f, 1.52488637914f };
796 for(int c = 0; c < 2; c++)
797 UV_star[c] = factors[c] * ((float *)&UVD)[c] / (fabs(((float *)&UVD)[c]) + half_values[c]);
798
799 // The following is equivalent to a 2D matrix product
800 UV_star_prime[0] = -1.124983854323892f * UV_star[0] - 0.980483721769325f * UV_star[1];
801 UV_star_prime[1] = 1.86323315098672f * UV_star[0] + 1.971853092390862f * UV_star[1];
802}
803
804
805static inline float4 xyY_to_dt_UCS_JCH(const float4 xyY, const float L_white)
806{
807 /*
808 input :
809 * xyY in normalized CIE XYZ for the 2° 1931 observer adapted for D65
810 * L_white the lightness of white as dt UCS L* lightness
811 * cz = 1 for standard pre-print proofing conditions with average surround and n = 20 %
812 (background = middle grey, white = perfect diffuse white)
813 range : xy in [0; 1], Y normalized for perfect diffuse white = 1
814 */
815
816 float UV_star_prime[2];
817 xyY_to_dt_UCS_UV(xyY, UV_star_prime);
818
819 const float L_star = Y_to_dt_UCS_L_star(xyY.z);
820 const float M2 = UV_star_prime[0] * UV_star_prime[0] + UV_star_prime[1] * UV_star_prime[1]; // square of colorfulness M
821
822 // should be JCH[0] = powf(L_star / L_white), cz) but we treat only the case where cz = 1
823 float4 JCH;
824 JCH.x = L_star / L_white;
825 JCH.y = 15.932993652962535f * native_powr(L_star, 0.6523997524738018f) * native_powr(M2, 0.6007557017508491f) / L_white;
826 JCH.z = atan2(UV_star_prime[1], UV_star_prime[0]);
827 return JCH;
828
829}
830
831static inline float4 dt_UCS_JCH_to_xyY(const float4 JCH, const float L_white)
832{
833 /*
834 input :
835 * xyY in normalized CIE XYZ for the 2° 1931 observer adapted for D65
836 * L_white the lightness of white as dt UCS L* lightness
837 * cz = 1 for standard pre-print proofing conditions with average surround and n = 20 %
838 (background = middle grey, white = perfect diffuse white)
839 range : xy in [0; 1], Y normalized for perfect diffuse white = 1
840 */
841
842 // should be L_star = powf(JCH[0], 1.f / cz) * L_white but we treat only the case where cz = 1
843 const float L_star = JCH.x * L_white;
844 const float M = native_powr(JCH.y * L_white / (15.932993652962535f * native_powr(L_star, 0.6523997524738018f)), 0.8322850678616855f);
845
846 const float U_star_prime = M * native_cos(JCH.z);
847 const float V_star_prime = M * native_sin(JCH.z);
848
849 // The following is equivalent to a 2D matrix product
850 const float UV_star[2] = { -5.037522385190711f * U_star_prime - 2.504856328185843f * V_star_prime,
851 4.760029407436461f * U_star_prime + 2.874012963239247f * V_star_prime };
852
853 float UV[2] = {0.f};
854 const float factors[2] = { 1.39656225667f, 1.4513954287f };
855 const float half_values[2] = { 1.49217352929f,1.52488637914f };
856 for(int c = 0; c < 2; c++)
857 UV[c] = -half_values[c] * UV_star[c] / (fabs(UV_star[c]) - factors[c]);
858
859 const float4 U_factors = { 0.167171472114775f, -0.150959086409163f, 0.940254742367256f, 0.f };
860 const float4 V_factors = { 0.141299802443708f, -0.155185060382272f, 1.000000000000000f, 0.f };
861 const float4 offsets = { -0.00801531300850582f, -0.00843312433578007f, -0.0256325967652889f, 0.f };
862
863 float4 xyD = U_factors * UV[0] + V_factors * UV[1] + offsets;
864
865 float4 xyY;
866 xyY.x = xyD.x / xyD.z;
867 xyY.y = xyD.y / xyD.z;
868 xyY.z = dt_UCS_L_star_to_Y(L_star);
869 return xyY;
870}
871
872
873static inline float4 dt_UCS_JCH_to_HSB(const float4 JCH)
874{
875 float4 HSB;
876 HSB.z = JCH.x * (native_powr(JCH.y, 1.33654221029386f) + 1.f);
877 HSB.y = (HSB.z > 0.f) ? JCH.y / HSB.z : 0.f;
878 HSB.x = JCH.z;
879 return HSB;
880}
881
882
883static inline float4 dt_UCS_HSB_to_JCH(const float4 HSB)
884{
885 float4 JCH;
886 JCH.z = HSB.x;
887 JCH.y = HSB.y * HSB.z;
888 JCH.x = HSB.z / (native_powr(JCH.y, 1.33654221029386f) + 1.f);
889 return JCH;
890}
891
892
893static inline float4 dt_UCS_JCH_to_HCB(const float4 JCH)
894{
895 float4 HCB;
896 HCB.z = JCH.x * (native_powr(JCH.y, 1.33654221029386f) + 1.f);
897 HCB.y = JCH.y;
898 HCB.x = JCH.z;
899 return HCB;
900}
901
902
903static inline float4 dt_UCS_HCB_to_JCH(const float4 HCB)
904{
905 float4 JCH;
906 JCH.z = HCB.x;
907 JCH.y = HCB.y;
908 JCH.x = HCB.z / (native_powr(HCB.y, 1.33654221029386f) + 1.f);
909 return JCH;
910}
static float Lab(float val)
Definition ansel-curve-tool.c:273
const dt_colormatrix_t CAT16_LMS_to_XYZ
Definition chromatic_adaptation.h:74
const dt_colormatrix_t XYZ_to_CAT16_LMS
Definition chromatic_adaptation.h:70
const dt_colormatrix_t XYZ_to_Bradford_LMS
Definition chromatic_adaptation.h:40
const dt_colormatrix_t Bradford_LMS_to_XYZ
Definition chromatic_adaptation.h:44
@ HSL
Definition colorbalance.c:84
static float4 dt_UCS_JCH_to_HCB(const float4 JCH)
Definition colorspace.h:893
static float4 RGB_2_HSV(const float4 RGB)
Definition colorspace.h:253
static float4 LCH_2_Lab(float4 LCH)
Definition colorspace.h:68
static float4 XYZ_to_JzAzBz(float4 XYZ_D65)
Definition colorspace.h:362
static float4 dt_xyY_to_XYZ(const float4 xyY)
Definition colorspace.h:627
static float4 Lab_to_prophotorgb(float4 Lab)
Definition colorspace.h:157
static float4 prophotorgb_to_Lab(float4 rgb)
Definition colorspace.h:163
static float4 dt_UCS_HSB_to_JCH(const float4 HSB)
Definition colorspace.h:883
static void CAT16_adapt_D50(float4 *lms_in, const float4 origin_illuminant, const float D, const int full)
Definition colorspace.h:704
static float4 gradingRGB_to_LMS(const float4 RGB)
Definition colorspace.h:491
static void xyY_to_dt_UCS_UV(const float4 xyY, float UV_star_prime[2])
Definition colorspace.h:784
static float4 XYZ_to_sRGB(float4 XYZ)
Definition colorspace.h:335
static float4 gamut_check_Yrg(float4 Ych)
Definition colorspace.h:730
static float4 dt_xyY_to_uvY(const float4 xyY)
Definition colorspace.h:582
static float4 XYZ_to_Lab(float4 xyz)
Definition colorspace.h:86
static float4 matrix_product_float4(const float4 xyz, constant const float *const matrix)
Definition colorspace.h:46
static float4 JzAzBz_2_XYZ(const float4 JzAzBz)
Definition colorspace.h:396
static float4 HSL_2_RGB(const float4 HSL)
Definition colorspace.h:223
static float4 LMS_to_XYZ(const float4 LMS)
Definition colorspace.h:473
static float4 prophotorgb_to_XYZ(float4 rgb)
Definition colorspace.h:117
static float4 Lab_2_LCH(float4 Lab)
Definition colorspace.h:55
static float4 convert_XYZ_to_bradford_LMS(const float4 XYZ)
Definition colorspace.h:639
static float4 JzAzBz_to_JzCzhz(float4 JzAzBz)
Definition colorspace.h:442
static float4 matrix_product(const float4 xyz, constant const float *const matrix)
Definition colorspace.h:36
static float4 Yrg_to_Ych(const float4 Yrg)
Definition colorspace.h:556
static float4 Ych_to_Yrg(const float4 Ych)
Definition colorspace.h:571
static float Hue_2_RGB(float v1, float v2, float vH)
Definition colorspace.h:211
static float4 dt_uvY_to_xyY(const float4 uvY)
Definition colorspace.h:600
static float4 convert_XYZ_to_CAT16_LMS(const float4 XYZ)
Definition colorspace.h:659
static void bradford_adapt_D50(float4 *lms_in, const float4 origin_illuminant, const float p, const int full)
Definition colorspace.h:679
static float4 lab_f(float4 x)
Definition colorspace.h:78
static float4 LMS_to_gradingRGB(const float4 LMS)
Definition colorspace.h:501
static void XYZ_adapt_D50(float4 *lms_in, const float4 origin_illuminant)
Definition colorspace.h:719
static float4 RGB_2_HSL(const float4 RGB)
Definition colorspace.h:169
static float4 dt_UCS_JCH_to_HSB(const float4 JCH)
Definition colorspace.h:873
static float4 matrix_dot(const float4 vector, const float4 matrix[3])
Definition colorspace.h:24
static float dt_UCS_L_star_to_Y(const float L_star)
Definition colorspace.h:778
static float4 dt_UCS_JCH_to_xyY(const float4 JCH, const float L_white)
Definition colorspace.h:831
static float4 HSV_2_RGB(const float4 HSV)
Definition colorspace.h:290
static float4 Yrg_to_LMS(const float4 Yrg)
Definition colorspace.h:532
static float4 LMS_to_Yrg(const float4 LMS)
Definition colorspace.h:516
static float Y_to_dt_UCS_L_star(const float Y)
Definition colorspace.h:771
static float4 lab_f_inv(float4 x)
Definition colorspace.h:99
static float4 xyY_to_dt_UCS_JCH(const float4 xyY, const float L_white)
Definition colorspace.h:805
static float4 dt_XYZ_to_xyY(const float4 XYZ)
Definition colorspace.h:617
static float4 convert_CAT16_LMS_to_XYZ(const float4 LMS)
Definition colorspace.h:669
static float4 Lab_to_XYZ(float4 Lab)
Definition colorspace.h:107
static float4 XYZ_to_prophotorgb(float4 XYZ)
Definition colorspace.h:137
static float4 convert_bradford_LMS_to_XYZ(const float4 LMS)
Definition colorspace.h:649
static float4 XYZ_to_LMS(const float4 XYZ)
Definition colorspace.h:462
static float4 dt_UCS_HCB_to_JCH(const float4 HCB)
Definition colorspace.h:903
static float4 sRGB_to_XYZ(float4 sRGB)
Definition colorspace.h:349
#define B(y, x)
Definition colorspaces.c:149
#define A(y, x)
Definition colorspaces.c:148
static const dt_aligned_pixel_t d50
Definition colorspaces_inline_conversions.h:207
static const dt_colormatrix_t filmlightRGB_D65_to_LMS_D65
Definition colorspaces_inline_conversions.h:1069
static const dt_colormatrix_t LMS_D65_to_filmlightRGB_D65
Definition colorspaces_inline_conversions.h:1074
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
#define S(V, params)
Definition common/histogram.c:32
#define M_PI_F
Definition data/kernels/common.h:32
#define H
Definition diffuse.c:526
static float f(const float t, const float c, const float x)
Definition graduatednd.c:173
static double dot(int g[], double x, double y, double z)
Definition grain.c:147
#define R
#define c2
#define c1