Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorspaces.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Alex Chateau.
4 Copyright (C) 2010-2011 Henrik Andersson.
5 Copyright (C) 2010-2013, 2016-2017 johannes hanika.
6 Copyright (C) 2010 José Carlos García Sogo.
7 Copyright (C) 2010, 2012-2014 Pascal de Bruijn.
8 Copyright (C) 2011 Antony Dovgal.
9 Copyright (C) 2011 Bruce Guenter.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011-2018 Tobias Ellinghaus.
12 Copyright (C) 2011, 2013-2014 Ulrich Pegelow.
13 Copyright (C) 2012 Christian Tellefsen.
14 Copyright (C) 2012 Jérémy Rosen.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2014-2016 Pedro Côrte-Real.
17 Copyright (C) 2014-2016 Roman Lebedev.
18 Copyright (C) 2015 parafin.
19 Copyright (C) 2016 Peter Budai.
20 Copyright (C) 2018-2019 Edgardo Hoszowski.
21 Copyright (C) 2019 Andreas Schneider.
22 Copyright (C) 2019-2020 Heiko Bauke.
23 Copyright (C) 2019 jakubfi.
24 Copyright (C) 2019 luzpaz.
25 Copyright (C) 2019 Matthias Vogelgesang.
26 Copyright (C) 2019-2022 Pascal Obry.
27 Copyright (C) 2019 Philippe Weyland.
28 Copyright (C) 2020 a.
29 Copyright (C) 2020, 2022-2026 Aurélien PIERRE.
30 Copyright (C) 2020 Dan Torop.
31 Copyright (C) 2020 Hubert Kowalski.
32 Copyright (C) 2020-2021 Miloš Komarčević.
33 Copyright (C) 2020-2021 Ralf Brown.
34 Copyright (C) 2021 Sakari Kapanen.
35 Copyright (C) 2022 Martin Bařinka.
36 Copyright (C) 2023 Alynx Zhou.
37 Copyright (C) 2023 Luca Zulberti.
38
39 darktable is free software: you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation, either version 3 of the License, or
42 (at your option) any later version.
43
44 darktable is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
48
49 You should have received a copy of the GNU General Public License
50 along with darktable. If not, see <http://www.gnu.org/licenses/>.
51*/
52
53#include "common/colorspaces.h"
55#include "common/darktable.h"
56#include "common/debug.h"
57#include "common/image_cache.h"
59#include "common/math.h"
60#include "common/matrices.h"
62#include "common/utility.h"
63#include "control/conf.h"
64#include "control/control.h"
65#include "develop/imageop.h"
66
67#include <strings.h>
68
69#ifdef USE_COLORDGTK
70#include "colord-gtk.h"
71#endif
72
73#ifdef _WIN32
74#include <dwmapi.h>
75#include <gdk/gdkwin32.h>
76#endif
77
78#ifdef HAVE_OPENJPEG
79#include "common/imageio_j2k.h"
80#endif
81#include "common/imageio_jpeg.h"
82#include "common/imageio_png.h"
83#include "common/imageio_tiff.h"
84#ifdef HAVE_LIBAVIF
85#include "common/imageio_avif.h"
86#endif
87#ifdef HAVE_LIBHEIF
88#include "common/imageio_heif.h"
89#endif
90
91#if 0
92#include <ApplicationServices/ApplicationServices.h>
93#include <Carbon/Carbon.h>
94#include <CoreServices/CoreServices.h>
95#endif
96
98 cmsHPROFILE profile, const char *name, int in_pos,
99 int out_pos, int display_pos, int category_pos,
100 int work_pos);
101
102static const cmsCIEXYZ d65 = {0.95045471, 1.00000000, 1.08905029};
103
104//D65 (sRGB, AdobeRGB, Rec2020)
105static const cmsCIExyY D65xyY = {0.312700492, 0.329000939, 1.0};
106
107//D60
108//static const cmsCIExyY d60 = {0.32168, 0.33767, 1.0};
109
110//D50 (ProPhoto RGB)
111static const cmsCIExyY D50xyY = {0.3457, 0.3585, 1.0};
112
113// D65:
114static const cmsCIExyYTRIPLE sRGB_Primaries = {
115 {0.6400, 0.3300, 1.0}, // red
116 {0.3000, 0.6000, 1.0}, // green
117 {0.1500, 0.0600, 1.0} // blue
118};
119
120// D65:
121static const cmsCIExyYTRIPLE Rec2020_Primaries = {
122 {0.7080, 0.2920, 1.0}, // red
123 {0.1700, 0.7970, 1.0}, // green
124 {0.1310, 0.0460, 1.0} // blue
125};
126
127// D65:
128static const cmsCIExyYTRIPLE Rec709_Primaries = {
129 {0.6400, 0.3300, 1.0}, // red
130 {0.3000, 0.6000, 1.0}, // green
131 {0.1500, 0.0600, 1.0} // blue
132};
133
134// D65:
135static const cmsCIExyYTRIPLE Adobe_Primaries = {
136 {0.6400, 0.3300, 1.0}, // red
137 {0.2100, 0.7100, 1.0}, // green
138 {0.1500, 0.0600, 1.0} // blue
139};
140
141// D65:
142static const cmsCIExyYTRIPLE P3_Primaries = {
143 {0.680, 0.320, 1.0}, // red
144 {0.265, 0.690, 1.0}, // green
145 {0.150, 0.060, 1.0} // blue
146};
147
148// https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space
149// D50:
150static const cmsCIExyYTRIPLE ProPhoto_Primaries = {
151 /* x, y, Y */
152 { 0.734699, 0.265301, 1.0000 }, /* red */
153 { 0.159597, 0.840403, 1.0000 }, /* green */
154 { 0.036598, 0.000105, 1.0000 }, /* blue */
155};
156
158
159#define generate_mat3inv_body(c_type, A, B) \
160 int mat3inv_##c_type(c_type *const dst, const c_type *const src) \
161 { \
162 \
163 const c_type det = A(1, 1) * (A(3, 3) * A(2, 2) - A(3, 2) * A(2, 3)) \
164 - A(2, 1) * (A(3, 3) * A(1, 2) - A(3, 2) * A(1, 3)) \
165 + A(3, 1) * (A(2, 3) * A(1, 2) - A(2, 2) * A(1, 3)); \
166 \
167 const c_type epsilon = 1e-7f; \
168 if(fabs(det) < epsilon) return 1; \
169 \
170 const c_type invDet = 1.0 / det; \
171 \
172 B(1, 1) = invDet * (A(3, 3) * A(2, 2) - A(3, 2) * A(2, 3)); \
173 B(1, 2) = -invDet * (A(3, 3) * A(1, 2) - A(3, 2) * A(1, 3)); \
174 B(1, 3) = invDet * (A(2, 3) * A(1, 2) - A(2, 2) * A(1, 3)); \
175 \
176 B(2, 1) = -invDet * (A(3, 3) * A(2, 1) - A(3, 1) * A(2, 3)); \
177 B(2, 2) = invDet * (A(3, 3) * A(1, 1) - A(3, 1) * A(1, 3)); \
178 B(2, 3) = -invDet * (A(2, 3) * A(1, 1) - A(2, 1) * A(1, 3)); \
179 \
180 B(3, 1) = invDet * (A(3, 2) * A(2, 1) - A(3, 1) * A(2, 2)); \
181 B(3, 2) = -invDet * (A(3, 2) * A(1, 1) - A(3, 1) * A(1, 2)); \
182 B(3, 3) = invDet * (A(2, 2) * A(1, 1) - A(2, 1) * A(1, 2)); \
183 return 0; \
184 }
185
186#define A(y, x) src[(y - 1) * 3 + (x - 1)]
187#define B(y, x) dst[(y - 1) * 3 + (x - 1)]
190
191 int mat3inv(float *const dst, const float *const src)
192{
193 return mat3inv_float(dst, src);
194}
195
197#undef B
198#undef A
199#undef generate_mat3inv_body
200
201
204 const char *filename,
206
208static int dt_colorspaces_get_matrix_from_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg,
209 float *lutb, const int lutsize, const int input)
210{
211 // create an OpenCL processable matrix + tone curves from an cmsHPROFILE:
212 // NOTE: may be invoked with matrix and LUT pointers set to null to find
213 // out if the profile can be created at all.
214
215 // check this first:
216 if(IS_NULL_PTR(prof) || !cmsIsMatrixShaper(prof)) return 1;
217
218 // there are some profiles that contain both a color LUT for some specific
219 // intent and a generic matrix. in some cases the matrix might be
220 // deliberately wrong with swapped blue and red channels in order to easily
221 // detect if a color managed software is applying the LUT or the matrix.
222 // thus, if this profile contains LUT for any intent, it might also contain
223 // swapped matrix, so the only right way to handle it is to let LCMS apply it.
224 const int UsedDirection = input ? LCMS_USED_AS_INPUT : LCMS_USED_AS_OUTPUT;
225
226 if(cmsIsCLUT(prof, INTENT_PERCEPTUAL, UsedDirection)
227 || cmsIsCLUT(prof, INTENT_RELATIVE_COLORIMETRIC, UsedDirection)
228 || cmsIsCLUT(prof, INTENT_ABSOLUTE_COLORIMETRIC, UsedDirection)
229 || cmsIsCLUT(prof, INTENT_SATURATION, UsedDirection))
230 return 1;
231
232 cmsToneCurve *red_curve = cmsReadTag(prof, cmsSigRedTRCTag);
233 cmsToneCurve *green_curve = cmsReadTag(prof, cmsSigGreenTRCTag);
234 cmsToneCurve *blue_curve = cmsReadTag(prof, cmsSigBlueTRCTag);
235
236 cmsCIEXYZ *red_color = cmsReadTag(prof, cmsSigRedColorantTag);
237 cmsCIEXYZ *green_color = cmsReadTag(prof, cmsSigGreenColorantTag);
238 cmsCIEXYZ *blue_color = cmsReadTag(prof, cmsSigBlueColorantTag);
239
240 if(IS_NULL_PTR(red_curve) || IS_NULL_PTR(green_curve) || IS_NULL_PTR(blue_curve) || IS_NULL_PTR(red_color) || IS_NULL_PTR(green_color) || IS_NULL_PTR(blue_color)) return 2;
241
242 dt_colormatrix_t matrix_tmp = { { red_color->X, green_color->X, blue_color->X },
243 { red_color->Y, green_color->Y, blue_color->Y },
244 { red_color->Z, green_color->Z, blue_color->Z } };
245
246 // some camera ICC profiles claim to have color locations for red, green and blue base colors defined,
247 // but in fact these are all set to zero. we catch this case here.
248 float sum = 0.0f;
249 for(int k1 = 0; k1 < 3; k1++)
250 for(int k2 = 0; k2 < 3; k2++)
251 sum += matrix_tmp[k1][k2];
252 if(sum == 0.0f) return 3;
253
254 if(input)
255 {
256 // mark as linear, if they are:
257 if(lutr && lutg && lutb)
258 {
259 if(cmsIsToneCurveLinear(red_curve))
260 lutr[0] = -1.0f;
261 else
262 for(int k = 0; k < lutsize; k++) lutr[k] = cmsEvalToneCurveFloat(red_curve, k / (lutsize - 1.0f));
263 if(cmsIsToneCurveLinear(green_curve))
264 lutg[0] = -1.0f;
265 else
266 for(int k = 0; k < lutsize; k++) lutg[k] = cmsEvalToneCurveFloat(green_curve, k / (lutsize - 1.0f));
267 if(cmsIsToneCurveLinear(blue_curve))
268 lutb[0] = -1.0f;
269 else
270 for(int k = 0; k < lutsize; k++) lutb[k] = cmsEvalToneCurveFloat(blue_curve, k / (lutsize - 1.0f));
271 }
272 }
273 else
274 {
275 // invert profile->XYZ matrix for output profiles
277 memcpy(tmp, matrix_tmp, sizeof(dt_colormatrix_t));
278 if(mat3SSEinv(matrix_tmp, tmp))
279 return 3;
280 // also need to reverse gamma, to apply reverse before matrix multiplication:
281 cmsToneCurve *rev_red = cmsReverseToneCurveEx(0x8000, red_curve);
282 cmsToneCurve *rev_green = cmsReverseToneCurveEx(0x8000, green_curve);
283 cmsToneCurve *rev_blue = cmsReverseToneCurveEx(0x8000, blue_curve);
284 if(IS_NULL_PTR(rev_red) || IS_NULL_PTR(rev_green) || IS_NULL_PTR(rev_blue))
285 {
286 cmsFreeToneCurve(rev_red);
287 cmsFreeToneCurve(rev_green);
288 cmsFreeToneCurve(rev_blue);
289 return 4;
290 }
291
292 if(lutr && lutg && lutb)
293 {
294 // pass on tonecurves, in case lutsize > 0:
295 if(cmsIsToneCurveLinear(red_curve))
296 lutr[0] = -1.0f;
297 else
298 for(int k = 0; k < lutsize; k++) lutr[k] = cmsEvalToneCurveFloat(rev_red, k / (lutsize - 1.0f));
299 if(cmsIsToneCurveLinear(green_curve))
300 lutg[0] = -1.0f;
301 else
302 for(int k = 0; k < lutsize; k++) lutg[k] = cmsEvalToneCurveFloat(rev_green, k / (lutsize - 1.0f));
303 if(cmsIsToneCurveLinear(blue_curve))
304 lutb[0] = -1.0f;
305 else
306 for(int k = 0; k < lutsize; k++) lutb[k] = cmsEvalToneCurveFloat(rev_blue, k / (lutsize - 1.0f));
307 }
308
309 cmsFreeToneCurve(rev_red);
310 cmsFreeToneCurve(rev_green);
311 cmsFreeToneCurve(rev_blue);
312 }
313
314 if(matrix)
315 memcpy(matrix, matrix_tmp, sizeof(dt_colormatrix_t));
316
317 return 0;
318}
319
320int dt_colorspaces_get_matrix_from_input_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg,
321 float *lutb, const int lutsize)
322{
323 return dt_colorspaces_get_matrix_from_profile(prof, matrix, lutr, lutg, lutb, lutsize, 1);
324}
325
326int dt_colorspaces_get_matrix_from_output_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg,
327 float *lutb, const int lutsize)
328{
329 return dt_colorspaces_get_matrix_from_profile(prof, matrix, lutr, lutg, lutb, lutsize, 0);
330}
331
333{
334 return cmsCreateLab4Profile(cmsD50_xyY());
335}
336
337static void _compute_prequantized_primaries(const cmsCIExyY* whitepoint,
338 const cmsCIExyYTRIPLE* primaries,
339 cmsCIEXYZTRIPLE *primaries_prequantized)
340{
341 cmsHPROFILE profile = cmsCreateRGBProfile(whitepoint, primaries, NULL);
342
343 cmsCIEXYZ *R = cmsReadTag(profile, cmsSigRedColorantTag);
344 cmsCIEXYZ *G = cmsReadTag(profile, cmsSigGreenColorantTag);
345 cmsCIEXYZ *B = cmsReadTag(profile, cmsSigBlueColorantTag);
346
347 primaries_prequantized->Red.X = (double)R->X;
348 primaries_prequantized->Red.Y = (double)R->Y;
349 primaries_prequantized->Red.Z = (double)R->Z;
350
351 primaries_prequantized->Green.X = (double)G->X;
352 primaries_prequantized->Green.Y = (double)G->Y;
353 primaries_prequantized->Green.Z = (double)G->Z;
354
355 primaries_prequantized->Blue.X = (double)B->X;
356 primaries_prequantized->Blue.Y = (double)B->Y;
357 primaries_prequantized->Blue.Z = (double)B->Z;
358
359 cmsCloseProfile(profile);
360}
361
362static cmsHPROFILE _create_lcms_profile(const char *desc, const char *dmdd,
363 const cmsCIExyY *whitepoint, const cmsCIExyYTRIPLE *primaries, cmsToneCurve *trc,
364 gboolean v2)
365{
366 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
367 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
368 cmsMLU *mlu3 = cmsMLUalloc(NULL, 1);
369 cmsMLU *mlu4 = cmsMLUalloc(NULL, 1);
370
371 cmsToneCurve *out_curves[3] = { trc, trc, trc };
372 cmsHPROFILE profile = cmsCreateRGBProfile(whitepoint, primaries, out_curves);
373
374 if(v2) cmsSetProfileVersion(profile, 2.4);
375
376 cmsSetHeaderFlags(profile, cmsEmbeddedProfileTrue);
377
378 cmsMLUsetASCII(mlu1, "en", "US", "Public Domain");
379 cmsWriteTag(profile, cmsSigCopyrightTag, mlu1);
380
381 cmsMLUsetASCII(mlu2, "en", "US", desc);
382 cmsWriteTag(profile, cmsSigProfileDescriptionTag, mlu2);
383
384 cmsMLUsetASCII(mlu3, "en", "US", dmdd);
385 cmsWriteTag(profile, cmsSigDeviceModelDescTag, mlu3);
386
387 cmsMLUsetASCII(mlu4, "en", "US", "darktable");
388 cmsWriteTag(profile, cmsSigDeviceMfgDescTag, mlu4);
389
390 cmsMLUfree(mlu1);
391 cmsMLUfree(mlu2);
392 cmsMLUfree(mlu3);
393 cmsMLUfree(mlu4);
394
395 return profile;
396}
397
398// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-F.pdf
399// Perceptual Quantization / SMPTE standard ST.2084
400static double _PQ_fct(double x)
401{
402 static const double M1 = 2610.0 / 16384.0;
403 static const double M2 = (2523.0 / 4096.0) * 128.0;
404 static const double C1 = 3424.0 / 4096.0;
405 static const double C2 = (2413.0 / 4096.0) * 32.0;
406 static const double C3 = (2392.0 / 4096.0) * 32.0;
407
408 if (x == 0.0) return 0.0;
409 const double sign = x;
410 x = fabs(x);
411
412 const double xpo = pow(x, 1.0 / M2);
413 const double num = MAX(xpo - C1, 0.0);
414 const double den = C2 - C3 * xpo;
415 const double res = pow(num / den, 1.0 / M1);
416
417 return copysign(res, sign);
418}
419
420// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-F.pdf
421// Hybrid Log-Gamma
422static double _HLG_fct(double x)
423{
424 static const double A = 0.17883277;
425 static const double B = 0.28466892;
426 static const double C = 0.55991073;
427
434 const double sign = x;
435 const double e = fabs(x);
436
437 if(e <= 0.5)
438 return copysign((e * e) / 3.0, sign);
439
440 return copysign((exp((e - C) / A) + B) / 12.0, sign);
441}
442
443static cmsToneCurve* _colorspaces_create_transfer(int32_t size, double (*fct)(double))
444{
445 float *values = g_malloc(sizeof(float) * size);
446
447 for (int32_t i = 0; i < size; ++i)
448 {
449 const double x = (float)i / (size - 1);
450 const double y = MIN(fct(x), 1.0f);
451 values[i] = (float)y;
452 }
453
454 cmsToneCurve* result = cmsBuildTabulatedToneCurveFloat(NULL, size, values);
455 dt_free(values);
456 return result;
457}
458
459static cmsHPROFILE _colorspaces_create_srgb_profile(gboolean v2)
460{
461 cmsFloat64Number srgb_parameters[5] = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
462 cmsToneCurve *transferFunction = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters);
463
464 cmsHPROFILE profile = _create_lcms_profile("sRGB", "sRGB",
465 &D65xyY, &sRGB_Primaries, transferFunction, v2);
466
467 cmsFreeToneCurve(transferFunction);
468
469 return profile;
470}
471
476
481
483{
484 cmsFloat64Number srgb_parameters[5] = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
485 cmsToneCurve *transferFunction = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters);
486
487 cmsCIExyYTRIPLE BRG_Primaries = { sRGB_Primaries.Blue, sRGB_Primaries.Red, sRGB_Primaries.Green };
488
489 cmsHPROFILE profile = _create_lcms_profile("BRG", "BRG",
490 &D65xyY, &BRG_Primaries, transferFunction, TRUE);
491
492 cmsFreeToneCurve(transferFunction);
493
494 return profile;
495}
496
498{
499 cmsFloat64Number srgb_parameters[5] = { 1/0.45, 1.0 / 1.099, 0.099 / 1.099, 1.0 / 4.5, 0.081 };
500 cmsToneCurve *transferFunction = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters);
501
502 cmsHPROFILE profile = _create_lcms_profile("Gamma Rec709 RGB", "Gamma Rec709 RGB",
503 &D65xyY, &Rec709_Primaries, transferFunction, TRUE);
504
505 cmsFreeToneCurve(transferFunction);
506
507 return profile;
508}
509
511{
512 // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1886-0-201103-I!!PDF-E.pdf
513 cmsToneCurve *transferFunction = cmsBuildGamma(NULL, 2.19921875);
514
515 cmsHPROFILE profile = _create_lcms_profile("ITU-R BT.1886 (gamma 2.4 Rec709)", "ITU-R BT.1886 (gamma 2.4 Rec709)",
516 &D65xyY, &Rec709_Primaries, transferFunction, TRUE);
517
518 cmsFreeToneCurve(transferFunction);
519
520 return profile;
521}
522
523
524// Create the ICC virtual profile for adobe rgb space
526{
527 // AdobeRGB's "2.2" gamma is technically defined as 2 + 51/256
528 cmsToneCurve *transferFunction = cmsBuildGamma(NULL, 2.19921875);
529
530 cmsHPROFILE profile = _create_lcms_profile("Adobe RGB (compatible)", "Adobe RGB",
531 &D65xyY, &Adobe_Primaries, transferFunction, TRUE);
532
533 cmsFreeToneCurve(transferFunction);
534
535 return profile;
536}
537
538
539cmsHPROFILE dt_colorspaces_create_alternate_profile(const char *makermodel)
540{
542 for(int k = 0; k < dt_alternate_colormatrix_cnt; k++)
543 {
544 if(!strcmp(makermodel, dt_alternate_colormatrices[k].makermodel))
545 {
547 break;
548 }
549 }
550 if(IS_NULL_PTR(preset)) return NULL;
551
552 const float wxyz = preset->white[0] + preset->white[1] + preset->white[2];
553 const float rxyz = preset->rXYZ[0] + preset->rXYZ[1] + preset->rXYZ[2];
554 const float gxyz = preset->gXYZ[0] + preset->gXYZ[1] + preset->gXYZ[2];
555 const float bxyz = preset->bXYZ[0] + preset->bXYZ[1] + preset->bXYZ[2];
556 cmsCIExyY WP = { preset->white[0] / wxyz, preset->white[1] / wxyz, 1.0 };
557 cmsCIExyYTRIPLE XYZPrimaries = { { preset->rXYZ[0] / rxyz, preset->rXYZ[1] / rxyz, 1.0 },
558 { preset->gXYZ[0] / gxyz, preset->gXYZ[1] / gxyz, 1.0 },
559 { preset->bXYZ[0] / bxyz, preset->bXYZ[1] / bxyz, 1.0 } };
560 cmsToneCurve *Gamma[3];
561 cmsHPROFILE hp;
562
563 Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildGamma(NULL, 1.0);
564
565 hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma);
566 cmsFreeToneCurve(Gamma[0]);
567 if(IS_NULL_PTR(hp)) return NULL;
568
569 char name[512];
570 snprintf(name, sizeof(name), "darktable alternate %s", makermodel);
571 cmsSetProfileVersion(hp, 2.1);
572 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
573 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
574 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
575 cmsMLUsetASCII(mlu1, "en", "US", name);
576 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
577 cmsMLUsetASCII(mlu2, "en", "US", name);
578 cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0);
579 cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1);
580 // this will only be displayed when the embedded profile is read by for example GIMP
581 cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2);
582 cmsMLUfree(mlu0);
583 cmsMLUfree(mlu1);
584 cmsMLUfree(mlu2);
585
586 return hp;
587}
588
589cmsHPROFILE dt_colorspaces_create_vendor_profile(const char *makermodel)
590{
592 for(int k = 0; k < dt_vendor_colormatrix_cnt; k++)
593 {
594 if(!strcmp(makermodel, dt_vendor_colormatrices[k].makermodel))
595 {
597 break;
598 }
599 }
600 if(IS_NULL_PTR(preset)) return NULL;
601
602 const float wxyz = preset->white[0] + preset->white[1] + preset->white[2];
603 const float rxyz = preset->rXYZ[0] + preset->rXYZ[1] + preset->rXYZ[2];
604 const float gxyz = preset->gXYZ[0] + preset->gXYZ[1] + preset->gXYZ[2];
605 const float bxyz = preset->bXYZ[0] + preset->bXYZ[1] + preset->bXYZ[2];
606 cmsCIExyY WP = { preset->white[0] / wxyz, preset->white[1] / wxyz, 1.0 };
607 cmsCIExyYTRIPLE XYZPrimaries = { { preset->rXYZ[0] / rxyz, preset->rXYZ[1] / rxyz, 1.0 },
608 { preset->gXYZ[0] / gxyz, preset->gXYZ[1] / gxyz, 1.0 },
609 { preset->bXYZ[0] / bxyz, preset->bXYZ[1] / bxyz, 1.0 } };
610 cmsToneCurve *Gamma[3];
611 cmsHPROFILE hp;
612
613 Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildGamma(NULL, 1.0);
614
615 hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma);
616 cmsFreeToneCurve(Gamma[0]);
617 if(IS_NULL_PTR(hp)) return NULL;
618
619 char name[512];
620 snprintf(name, sizeof(name), "darktable vendor %s", makermodel);
621 cmsSetProfileVersion(hp, 2.1);
622 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
623 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
624 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
625 cmsMLUsetASCII(mlu1, "en", "US", name);
626 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
627 cmsMLUsetASCII(mlu2, "en", "US", name);
628 cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0);
629 cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1);
630 // this will only be displayed when the embedded profile is read by for example GIMP
631 cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2);
632 cmsMLUfree(mlu0);
633 cmsMLUfree(mlu1);
634 cmsMLUfree(mlu2);
635
636 return hp;
637}
638
639cmsHPROFILE dt_colorspaces_create_darktable_profile(const char *makermodel)
640{
642 for(int k = 0; k < dt_profiled_colormatrix_cnt; k++)
643 {
644 if(!strcasecmp(makermodel, dt_profiled_colormatrices[k].makermodel))
645 {
647 break;
648 }
649 }
650 if(IS_NULL_PTR(preset)) return NULL;
651
652 const float wxyz = preset->white[0] + preset->white[1] + preset->white[2];
653 const float rxyz = preset->rXYZ[0] + preset->rXYZ[1] + preset->rXYZ[2];
654 const float gxyz = preset->gXYZ[0] + preset->gXYZ[1] + preset->gXYZ[2];
655 const float bxyz = preset->bXYZ[0] + preset->bXYZ[1] + preset->bXYZ[2];
656 cmsCIExyY WP = { preset->white[0] / wxyz, preset->white[1] / wxyz, 1.0 };
657 cmsCIExyYTRIPLE XYZPrimaries = { { preset->rXYZ[0] / rxyz, preset->rXYZ[1] / rxyz, 1.0 },
658 { preset->gXYZ[0] / gxyz, preset->gXYZ[1] / gxyz, 1.0 },
659 { preset->bXYZ[0] / bxyz, preset->bXYZ[1] / bxyz, 1.0 } };
660 cmsToneCurve *Gamma[3];
661 cmsHPROFILE hp;
662
663 Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildGamma(NULL, 1.0);
664
665 hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma);
666 cmsFreeToneCurve(Gamma[0]);
667 if(IS_NULL_PTR(hp)) return NULL;
668
669 char name[512];
670 snprintf(name, sizeof(name), "darktable profiled %s", makermodel);
671 cmsSetProfileVersion(hp, 2.1);
672 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
673 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
674 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
675 cmsMLUsetASCII(mlu1, "en", "US", name);
676 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
677 cmsMLUsetASCII(mlu2, "en", "US", name);
678 cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0);
679 cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1);
680 // this will only be displayed when the embedded profile is read by for example GIMP
681 cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2);
682 cmsMLUfree(mlu0);
683 cmsMLUfree(mlu1);
684 cmsMLUfree(mlu2);
685
686 return hp;
687}
688
689static cmsHPROFILE dt_colorspaces_create_xyz_profile(void)
690{
691 cmsHPROFILE hXYZ = cmsCreateXYZProfile();
692 cmsSetPCS(hXYZ, cmsSigXYZData);
693 cmsSetHeaderRenderingIntent(hXYZ, INTENT_PERCEPTUAL);
694
695 if(IS_NULL_PTR(hXYZ)) return NULL;
696
697 cmsSetProfileVersion(hXYZ, 2.1);
698 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
699 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
700 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
701 cmsMLUsetASCII(mlu1, "en", "US", "linear XYZ");
702 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
703 cmsMLUsetASCII(mlu2, "en", "US", "darktable linear XYZ");
704 cmsWriteTag(hXYZ, cmsSigDeviceMfgDescTag, mlu0);
705 cmsWriteTag(hXYZ, cmsSigDeviceModelDescTag, mlu1);
706 // this will only be displayed when the embedded profile is read by for example GIMP
707 cmsWriteTag(hXYZ, cmsSigProfileDescriptionTag, mlu2);
708 cmsMLUfree(mlu0);
709 cmsMLUfree(mlu1);
710 cmsMLUfree(mlu2);
711
712 return hXYZ;
713}
714
716{
717 cmsToneCurve *transferFunction = cmsBuildGamma(NULL, 1.0);
718
719 cmsHPROFILE profile = _create_lcms_profile("Linear Rec709 RGB", "Linear Rec709 RGB",
720 &D65xyY, &Rec709_Primaries, transferFunction, TRUE);
721
722 cmsFreeToneCurve(transferFunction);
723
724 return profile;
725}
726
728{
729 cmsToneCurve *transferFunction = cmsBuildGamma(NULL, 1.0);
730
731 cmsHPROFILE profile = _create_lcms_profile("Linear Rec2020 RGB", "Linear Rec2020 RGB",
732 &D65xyY, &Rec2020_Primaries, transferFunction, TRUE);
733
734 cmsFreeToneCurve(transferFunction);
735
736 return profile;
737}
738
740{
741 cmsToneCurve *transferFunction = _colorspaces_create_transfer(4096, _PQ_fct);
742
743 cmsHPROFILE profile = _create_lcms_profile("PQ Rec2020 RGB", "PQ Rec2020 RGB",
744 &D65xyY, &Rec2020_Primaries, transferFunction, TRUE);
745
746 cmsFreeToneCurve(transferFunction);
747
748 return profile;
749}
750
752{
753 cmsToneCurve *transferFunction = _colorspaces_create_transfer(4096, _HLG_fct);
754
755 cmsHPROFILE profile = _create_lcms_profile("HLG Rec2020 RGB", "HLG Rec2020 RGB",
756 &D65xyY, &Rec2020_Primaries, transferFunction, TRUE);
757
758 cmsFreeToneCurve(transferFunction);
759
760 return profile;
761}
762
764{
765 cmsToneCurve *transferFunction = _colorspaces_create_transfer(4096, _PQ_fct);
766
767 cmsHPROFILE profile = _create_lcms_profile("PQ P3 RGB", "PQ P3 RGB",
768 &D65xyY, &P3_Primaries, transferFunction, TRUE);
769
770 cmsFreeToneCurve(transferFunction);
771
772 return profile;
773}
774
776{
777 cmsToneCurve *transferFunction = _colorspaces_create_transfer(4096, _HLG_fct);
778
779 cmsHPROFILE profile = _create_lcms_profile("HLG P3 RGB", "HLG P3 RGB",
780 &D65xyY, &P3_Primaries, transferFunction, TRUE);
781
782 cmsFreeToneCurve(transferFunction);
783
784 return profile;
785}
786
788{
789 cmsFloat64Number srgb_parameters[5] = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
790 cmsToneCurve *transferFunction = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters);
791
792 cmsHPROFILE profile = _create_lcms_profile("Display P3 RGB", "Display P3 RGB",
793 &D65xyY, &P3_Primaries, transferFunction, TRUE);
794
795 cmsFreeToneCurve(transferFunction);
796
797 return profile;
798}
799
801{
802 cmsToneCurve *transferFunction = cmsBuildGamma(NULL, 1.0);
803
804 cmsHPROFILE profile = _create_lcms_profile("Linear ProPhoto RGB", "Linear ProPhoto RGB",
805 &D50xyY, &ProPhoto_Primaries, transferFunction, TRUE);
806
807 cmsFreeToneCurve(transferFunction);
808
809 return profile;
810}
811
813{
814 cmsToneCurve *transferFunction = cmsBuildGamma(NULL, 1.0);
815
816 // linear rgb with r and b swapped:
817 cmsCIExyYTRIPLE BGR_Primaries = { sRGB_Primaries.Blue, sRGB_Primaries.Green, sRGB_Primaries.Red };
818
819 cmsHPROFILE profile = _create_lcms_profile("Linear Infrared BGR", "darktable Linear Infrared BGR",
820 &D65xyY, &BGR_Primaries, transferFunction, FALSE);
821
822 cmsFreeToneCurve(transferFunction);
823
824 return profile;
825}
826
828{
829 // find the colorin module -- the pointer stays valid until darktable shuts down
830 static const dt_iop_module_so_t *colorin = NULL;
831 if(IS_NULL_PTR(colorin))
832 {
833 for(const GList *modules = darktable.iop; modules; modules = g_list_next(modules))
834 {
835 const dt_iop_module_so_t *module = (const dt_iop_module_so_t *)(modules->data);
836 if(!strcmp(module->op, "colorin"))
837 {
838 colorin = module;
839 break;
840 }
841 }
842 }
843
844 const dt_colorspaces_color_profile_t *p = NULL;
845
846 if(colorin && colorin->get_p)
847 {
848 // get the profile assigned from colorin
849 // FIXME: does this work when using JPEG thumbs and the image was never opened?
850 sqlite3_stmt *stmt;
851 // clang-format off
854 "SELECT op_params FROM main.history WHERE imgid=?1 AND operation='colorin' ORDER BY num DESC LIMIT 1", -1,
855 &stmt, NULL);
856 // clang-format on
857 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
858 if(sqlite3_step(stmt) == SQLITE_ROW)
859 {
860 // use introspection to get the profile name from the binary params blob
861 const void *params = sqlite3_column_blob(stmt, 0);
862 dt_colorspaces_color_profile_type_t *type = colorin->get_p(params, "type_work");
863 char *filename = colorin->get_p(params, "filename_work");
864
865 if(type && filename) p = dt_colorspaces_get_profile(*type, filename,
867 }
868 sqlite3_finalize(stmt);
869 }
870
871 // if all else fails -> fall back to linear Rec2020 RGB
873
874 return p;
875}
876
877dt_colorspaces_color_profile_type_t dt_image_find_best_color_profile(int32_t imgid, cmsHPROFILE *output, gboolean *new_profile)
878{
879 // Note : when the image has already been opened from cache on the current session,
880 // the embedded color profile is already inited and stored in img->profile.
881
882 // Untagged images should be assumed to be sRGB.
884 *new_profile = FALSE;
885
886 // Fetch filename for extension retrieval
887 char filename[PATH_MAX] = { 0 };
888 gboolean from_cache = TRUE;
889 dt_image_full_path(imgid, filename, sizeof(filename), &from_cache, __FUNCTION__);
890
891 const gchar *cc = filename + strlen(filename);
892 for(; *cc != '.' && cc > filename; cc--);
893 gchar *ext = g_ascii_strdown(cc + 1, -1);
894
895 // Fetch actual image
897 if(IS_NULL_PTR(img)) goto finish;
898
899 // Image codecs doing their own colorspace detection should set this to TRUE
900 gboolean already_set = FALSE;
901
902 dt_print(DT_DEBUG_COLORPROFILE, "Color profile type for %s: \n", filename);
903
904 if(img->profile && img->profile_size > 0
906 {
907 // Fast path : we already extracted ICC before. ICC profile is already inside.
908 color_profile = DT_COLORSPACE_EMBEDDED_ICC;
909 if(!IS_NULL_PTR(output))
910 {
912 *new_profile = TRUE;
913 }
914 dt_print(DT_DEBUG_COLORPROFILE, "Embedded ICC profile (inline)\n");
915 }
916 else if(!isnan(img->d65_color_matrix[0])
918 {
919 // DNG and others : matrix inside EXIF
920 color_profile = DT_COLORSPACE_EMBEDDED_MATRIX;
921 if(!IS_NULL_PTR(output))
922 {
924 *new_profile = TRUE;
925 }
926 dt_print(DT_DEBUG_COLORPROFILE, "Embedded EXIF matrix\n");
927 }
928 else if(dt_image_is_monochrome(img))
929 {
930 // Monochrome RAW - colorspace doesn't matter
931 color_profile = DT_COLORSPACE_LIN_REC709;
932 if(!IS_NULL_PTR(output))
934 dt_print(DT_DEBUG_COLORPROFILE, "Monochrome RAW\n");
935 }
937 {
938 // Color RAW
939 color_profile = DT_COLORSPACE_STANDARD_MATRIX;
940 if(!IS_NULL_PTR(output))
941 {
943 *new_profile = TRUE;
944 }
945 dt_print(DT_DEBUG_COLORPROFILE, "Typical RAW\n");
946 }
947 else if(img->flags & DT_IMAGE_4BAYER)
948 {
949 // 4Bayer images have been pre-converted to rec2020
950 color_profile = DT_COLORSPACE_LIN_REC2020;
951 if(!IS_NULL_PTR(output))
953 dt_print(DT_DEBUG_COLORPROFILE, "4Bayer RAW\n");
954 }
955 else if(img->colorspace == DT_IMAGE_COLORSPACE_SRGB)
956 {
957 // Images tagged explicitely with sRGB flag
958 color_profile = DT_COLORSPACE_SRGB;
959 dt_print(DT_DEBUG_COLORPROFILE, "Raster image tagged with sRGB\n");
960 }
962 {
963 // Images tagged explicitely with Adobe RGB flag
964 color_profile = DT_COLORSPACE_ADOBERGB;
965 if(!IS_NULL_PTR(output))
967 dt_print(DT_DEBUG_COLORPROFILE, "Raster image tagged with Adobe RGB\n");
968 }
969 else if(!strcmp(ext, "pfm"))
970 {
971 // PFM have no embedded color profile nor ICC tag, we can't know the color space
972 // but we can assume the are linear since it's a floating point format
973 color_profile = DT_COLORSPACE_LIN_REC709;
974 if(!IS_NULL_PTR(output))
976 dt_print(DT_DEBUG_COLORPROFILE, "PFM untagged image\n");
977 }
978 else
979 {
980 // Images that need codecs.
981
982 // First, extract embedded profiles from headers.
983 // Done only once : if everything goes well, the next time we access this image from cache,
984 // we will read img->profile directly (first branch here).
985
986 if(!strcmp(ext, "jpg") || !strcmp(ext, "jpeg"))
987 {
989 if(!dt_imageio_jpeg_read_header(filename, &jpg))
991 }
992#ifdef HAVE_OPENJPEG
993 else if(!strcmp(ext, "jp2") || !strcmp(ext, "j2k") || !strcmp(ext, "j2c") || !strcmp(ext, "jpc"))
994 {
995 img->profile_size = dt_imageio_j2k_read_profile(filename, &img->profile);
996 }
997#endif
998 else if((!strcmp(ext, "tif") || !strcmp(ext, "tiff")))
999 {
1000 img->profile_size = dt_imageio_tiff_read_profile(filename, &img->profile);
1001 }
1002 else if(!strcmp(ext, "png"))
1003 {
1004 img->profile_size = dt_imageio_png_read_profile(filename, &img->profile);
1005 }
1006#ifdef HAVE_LIBAVIF
1007 else if(!strcmp(ext, "avif"))
1008 {
1010 img->profile_size = dt_imageio_avif_read_profile(filename, &img->profile, &cicp);
1011
1012 // try the nclx box before falling back to any ICC profile
1013 color_profile = dt_colorspaces_cicp_to_type(&cicp, filename);
1014
1015 // If we found a basic RGB colorspace from private AVIF metadata,
1016 // bypass generic LCMS2 reading below
1017 if(color_profile != DT_COLORSPACE_NONE) already_set = TRUE;
1018 }
1019#endif
1020#ifdef HAVE_LIBHEIF
1021 else if(!strcmp(ext, "heif") || !strcmp(ext, "heic") || !strcmp(ext, "hif"))
1022 {
1024 img->profile_size = dt_imageio_heif_read_profile(filename, &img->profile, &cicp);
1025
1026 // try the nclx box before falling back to any ICC profile
1027 color_profile = dt_colorspaces_cicp_to_type(&cicp, filename);
1028
1029 // If we found a basic RGB colorspace from private AVIF metadata,
1030 // bypass generic LCMS2 reading below
1031 if(color_profile != DT_COLORSPACE_NONE) already_set = TRUE;
1032 }
1033#endif
1034
1035 // Finally, read the prepared embedded profile
1036 if(!already_set && img->profile && img->profile_size > 0
1038 {
1039 color_profile = DT_COLORSPACE_EMBEDDED_ICC;
1040 if(!IS_NULL_PTR(output))
1041 {
1043 *new_profile = TRUE;
1044 }
1045 dt_print(DT_DEBUG_COLORPROFILE, "Embedded ICC (extracted)\n");
1046 }
1047 else if(already_set && img->profile && img->profile_size > 0)
1048 {
1049 // This happens when AVIF/HEIF found a basic color profile into CICP fields
1050 if(!IS_NULL_PTR(output))
1051 *output = dt_colorspaces_get_profile(color_profile, "", DT_PROFILE_DIRECTION_IN)->profile;
1052 dt_print(DT_DEBUG_COLORPROFILE, "Embedded ICC (extracted)\n");
1053 }
1054 }
1055
1056 // Handle the fallback to sRGB space
1057 if(color_profile == DT_COLORSPACE_NONE) color_profile = DT_COLORSPACE_SRGB;
1058 if(color_profile == DT_COLORSPACE_SRGB && !IS_NULL_PTR(output))
1060
1061finish:
1063 dt_free(ext);
1064 return color_profile;
1065}
1066
1068 int32_t imgid,
1070 cmsHPROFILE *output,
1071 gboolean *new_profile)
1072{
1073 if(output) *output = NULL;
1074 if(new_profile) *new_profile = FALSE;
1075
1076 if(requested == DT_COLORSPACE_NONE)
1077 return dt_image_find_best_color_profile(imgid, output, new_profile);
1078
1079 if(requested != DT_COLORSPACE_EMBEDDED_ICC
1080 && requested != DT_COLORSPACE_EMBEDDED_MATRIX
1081 && requested != DT_COLORSPACE_STANDARD_MATRIX)
1082 return DT_COLORSPACE_NONE;
1083
1084 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1085 if(IS_NULL_PTR(img)) return DT_COLORSPACE_NONE;
1086
1088 {
1090 return dt_image_find_best_color_profile(imgid, output, new_profile);
1091 }
1092
1093 gboolean have_embedded_icc = (img->profile && img->profile_size > 0);
1095
1096 if(requested == DT_COLORSPACE_EMBEDDED_ICC && !have_embedded_icc)
1097 {
1098 // Try to extract embedded ICC into cache if needed.
1099 gboolean dummy_new_profile = FALSE;
1100 dt_image_find_best_color_profile(imgid, NULL, &dummy_new_profile);
1101 }
1102
1103 img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1104 if(IS_NULL_PTR(img)) return DT_COLORSPACE_NONE;
1105
1106 cmsHPROFILE profile = NULL;
1108
1110 {
1111 if(img->profile && img->profile_size > 0)
1112 {
1114 if(profile)
1115 {
1117 goto finish;
1118 }
1119 }
1121 }
1122
1124 {
1125 if(!isnan(img->d65_color_matrix[0]))
1126 {
1128 if(profile)
1129 {
1131 goto finish;
1132 }
1133 }
1135 }
1136
1138 {
1139 if(!isnan(img->adobe_XYZ_to_CAM[0][0]))
1140 {
1142 if(profile)
1143 {
1145 goto finish;
1146 }
1147 }
1148 }
1149
1151
1152finish:
1154
1155 if(profile)
1156 {
1157 if(output)
1158 {
1159 *output = profile;
1160 if(new_profile) *new_profile = TRUE;
1161 }
1162 else
1163 {
1165 }
1166 }
1167
1168 return type;
1169}
1170
1171
1172const cmsHPROFILE dt_colorspaces_get_embedded_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *type, gboolean *new_profile)
1173{
1174 cmsHPROFILE output;
1175 *type = dt_image_find_best_color_profile(imgid, &output, new_profile);
1176 return output;
1177}
1178
1179
1181{
1182 gboolean new_profile;
1183 cmsHPROFILE profile = dt_colorspaces_get_embedded_profile(imgid, type, &new_profile);
1184
1185 // create a dt profile object. -1 in all indices ensures it's hidden from GUI
1186 dt_colorspaces_color_profile_t *container = _create_profile(*type, profile, "", -1, -1, -1, -1, -1);
1187
1188 if(profile && container && new_profile)
1189 {
1190 // Set the name string for the profile
1191 char *lang = getenv("LANG");
1192 if(IS_NULL_PTR(lang)) lang = "en_US";
1193 dt_colorspaces_get_profile_name(profile, lang, lang + 3, container->name, sizeof(container->name));
1194
1195 // add it to the stack of dt profiles so it gets freed properly when we don't need it anymore
1197 }
1198
1200}
1201
1202
1205 const char *over_filename)
1206{
1207
1208 const dt_colorspaces_color_profile_t *p = NULL;
1209
1210 // Special case if output is undefined or uses private image color spaces : use the embedded profile if any.
1211 // We need to read it from the original image and create it on-the-fly.
1212 // Note: we don't allow export with deprecated vendor/enhanced/alternate matrices
1213 if(*over_type == DT_COLORSPACE_NONE ||
1214 *over_type == DT_COLORSPACE_EMBEDDED_ICC ||
1215 *over_type == DT_COLORSPACE_STANDARD_MATRIX ||
1216 *over_type == DT_COLORSPACE_EMBEDDED_MATRIX)
1217 {
1218 p = _build_embedded_profile(imgid, over_type);
1219 }
1220 else
1221 {
1222 // return a pointer to the profile specified in export.
1223 // we have that in here to get rid of the if() check in all places calling this function.
1225 }
1226
1227 // if all else fails -> fall back to sRGB
1228 if(IS_NULL_PTR(p))
1229 {
1231 *over_type = DT_COLORSPACE_SRGB;
1232 }
1233
1234 return p;
1235}
1236
1237#if 0
1238static void dt_colorspaces_create_cmatrix(float cmatrix[4][3], float mat[3][3])
1239{
1240 // sRGB D65, the linear part:
1241 static const dt_colormatrix_t rgb_to_xyz = { { 0.4124564f, 0.3575761f, 0.1804375f, 0.0f },
1242 { 0.2126729f, 0.7151522f, 0.0721750f, 0.0f },
1243 { 0.0193339f, 0.1191920f, 0.9503041f, 0.0f } };
1244
1245 for(int c = 0; c < 3; c++)
1246 {
1247 for(int j = 0; j < 3; j++)
1248 {
1249 mat[c][j] = 0.0f;
1250 for(int k = 0; k < 3; k++)
1251 {
1252 mat[c][j] += rgb_to_xyz[k][j] * cmatrix[c][k];
1253 }
1254 }
1255 }
1256}
1257#endif
1258
1259static cmsHPROFILE dt_colorspaces_create_xyzmatrix_profile(const float mat[3][3])
1260{
1261 // mat: cam -> xyz
1263 for(int k = 0; k < 3; k++)
1264 {
1265 const float norm = mat[0][k] + mat[1][k] + mat[2][k];
1266 x[k] = mat[0][k] / norm;
1267 y[k] = mat[1][k] / norm;
1268 }
1269 cmsCIExyYTRIPLE CameraPrimaries = { { x[0], y[0], 1.0 }, { x[1], y[1], 1.0 }, { x[2], y[2], 1.0 } };
1270 cmsHPROFILE profile;
1271
1272 cmsCIExyY D65;
1273 cmsXYZ2xyY(&D65, &d65);
1274
1275 cmsToneCurve *Gamma[3];
1276 Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildGamma(NULL, 1.0);
1277 profile = cmsCreateRGBProfile(&D65, &CameraPrimaries, Gamma);
1278 cmsFreeToneCurve(Gamma[0]);
1279 if(IS_NULL_PTR(profile)) return NULL;
1280
1281 cmsSetProfileVersion(profile, 2.1);
1282 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
1283 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
1284 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
1285 cmsMLUsetASCII(mlu1, "en", "US", "color matrix built-in");
1286 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
1287 cmsMLUsetASCII(mlu2, "en", "US", "color matrix built-in");
1288 cmsWriteTag(profile, cmsSigDeviceMfgDescTag, mlu0);
1289 cmsWriteTag(profile, cmsSigDeviceModelDescTag, mlu1);
1290 // this will only be displayed when the embedded profile is read by for example GIMP
1291 cmsWriteTag(profile, cmsSigProfileDescriptionTag, mlu2);
1292 cmsMLUfree(mlu0);
1293 cmsMLUfree(mlu1);
1294 cmsMLUfree(mlu2);
1295
1296 return profile;
1297}
1298
1300{
1301 // mat: xyz -> cam
1302 float imat[3][3];
1303 mat3inv((float *)imat, (float *)mat);
1305}
1306
1307static cmsHPROFILE _ensure_rgb_profile(cmsHPROFILE profile)
1308{
1309 if(profile && cmsGetColorSpace(profile) == cmsSigGrayData)
1310 {
1311 cmsToneCurve *trc = cmsReadTag(profile, cmsSigGrayTRCTag);
1312 cmsCIEXYZ *wtpt = cmsReadTag(profile, cmsSigMediaWhitePointTag);
1313 cmsCIEXYZ *bkpt = cmsReadTag(profile, cmsSigMediaBlackPointTag);
1314 cmsCIEXYZ *chad = cmsReadTag(profile, cmsSigChromaticAdaptationTag);
1315
1316 cmsMLU *cprt = cmsReadTag(profile, cmsSigCopyrightTag);
1317 cmsMLU *desc = cmsReadTag(profile, cmsSigProfileDescriptionTag);
1318 cmsMLU *dmnd = cmsReadTag(profile, cmsSigDeviceMfgDescTag);
1319 cmsMLU *dmdd = cmsReadTag(profile, cmsSigDeviceModelDescTag);
1320
1321 cmsHPROFILE rgb_profile = cmsCreateProfilePlaceholder(0);
1322
1323 cmsSetDeviceClass(rgb_profile, cmsSigDisplayClass);
1324 cmsSetColorSpace(rgb_profile, cmsSigRgbData);
1325 cmsSetPCS(rgb_profile, cmsSigXYZData);
1326
1327 cmsWriteTag(rgb_profile, cmsSigCopyrightTag, cprt);
1328 cmsWriteTag(rgb_profile, cmsSigProfileDescriptionTag, desc);
1329 cmsWriteTag(rgb_profile, cmsSigDeviceMfgDescTag, dmnd);
1330 cmsWriteTag(rgb_profile, cmsSigDeviceModelDescTag, dmdd);
1331
1332 cmsWriteTag(rgb_profile, cmsSigMediaBlackPointTag, bkpt);
1333 cmsWriteTag(rgb_profile, cmsSigMediaWhitePointTag, wtpt);
1334 cmsWriteTag(rgb_profile, cmsSigChromaticAdaptationTag, chad);
1335 cmsSetColorSpace(rgb_profile, cmsSigRgbData);
1336 cmsSetPCS(rgb_profile, cmsSigXYZData);
1337
1338 // TODO: we still use prequantized primaries here, we will probably want to rework this
1339 // part to create a profile using cmsCreateRGBProfile() as done in _create_lcms_profile().
1340 cmsWriteTag(rgb_profile, cmsSigRedColorantTag, (void *)&Rec709_Primaries_Prequantized.Red);
1341 cmsWriteTag(rgb_profile, cmsSigGreenColorantTag, (void *)&Rec709_Primaries_Prequantized.Green);
1342 cmsWriteTag(rgb_profile, cmsSigBlueColorantTag, (void *)&Rec709_Primaries_Prequantized.Blue);
1343
1344 cmsWriteTag(rgb_profile, cmsSigRedTRCTag, (void *)trc);
1345 cmsLinkTag(rgb_profile, cmsSigGreenTRCTag, cmsSigRedTRCTag);
1346 cmsLinkTag(rgb_profile, cmsSigBlueTRCTag, cmsSigRedTRCTag);
1347
1348 cmsCloseProfile(profile);
1349 profile = rgb_profile;
1350 }
1351
1352 return profile;
1353}
1354
1355cmsHPROFILE dt_colorspaces_get_rgb_profile_from_mem(uint8_t *data, uint32_t size)
1356{
1357 cmsHPROFILE profile = _ensure_rgb_profile(cmsOpenProfileFromMem(data, size));
1358
1359 return profile;
1360}
1361
1363{
1364 if(IS_NULL_PTR(p)) return;
1365 cmsCloseProfile(p);
1366}
1367
1368void dt_colorspaces_get_profile_name(cmsHPROFILE p, const char *language, const char *country, char *name,
1369 size_t len)
1370{
1371 cmsUInt32Number size;
1372 gchar *buf = NULL;
1373 wchar_t *wbuf = NULL;
1374 gchar *utf8 = NULL;
1375
1376 size = cmsGetProfileInfoASCII(p, cmsInfoDescription, language, country, NULL, 0);
1377 if(size == 0) goto error;
1378
1379 buf = (char *)calloc(size + 1, sizeof(char));
1380 size = cmsGetProfileInfoASCII(p, cmsInfoDescription, language, country, buf, size);
1381 if(size == 0) goto error;
1382
1383 // most unix like systems should work with this, but at least Windows doesn't
1384 if(sizeof(wchar_t) != 4 || g_utf8_validate(buf, -1, NULL))
1385 g_strlcpy(name, buf, len); // better a little weird than totally borked
1386 else
1387 {
1388 wbuf = (wchar_t *)calloc(size + 1, sizeof(wchar_t));
1389 size = cmsGetProfileInfo(p, cmsInfoDescription, language, country, wbuf, sizeof(wchar_t) * size);
1390 if(size == 0) goto error;
1391 utf8 = g_ucs4_to_utf8((gunichar *)wbuf, -1, NULL, NULL, NULL);
1392 if(IS_NULL_PTR(utf8)) goto error;
1393 g_strlcpy(name, utf8, len);
1394 }
1395
1396 dt_free(buf);
1397 dt_free(wbuf);
1398 dt_free(utf8);
1399 return;
1400
1401error:
1402 if(buf)
1403 g_strlcpy(name, buf, len); // better a little weird than totally borked
1404 else
1405 *name = '\0'; // nothing to do here
1406 dt_free(buf);
1407 dt_free(wbuf);
1408 dt_free(utf8);
1409}
1410
1411void rgb2hsl(const dt_aligned_pixel_t rgb, float *h, float *s, float *l)
1412{
1413 const float r = rgb[0], g = rgb[1], b = rgb[2];
1414 const float pmax = fmaxf(r, fmax(g, b));
1415 const float pmin = fminf(r, fmin(g, b));
1416 const float delta = (pmax - pmin);
1417
1418 float hv = 0, sv = 0, lv = (pmin + pmax) / 2.0;
1419
1420 if(delta != 0.0f)
1421 {
1422 sv = lv < 0.5 ? delta / fmaxf(pmax + pmin, 1.52587890625e-05f)
1423 : delta / fmaxf(2.0 - pmax - pmin, 1.52587890625e-05f);
1424
1425 if(pmax == r)
1426 hv = (g - b) / delta;
1427 else if(pmax == g)
1428 hv = 2.0 + (b - r) / delta;
1429 else if(pmax == b)
1430 hv = 4.0 + (r - g) / delta;
1431 hv /= 6.0;
1432 if(hv < 0.0)
1433 hv += 1.0;
1434 else if(hv > 1.0)
1435 hv -= 1.0;
1436 }
1437 *h = hv;
1438 *s = sv;
1439 *l = lv;
1440}
1441
1442// for efficiency, 'hue' must be pre-scaled to be in 0..6
1443static inline __attribute__((always_inline)) float hue2rgb(float m1, float m2, float hue)
1444{
1445 // compute the value for one of the RGB channels from the hue angle.
1446 // If 1 <= angle < 3, return m2; if 4 <= angle <= 6, return m1; otherwise, linearly interpolate between m1 and m2.
1447 if(hue < 1.0f)
1448 return (m1 + (m2 - m1) * hue);
1449 else if(hue < 3.0f)
1450 return m2;
1451 else
1452 return hue < 4.0f ? (m1 + (m2 - m1) * (4.0f - hue)) : m1;
1453}
1454
1455void hsl2rgb(dt_aligned_pixel_t rgb, float h, float s, float l)
1456{
1457 float m1, m2;
1458 if(s == 0)
1459 {
1460 rgb[0] = rgb[1] = rgb[2] = l;
1461 return;
1462 }
1463 m2 = l < 0.5 ? l * (1.0 + s) : l + s - l * s;
1464 m1 = (2.0 * l - m2);
1465 h *= 6.0f; // pre-scale hue angle
1466 rgb[0] = hue2rgb(m1, m2, h < 4.0f ? h + 2.0f : h - 4.0f);
1467 rgb[1] = hue2rgb(m1, m2, h);
1468 rgb[2] = hue2rgb(m1, m2, h > 2.0f ? h - 2.0f : h + 4.0f);
1469}
1470
1472 cmsHPROFILE profile, const char *name, int in_pos,
1473 int out_pos, int display_pos, int category_pos,
1474 int work_pos)
1475{
1478 prof->type = type;
1479 g_strlcpy(prof->name, name, sizeof(prof->name));
1480 prof->profile = profile;
1481 prof->in_pos = in_pos;
1482 prof->out_pos = out_pos;
1483 prof->display_pos = display_pos;
1484 prof->category_pos = category_pos;
1485 prof->work_pos = work_pos;
1486 return prof;
1487}
1488
1489// this function is basically thread safe, at least when not called on the global darktable.color_profiles
1491{
1492 if(self->transform_srgb_to_display) cmsDeleteTransform(self->transform_srgb_to_display);
1493 self->transform_srgb_to_display = NULL;
1494
1495 if(self->transform_adobe_rgb_to_display) cmsDeleteTransform(self->transform_adobe_rgb_to_display);
1496 self->transform_adobe_rgb_to_display = NULL;
1497
1498 if(self->transform_xyz_to_display) cmsDeleteTransform(self->transform_xyz_to_display);
1499 self->transform_xyz_to_display = NULL;
1500
1501 if(self->transform_display_to_adobe_rgb) cmsDeleteTransform(self->transform_display_to_adobe_rgb);
1502 self->transform_display_to_adobe_rgb = NULL;
1503
1504 const dt_colorspaces_color_profile_t *display_dt_profile = _get_profile(self, self->display_type,
1505 self->display_filename,
1507 if(IS_NULL_PTR(display_dt_profile)) return;
1508 cmsHPROFILE display_profile = display_dt_profile->profile;
1509 if(IS_NULL_PTR(display_profile)) return;
1510
1511 self->transform_srgb_to_display = cmsCreateTransform(_get_profile(self, DT_COLORSPACE_SRGB, "",
1513 TYPE_RGBA_8,
1514 display_profile,
1515 TYPE_BGRA_8,
1516 self->display_intent,
1517 0);
1518
1519 self->transform_xyz_to_display = cmsCreateTransform(_get_profile(self, DT_COLORSPACE_XYZ, "",
1520 DT_PROFILE_DIRECTION_IN)->profile,
1522 display_profile,
1523 TYPE_RGBA_FLT,
1524 self->display_intent,
1525 0);
1526
1527 self->transform_adobe_rgb_to_display = cmsCreateTransform(_get_profile(self, DT_COLORSPACE_ADOBERGB, "",
1529 TYPE_RGBA_8,
1530 display_profile,
1531 TYPE_BGRA_8,
1532 self->display_intent,
1533 0);
1534
1535 self->transform_display_to_adobe_rgb = cmsCreateTransform(display_profile,
1536 TYPE_BGRA_8,
1539 TYPE_RGBA_8,
1540 self->display_intent,
1541 0);
1542}
1543
1544// update cached transforms for color management of thumbnails
1545// make sure that darktable.color_profiles->xprofile_lock is held when calling this!
1550
1551void dt_colorspaces_transform_rgba_float_row(const cmsHTRANSFORM transform, const float *in, float *out,
1552 const int width)
1553{
1554 cmsDoTransform(transform, in, out, width);
1555}
1556
1558void dt_colorspaces_transform_rgba_float_image(const cmsHTRANSFORM transform, const float *image_in, float *image_out,
1559 const int width, const int height)
1560{
1561 if(IS_NULL_PTR(transform) || IS_NULL_PTR(image_in) || IS_NULL_PTR(image_out) || width <= 0 || height <= 0) return;
1562
1563 /* Share the aliased LCMS transform explicitly. Do not read it indirectly
1564 * through module state inside the loop body. */
1566 for(int y = 0; y < height; y++)
1567 {
1568 const float *const in = image_in + (size_t)y * width * 4;
1569 float *const out = image_out + (size_t)y * width * 4;
1571 }
1572}
1573
1574void dt_colorspaces_transform_rgba8_to_bgra8(const cmsHTRANSFORM transform, const uint8_t *image_in, uint8_t *image_out,
1575 const int width, const int height)
1576{
1577 if(IS_NULL_PTR(image_in) || IS_NULL_PTR(image_out) || width <= 0 || height <= 0) return;
1578
1579 /* Same threading rule as float transforms: pass an aliased transform handle
1580 * into the helper and share only that stable local state. */
1582 for(int y = 0; y < height; y++)
1583 {
1584 const uint8_t *const restrict in = image_in + (size_t)y * width * 4u;
1585 uint8_t *const restrict out = image_out + (size_t)y * width * 4u;
1586
1587 if(transform)
1588 {
1589 cmsDoTransform(transform, in, out, width);
1590 for(int x = 0; x < width; x++) out[4 * x + 3] = UINT8_MAX;
1591 }
1592 else
1593 {
1594 for(int x = 0; x < width; x++)
1595 {
1596 out[4 * x + 0] = in[4 * x + 2];
1597 out[4 * x + 1] = in[4 * x + 1];
1598 out[4 * x + 2] = in[4 * x + 0];
1599 out[4 * x + 3] = UINT8_MAX;
1600 }
1601 }
1602 }
1603}
1604
1605// make sure that darktable.color_profiles->xprofile_lock is held when calling this!
1606static void _update_display_profile(guchar *tmp_data, gsize size, char *name, size_t name_size)
1607{
1611
1612 cmsHPROFILE profile = cmsOpenProfileFromMem(tmp_data, size);
1613 if(profile)
1614 {
1615 for(GList *iter = darktable.color_profiles->profiles; iter; iter = g_list_next(iter))
1616 {
1618 if(p->type == DT_COLORSPACE_DISPLAY)
1619 {
1620 if(p->profile) dt_colorspaces_cleanup_profile(p->profile);
1621 p->profile = profile;
1622 if(name)
1623 dt_colorspaces_get_profile_name(profile, "en", "US", name, name_size);
1624
1625 // update cached transforms for color management of thumbnails
1627
1628 break;
1629 }
1630 }
1631 }
1632}
1633
1634static void cms_error_handler(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *text)
1635{
1636 dt_print(DT_DEBUG_COLORPROFILE, "[lcms2] error %d: %s\n", ErrorCode, text);
1637}
1638
1639static gint _sort_profiles(gconstpointer a, gconstpointer b)
1640{
1643
1644 gchar *name_a = g_utf8_casefold(profile_a->name, -1);
1645 gchar *name_b = g_utf8_casefold(profile_b->name, -1);
1646
1647 gint result = g_strcmp0(name_a, name_b);
1648
1649 dt_free(name_a);
1650 dt_free(name_b);
1651
1652 return result;
1653}
1654
1655static GList *load_profile_from_dir(const char *subdir)
1656{
1657 GList *temp_profiles = NULL;
1658 const gchar *d_name;
1659 char datadir[PATH_MAX] = { 0 };
1660 char confdir[PATH_MAX] = { 0 };
1661 dt_loc_get_user_config_dir(confdir, sizeof(confdir));
1662 dt_loc_get_datadir(datadir, sizeof(datadir));
1663 char *lang = getenv("LANG");
1664 if(IS_NULL_PTR(lang)) lang = "en_US";
1665
1666 char *dirname = g_build_filename(confdir, "color", subdir, NULL);
1667 if(!g_file_test(dirname, G_FILE_TEST_IS_DIR))
1668 {
1669 dt_free(dirname);
1670 dirname = g_build_filename(datadir, "color", subdir, NULL);
1671 }
1672 GDir *dir = g_dir_open(dirname, 0, NULL);
1673 if(dir)
1674 {
1675 while((d_name = g_dir_read_name(dir)))
1676 {
1677 char *filename = g_build_filename(dirname, d_name, NULL);
1678 const char *cc = filename + strlen(filename);
1679 for(; *cc != '.' && cc > filename; cc--)
1680 ;
1681 if(!g_ascii_strcasecmp(cc, ".icc") || !g_ascii_strcasecmp(cc, ".icm"))
1682 {
1683 size_t end;
1684 char *icc_content = dt_read_file(filename, &end);
1685 if(IS_NULL_PTR(icc_content)) goto icc_loading_done;
1686
1687 // TODO: add support for grayscale profiles, then remove _ensure_rgb_profile() from here
1688 cmsHPROFILE tmpprof = _ensure_rgb_profile(cmsOpenProfileFromMem(icc_content, sizeof(char) * end));
1689 if(tmpprof)
1690 {
1692 dt_colorspaces_get_profile_name(tmpprof, lang, lang + 3, prof->name, sizeof(prof->name));
1693 if(prof->name[0] == '\0')
1694 g_strlcpy(prof->name, _("(unknown name)"), sizeof(prof->name));
1695
1696 g_strlcpy(prof->filename, filename, sizeof(prof->filename));
1697 prof->type = DT_COLORSPACE_FILE;
1698 prof->profile = tmpprof;
1699 // these will be set after sorting!
1700 prof->in_pos = -1;
1701 prof->out_pos = -1;
1702 prof->display_pos = -1;
1703 prof->category_pos = -1;
1704 prof->work_pos = -1;
1705 temp_profiles = g_list_prepend(temp_profiles, prof);
1706 }
1707
1708icc_loading_done:
1709 dt_free(icc_content);
1710 }
1711 dt_free(filename);
1712 }
1713 g_dir_close(dir);
1714 temp_profiles = g_list_sort(temp_profiles, _sort_profiles);
1715 }
1716 dt_free(dirname);
1717 return temp_profiles;
1718}
1719
1721{
1722 cmsSetLogErrorHandler(cms_error_handler);
1723
1724 dt_colorspaces_t *res = (dt_colorspaces_t *)calloc(1, sizeof(dt_colorspaces_t));
1725
1727
1728 pthread_rwlock_init(&res->xprofile_lock, NULL);
1729
1730 int in_pos = -1,
1731 out_pos = -1,
1732 display_pos = -1,
1733 category_pos = -1,
1734 work_pos = -1;
1735
1736 // init the category profile with NULL profile, the actual profile must be retrieved dynamically by the caller
1737 res->profiles = g_list_append(res->profiles, _create_profile(DT_COLORSPACE_WORK, NULL, _("work profile"), -1, -1,
1738 -1, ++category_pos, -1));
1739
1740 res->profiles = g_list_append(res->profiles, _create_profile(DT_COLORSPACE_EXPORT, NULL, _("export profile"), -1,
1741 -1, -1, ++category_pos, -1));
1742
1743 res->profiles
1744 = g_list_append(res->profiles, _create_profile(DT_COLORSPACE_SOFTPROOF, NULL, _("softproof profile"), -1, -1,
1745 -1, ++category_pos, -1));
1746
1747 // init the display profile with srgb so some stupid code that runs before the real profile could be fetched has something to work with
1748 res->profiles = g_list_append(
1750 _("System display profile (recommended)"), -1, -1, ++display_pos, ++category_pos, -1));
1751
1752 // we want a v4 with parametric curve for input and a v2 with point trc for output
1753 // see http://ninedegreesbelow.com/photography/lcms-make-icc-profiles.html#profile-variants-and-versions
1754 // TODO: what about display?
1755 res->profiles
1757 _("sRGB (e.g. JPG)"), ++in_pos, -1, -1, -1, -1));
1758
1759 res->profiles
1761 _("sRGB"), -1, ++out_pos, ++display_pos,
1762 ++category_pos, ++work_pos));
1763
1764 res->profiles = g_list_append(res->profiles,
1766 _("Adobe RGB (compatible)"), ++in_pos, ++out_pos, ++display_pos,
1767 ++category_pos, ++work_pos));
1768
1769 res->profiles = g_list_append(
1771 _("linear Rec709 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1772 ++work_pos));
1773
1775 _("gamma Rec709 RGB"), ++in_pos, ++out_pos, -1, -1,
1776 ++work_pos));
1777
1779 _("ITU-R BT.1886 (gamma 2.4 Rec709)"), ++in_pos, ++out_pos, -1, -1,
1780 ++work_pos));
1781
1782 res->profiles = g_list_append(
1784 _("linear Rec2020 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1785 ++work_pos));
1786
1787 res->profiles = g_list_append(
1789 _("PQ Rec2020 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1790 ++work_pos));
1791
1792 res->profiles = g_list_append(
1794 _("HLG Rec2020 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1795 ++work_pos));
1796
1797 res->profiles = g_list_append(
1799 _("PQ P3 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1800 ++work_pos));
1801
1802 res->profiles = g_list_append(
1804 _("HLG P3 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1805 ++work_pos));
1806
1807 res->profiles = g_list_append(
1809 _("Display P3 RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1810 ++work_pos));
1811
1812 res->profiles = g_list_append(
1814 _("linear ProPhoto RGB"), ++in_pos, ++out_pos, ++display_pos, ++category_pos,
1815 ++work_pos));
1816
1817 res->profiles = g_list_append(
1818 res->profiles,
1820 dt_conf_get_bool("allow_lab_output") ? ++out_pos : -1, -1, -1, -1));
1821
1822 res->profiles = g_list_append(
1824 dt_conf_get_bool("allow_lab_output") ? ++out_pos : -1, -1, -1, -1));
1825
1826 res->profiles = g_list_append(
1828 _("linear infrared BGR"), ++in_pos, -1, -1, -1, -1));
1829
1830 res->profiles
1832 _("BRG (for testing)"), ++in_pos, ++out_pos, ++display_pos,
1833 -1, -1));
1834
1835 // init display profile and softproof/gama checking from conf
1836 res->display_type = dt_conf_get_int("ui_last/color/display_type");
1837 res->softproof_type = dt_conf_get_int("ui_last/color/softproof_type");
1838 const char *tmp = dt_conf_get_string_const("ui_last/color/display_filename");
1839 g_strlcpy(res->display_filename, tmp, sizeof(res->display_filename));
1840 tmp = dt_conf_get_string_const("ui_last/color/softproof_filename");
1841 g_strlcpy(res->softproof_filename, tmp, sizeof(res->softproof_filename));
1842 res->display_intent = dt_conf_get_int("ui_last/color/display_intent");
1843 res->softproof_intent = dt_conf_get_int("ui_last/color/softproof_intent");
1844 res->mode = dt_conf_get_int("ui_last/color/mode");
1845
1846 // sanity checks to ensure the profile filenames are present
1847
1848 if((unsigned int)res->display_type >= DT_COLORSPACE_LAST
1850 && (!res->display_filename[0] || !g_file_test(res->display_filename, G_FILE_TEST_IS_REGULAR))))
1852
1853 if((unsigned int)res->softproof_type >= DT_COLORSPACE_LAST
1855 && (!res->softproof_filename[0] || !g_file_test(res->softproof_filename, G_FILE_TEST_IS_REGULAR))))
1857
1858 // temporary list of profiles to be added, we keep this separate to be able to sort it before adding
1859 GList *temp_profiles;
1860
1861 // read {userconfig,datadir}/color/in/*.icc, in this order.
1862 temp_profiles = load_profile_from_dir("in");
1863 for(GList *iter = temp_profiles; iter; iter = g_list_next(iter))
1864 {
1866 prof->in_pos = ++in_pos;
1867 }
1868 res->profiles = g_list_concat(res->profiles, temp_profiles);
1869
1870 // read {conf,data}dir/color/out/*.icc
1871 temp_profiles = load_profile_from_dir("out");
1872 for(GList *iter = temp_profiles; iter; iter = g_list_next(iter))
1873 {
1875 // FIXME: do want to filter out non-RGB profiles for cases besides histogram profile? colorin is OK with RGB or XYZ, print is OK with anything which LCMS likes, otherwise things are more choosey
1876 const cmsColorSpaceSignature color_space = cmsGetColorSpace(prof->profile);
1877 // The histogram profile is used for histogram, clipping indicators and the global color picker.
1878 // Some of these also assume a matrix profile. LUT profiles don't make much sense in these applications
1879 // so filter out any profile that doesn't implement the relative colorimetric intent as a matrix (+ TRC).
1880 // For discussion, see e.g.
1881 // https://github.com/darktable-org/darktable/issues/7660#issuecomment-760143437
1882 // For the working profile we also require a matrix profile.
1883 const gboolean is_valid_matrix_profile
1884 = dt_colorspaces_get_matrix_from_output_profile(prof->profile, NULL, NULL, NULL, NULL, 0) == 0
1885 && dt_colorspaces_get_matrix_from_input_profile(prof->profile, NULL, NULL, NULL, NULL, 0) == 0;
1886 prof->out_pos = ++out_pos;
1887 prof->display_pos = ++display_pos;
1888 if(is_valid_matrix_profile)
1889 {
1890 prof->category_pos = ++category_pos;
1891 prof->work_pos = ++work_pos;
1892 }
1893 else
1894 {
1896 "output profile `%s' color space `%c%c%c%c' not supported for work or histogram profile\n",
1897 prof->name, (char)(color_space >> 24), (char)(color_space >> 16), (char)(color_space >> 8),
1898 (char)(color_space));
1899 }
1900 }
1901 res->profiles = g_list_concat(res->profiles, temp_profiles);
1902
1903
1904 if((unsigned int)res->mode > DT_PROFILE_GAMUTCHECK) res->mode = DT_PROFILE_NORMAL;
1905
1907
1908 return res;
1909}
1910
1912{
1913 // remember display profile and softproof/gama checking from conf
1914 dt_conf_set_int("ui_last/color/display_type", self->display_type);
1915 dt_conf_set_int("ui_last/color/softproof_type", self->softproof_type);
1916 dt_conf_set_string("ui_last/color/display_filename", self->display_filename);
1917 dt_conf_set_string("ui_last/color/softproof_filename", self->softproof_filename);
1918 dt_conf_set_int("ui_last/color/display_intent", self->display_intent);
1919 dt_conf_set_int("ui_last/color/softproof_intent", self->softproof_intent);
1920 dt_conf_set_int("ui_last/color/mode", self->mode);
1921
1922 if(self->transform_srgb_to_display) cmsDeleteTransform(self->transform_srgb_to_display);
1923 self->transform_srgb_to_display = NULL;
1924
1925 if(self->transform_adobe_rgb_to_display) cmsDeleteTransform(self->transform_adobe_rgb_to_display);
1926 self->transform_adobe_rgb_to_display = NULL;
1927
1928 if(self->transform_display_to_adobe_rgb) cmsDeleteTransform(self->transform_display_to_adobe_rgb);
1929 self->transform_display_to_adobe_rgb = NULL;
1930
1931 if(self->transform_xyz_to_display) cmsDeleteTransform(self->transform_xyz_to_display);
1932 self->transform_xyz_to_display = NULL;
1933
1934 for(GList *iter = self->profiles; iter; iter = g_list_next(iter))
1935 {
1937 if(p) dt_colorspaces_cleanup_profile(p->profile);
1938 }
1939 g_list_free_full(self->profiles, dt_free_gpointer);
1940 self->profiles = NULL;
1941
1942 pthread_rwlock_destroy(&self->xprofile_lock);
1944 dt_free(self->xprofile_data);
1945
1946 dt_free(self);
1947}
1948
1950 const char *filename)
1951{
1952 switch (type)
1953 {
1954 case DT_COLORSPACE_NONE:
1955 return NULL;
1956 case DT_COLORSPACE_FILE:
1957 return filename;
1958 case DT_COLORSPACE_SRGB:
1959 return _("sRGB");
1961 return _("Adobe RGB (compatible)");
1963 return _("linear Rec709 RGB");
1965 return _("linear Rec2020 RGB");
1966 case DT_COLORSPACE_XYZ:
1967 return _("linear XYZ");
1968 case DT_COLORSPACE_LAB:
1969 return _("Lab");
1971 return _("linear infrared BGR");
1973 return _("System display profile (recommended)");
1975 return _("embedded ICC profile");
1977 return _("embedded matrix");
1979 return _("standard color matrix");
1981 return _("enhanced color matrix");
1983 return _("vendor color matrix");
1985 return _("alternate color matrix");
1986 case DT_COLORSPACE_BRG:
1987 return _("BRG (experimental)");
1989 return _("export profile");
1991 return _("softproof profile");
1992 case DT_COLORSPACE_WORK:
1993 return _("work profile");
1995 return _("Not used. Shouldn't be here.");
1997 return _("Rec709 RGB");
1999 return _("linear ProPhoto RGB");
2001 return _("PQ Rec2020");
2003 return _("HLG Rec2020");
2005 return _("PQ P3");
2007 return _("HLG P3");
2009 return _("Display P3");
2011 return _("ITU-R BT.1886");
2012 case DT_COLORSPACE_LAST:
2013 break;
2014 }
2015
2016 return NULL;
2017}
2018
2019#ifdef USE_COLORDGTK
2020static void dt_colorspaces_get_display_profile_colord_callback(GObject *source, GAsyncResult *res, gpointer user_data)
2021{
2022 pthread_rwlock_wrlock(&darktable.color_profiles->xprofile_lock);
2023
2024 int profile_changed = 0;
2025 CdWindow *window = CD_WINDOW(source);
2026 GError *error = NULL;
2027 CdProfile *profile = cd_window_get_profile_finish(window, res, &error);
2028 if(IS_NULL_PTR(error) && !IS_NULL_PTR(profile))
2029 {
2030 const gchar *filename = cd_profile_get_filename(profile);
2031 if(filename)
2032 {
2033 if(g_strcmp0(filename, darktable.color_profiles->colord_profile_file))
2034 {
2035 /* the profile has changed (either because the user changed the colord settings or because we are on a
2036 * different screen now) */
2037 // update darktable.color_profiles->colord_profile_file
2039 darktable.color_profiles->colord_profile_file = g_strdup(filename);
2040
2041 // read the file
2042 guchar *tmp_data = NULL;
2043 gsize size;
2044 g_file_get_contents(filename, (gchar **)&tmp_data, &size, NULL);
2046 || memcmp(darktable.color_profiles->xprofile_data, tmp_data, size) != 0);
2047
2048 if(profile_changed)
2049 {
2050 _update_display_profile(tmp_data, size, NULL, 0);
2052 "[color profile] colord gave us a new screen profile: '%s' (size: %" G_GSIZE_FORMAT ")\n", filename, size);
2053 }
2054 else
2055 {
2056 dt_free(tmp_data);
2057 }
2058 }
2059 }
2060 }
2061 if(profile) g_object_unref(profile);
2062 g_object_unref(window);
2063
2064 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
2065
2067}
2068#endif
2069
2070#if defined GDK_WINDOWING_X11
2071static int _gtk_get_monitor_num(GdkMonitor *monitor)
2072{
2073 GdkDisplay *display;
2074 int n_monitors, i;
2075
2076 display = gdk_monitor_get_display(monitor);
2077 n_monitors = gdk_display_get_n_monitors(display);
2078 for(i = 0; i < n_monitors; i++)
2079 {
2080 if(gdk_display_get_monitor(display, i) == monitor) return i;
2081 }
2082
2083 return -1;
2084}
2085#endif
2086
2087// Get the display ICC profile of the monitor associated with the widget.
2088// For X display, uses the ICC profile specifications version 0.2 from
2089// http://burtonini.com/blog/computers/xicc
2090// Based on code from Gimp's modules/cdisplay_lcms.c
2092{
2093 if(!dt_control_running()) return;
2094 // make sure that no one gets a broken profile
2095 // FIXME: benchmark if the try is really needed when moving/resizing the window. Maybe we can just lock it
2096 // and block
2097 if(pthread_rwlock_trywrlock(&darktable.color_profiles->xprofile_lock))
2098 return; // we are already updating the profile. Or someone is reading right now. Too bad we can't
2099 // distinguish that. Whatever ...
2100
2101 guint8 *buffer = NULL;
2102 gint buffer_size = 0;
2103 gchar *profile_source = NULL;
2104
2105#if defined GDK_WINDOWING_X11
2106
2107 // we will use the xatom no matter what configured when compiled without colord
2108 gboolean use_xatom = TRUE;
2109#if defined USE_COLORDGTK
2110 gboolean use_colord = TRUE;
2111 const char *display_profile_source = dt_conf_get_string_const("ui_last/display_profile_source");
2112
2113 if(display_profile_source)
2114 {
2115 if(!strcmp(display_profile_source, "xatom"))
2116 use_colord = FALSE;
2117 else if(!strcmp(display_profile_source, "colord"))
2118 use_xatom = FALSE;
2119 }
2120#endif
2121
2122 /* let's have a look at the xatom, just in case ... */
2123 if(use_xatom)
2124 {
2126 GdkWindow *window = gtk_widget_get_window(widget);
2127 GdkScreen *screen = gtk_widget_get_screen(widget);
2128 if(IS_NULL_PTR(screen)) screen = gdk_screen_get_default();
2129
2130 GdkDisplay *display = gtk_widget_get_display(widget);
2131 int monitor = _gtk_get_monitor_num(gdk_display_get_monitor_at_window(display, window));
2132
2133 char *atom_name;
2134 if(monitor > 0)
2135 atom_name = g_strdup_printf("_ICC_PROFILE_%d", monitor);
2136 else
2137 atom_name = g_strdup("_ICC_PROFILE");
2138
2139 profile_source = g_strdup_printf("xatom %s", atom_name);
2140
2141 GdkAtom type = GDK_NONE;
2142 gint format = 0;
2143 gdk_property_get(gdk_screen_get_root_window(screen), gdk_atom_intern(atom_name, FALSE), GDK_NONE, 0,
2144 64 * 1024 * 1024, FALSE, &type, &format, &buffer_size, &buffer);
2145 dt_free(atom_name);
2146 }
2147
2148#ifdef USE_COLORDGTK
2149 /* also try to get the profile from colord. this will set the value asynchronously! */
2150 if(use_colord)
2151 {
2152 CdWindow *window = cd_window_new();
2153 GtkWidget *center_widget = dt_ui_center(darktable.gui->ui);
2154 cd_window_get_profile(window, center_widget, NULL, dt_colorspaces_get_display_profile_colord_callback,
2155 GINT_TO_POINTER(profile_type));
2156 }
2157#endif
2158
2159#elif defined GDK_WINDOWING_QUARTZ
2160#if 0
2161 GtkWidget *widget = (profile_type == DT_COLORSPACE_DISPLAY2) ? darktable.develop->second_window.second_wnd : dt_ui_center(darktable.gui->ui);
2162 GdkScreen *screen = gtk_widget_get_screen(widget);
2163 if(IS_NULL_PTR(screen)) screen = gdk_screen_get_default();
2164 int monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(widget));
2165
2166 CGDirectDisplayID ids[monitor + 1];
2167 uint32_t total_ids;
2168 CMProfileRef prof = NULL;
2169 if(CGGetOnlineDisplayList(monitor + 1, &ids[0], &total_ids) == kCGErrorSuccess && total_ids == monitor + 1)
2170 CMGetProfileByAVID(ids[monitor], &prof);
2171 if(!IS_NULL_PTR(prof))
2172 {
2173 CFDataRef data;
2174 data = CMProfileCopyICCData(NULL, prof);
2175 CMCloseProfile(prof);
2176
2177 UInt8 *tmp_buffer = (UInt8 *)g_malloc(CFDataGetLength(data));
2178 CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), tmp_buffer);
2179
2180 buffer = (guint8 *)tmp_buffer;
2181 buffer_size = CFDataGetLength(data);
2182
2183 CFRelease(data);
2184 }
2185 profile_source = g_strdup("osx color profile api");
2186#endif
2187#elif defined G_OS_WIN32
2188 //HDC hdc = GetDC(NULL);
2190 GdkWindow *window = gtk_widget_get_window(widget);
2191 HWND hwnd = (HWND)gdk_win32_window_get_handle(window); // get window handle
2192 HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); // get monitor handle
2193 if(IS_NULL_PTR(hMonitor)){ return;} //TODO log error
2194 MONITORINFOEX monitorInfo;
2195 monitorInfo.cbSize = sizeof(MONITORINFOEX);
2196 if(!GetMonitorInfoW(hMonitor,(LPMONITORINFO) &monitorInfo)) { return;} //get monitor info , TODO log error
2197 HDC hdc = CreateIC(L"MONITOR",monitorInfo.szDevice,NULL,NULL); // get device-info context of the monitor
2198 if(!IS_NULL_PTR(hdc))
2199 {
2200 DWORD len = 0;
2201 GetICMProfile(hdc, &len, NULL);
2202 wchar_t *wpath = g_new(wchar_t, len);
2203
2204 if(GetICMProfileW(hdc, &len, wpath))
2205 {
2206 gchar *path = g_utf16_to_utf8(wpath, -1, NULL, NULL, NULL);
2207 if(path)
2208 {
2209 gsize size;
2210 g_file_get_contents(path, (gchar **)&buffer, &size, NULL);
2211 buffer_size = size;
2212 dt_free(path);
2213 }
2214 }
2215 dt_free(wpath);
2216 DeleteDC(hdc);
2217 }
2218 profile_source = g_strdup("windows color profile api");
2219#endif
2220
2221 int profile_changed = buffer_size > 0 && (darktable.color_profiles->xprofile_size != buffer_size
2222 || memcmp(darktable.color_profiles->xprofile_data, buffer, buffer_size) != 0);
2223
2224 if(profile_changed)
2225 {
2226 char name[512] = { 0 };
2227 _update_display_profile(buffer, buffer_size, name, sizeof(name));
2228 dt_print(DT_DEBUG_CONTROL, "[color profile] we got a new screen profile `%s' from the %s (size: %d)\n",
2229 *name ? name : "(unknown)", profile_source, buffer_size);
2230 }
2231 else
2232 {
2233 dt_free(buffer);
2234 }
2235 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
2237 dt_free(profile_source);
2238}
2239
2240static gboolean _colorspaces_is_base_name(const char *profile)
2241{
2242 const char *f = profile;
2243 while(*f != '\0')
2244 {
2245 if(*f == '/' || *f == '\\') return FALSE;
2246 f++;
2247 }
2248 return TRUE;
2249}
2250
2251static const char *_colorspaces_get_base_name(const char *profile)
2252{
2253 const char* f = profile + strlen(profile);
2254 for (; f >= profile; f--)
2255 {
2256 if(*f == '/' || *f == '\\')
2257 return ++f; // path separator found - return the filename only, without the leading separator
2258 }
2259 return f; // no separator found - consider profile_name to be a "base" one
2260}
2261
2262gboolean dt_colorspaces_is_profile_equal(const char *fullname, const char *filename)
2263{
2264 // for backward compatibility we need to also ensure that we check
2265 // for basename, indeed filename parameter may be in fact just a
2266 // basename as recorded in an iop.
2267 return _colorspaces_is_base_name(filename)
2268 ? !strcmp(_colorspaces_get_base_name(fullname), filename)
2269 : !strcmp(_colorspaces_get_base_name(fullname), _colorspaces_get_base_name(filename));
2270}
2271
2273{
2274 switch(cicp->color_primaries)
2275 {
2276 /* Give up immediately if unspecified */
2280 return DT_COLORSPACE_NONE;
2281 break; /* unspecified */
2282
2283 /* REC709 */
2285
2286 switch(cicp->transfer_characteristics)
2287 {
2288 /* SRGB */
2290
2291 switch(cicp->matrix_coefficients)
2292 {
2293 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2296 case DT_CICP_MATRIX_COEFFICIENTS_REC601: /* support equivalents just in case of mistagging */
2297 case DT_CICP_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL: /* support incorrectly tagged files */
2299 return DT_COLORSPACE_SRGB;
2300 default:
2301 break;
2302 }
2303
2304 break; /* SRGB */
2305
2306 /* REC709 */
2308 case DT_CICP_TRANSFER_CHARACTERISTICS_REC601: /* support equivalents just in case of mistagging */
2309 case DT_CICP_TRANSFER_CHARACTERISTICS_REC2020_10B: /* support equivalents just in case of mistagging */
2310 case DT_CICP_TRANSFER_CHARACTERISTICS_REC2020_12B: /* support equivalents just in case of mistagging */
2311
2312 switch(cicp->matrix_coefficients)
2313 {
2314 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2318 return DT_COLORSPACE_REC709;
2319 default:
2320 break;
2321 }
2322
2323 break; /* REC709 */
2324
2325 /* LINEAR REC709 */
2327
2328 switch(cicp->matrix_coefficients)
2329 {
2330 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2335 default:
2336 break;
2337 }
2338
2339 break; /* LINEAR REC709 */
2340
2341 default:
2342 break;
2343 }
2344
2345 break; /* REC709 */
2346
2347 /* REC2020 */
2349
2350 switch(cicp->transfer_characteristics)
2351 {
2352 /* LINEAR REC2020 */
2354
2355 switch(cicp->matrix_coefficients)
2356 {
2357 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2362 default:
2363 break;
2364 }
2365
2366 break; /* LINEAR REC2020 */
2367
2368 /* PQ REC2020 */
2370
2371 switch(cicp->matrix_coefficients)
2372 {
2373 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2378 default:
2379 break;
2380 }
2381
2382 break; /* PQ REC2020 */
2383
2384 /* HLG REC2020 */
2386
2387 switch(cicp->matrix_coefficients)
2388 {
2389 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2394 default:
2395 break;
2396 }
2397
2398 break; /* HLG REC2020 */
2399
2400 default:
2401 break;
2402 }
2403
2404 break; /* REC2020 */
2405
2406 /* P3 */
2408
2409 switch(cicp->transfer_characteristics)
2410 {
2411 /* PQ P3 */
2413
2414 switch(cicp->matrix_coefficients)
2415 {
2416 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2422 return DT_COLORSPACE_PQ_P3;
2423 default:
2424 break;
2425 }
2426
2427 break; /* PQ P3 */
2428
2429 /* HLG P3 */
2431
2432 switch(cicp->matrix_coefficients)
2433 {
2434 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2440 return DT_COLORSPACE_HLG_P3;
2441 default:
2442 break;
2443 }
2444
2445 break; /* HLG P3 */
2446
2447 /* Display P3 */
2449
2450 switch(cicp->matrix_coefficients)
2451 {
2452 case DT_CICP_MATRIX_COEFFICIENTS_IDENTITY: /* support RGB (4:4:4 or lossless) */
2459 default:
2460 break;
2461 }
2462
2463 break; /* Display P3 */
2464
2465 default:
2466 break;
2467 }
2468
2469 break; /* P3 */
2470
2471 /* XYZ */
2473
2474 switch(cicp->transfer_characteristics)
2475 {
2476 /* LINEAR XYZ */
2478
2479 switch(cicp->matrix_coefficients)
2480 {
2483 return DT_COLORSPACE_XYZ;
2484 default:
2485 break;
2486 }
2487
2488 break; /* LINEAR XYZ */
2489
2490 default:
2491 break;
2492 }
2493
2494 break; /* XYZ */
2495
2496 default:
2497 break;
2498 }
2499
2500 if(!IS_NULL_PTR(filename))
2501 dt_print(DT_DEBUG_IMAGEIO, "[colorin] unsupported CICP color profile for `%s': %d/%d/%d\n", filename,
2503
2504 return DT_COLORSPACE_NONE;
2505}
2506
2509 const char *filename,
2511{
2512 for(GList *iter = self->profiles; iter; iter = g_list_next(iter))
2513 {
2515 if(((direction & DT_PROFILE_DIRECTION_IN && p->in_pos > -1)
2516 || (direction & DT_PROFILE_DIRECTION_OUT && p->out_pos > -1)
2517 || (direction & DT_PROFILE_DIRECTION_WORK && p->work_pos > -1)
2518 || (direction & DT_PROFILE_DIRECTION_DISPLAY && p->display_pos > -1))
2519 && (p->type == type
2520 && (type != DT_COLORSPACE_FILE || dt_colorspaces_is_profile_equal(p->filename, filename))))
2521 {
2522 return p;
2523 }
2524 }
2525
2526 return NULL;
2527}
2528
2535
2536// Copied from dcraw's pseudoinverse()
2538static void dt_colorspaces_pseudoinverse(double (*in)[3], double (*out)[3], int size)
2539{
2540 double work[3][6];
2541
2542 for(int i = 0; i < 3; i++) {
2543 for(int j = 0; j < 6; j++)
2544 work[i][j] = j == i+3;
2545 for(int j = 0; j < 3; j++)
2546 for(int k = 0; k < size; k++)
2547 work[i][j] += in[k][i] * in[k][j];
2548 }
2549 for(int i = 0; i < 3; i++) {
2550 double num = work[i][i];
2551 for(int j = 0; j < 6; j++)
2552 work[i][j] /= num;
2553 for(int k = 0; k < 3; k++) {
2554 if(k==i) continue;
2555 num = work[k][i];
2556 for(int j = 0; j < 6; j++)
2557 work[k][j] -= work[i][j] * num;
2558 }
2559 }
2560 for(int i = 0; i < size; i++)
2561 for(int j = 0; j < 3; j++)
2562 {
2563 out[i][j] = 0.0f;
2564 for(int k = 0; k < 3; k++)
2565 out[i][j] += work[j][k+3] * in[i][k];
2566 }
2567}
2568
2569int dt_colorspaces_conversion_matrices_xyz(const float adobe_XYZ_to_CAM[4][3], float in_XYZ_to_CAM[9], double XYZ_to_CAM[4][3], double CAM_to_XYZ[3][4])
2570{
2571 if(!isnan(in_XYZ_to_CAM[0]))
2572 {
2573 for(int i = 0; i < 9; i++)
2574 XYZ_to_CAM[i/3][i%3] = (double) in_XYZ_to_CAM[i];
2575 for(int i = 0; i < 3; i++)
2576 XYZ_to_CAM[3][i] = 0.0f;
2577 }
2578 else
2579 {
2580 if(isnan(adobe_XYZ_to_CAM[0][0]))
2581 return FALSE;
2582
2583 for(int i = 0; i < 4; i++)
2584 for(int j = 0; j < 3; j++)
2585 XYZ_to_CAM[i][j] = (double)adobe_XYZ_to_CAM[i][j];
2586 }
2587
2588 // Invert the matrix
2589 double inverse[4][3];
2590 dt_colorspaces_pseudoinverse (XYZ_to_CAM, inverse, 4);
2591 for(int i = 0; i < 3; i++)
2592 for(int j = 0; j < 4; j++)
2593 CAM_to_XYZ[i][j] = inverse[j][i];
2594
2595 return TRUE;
2596}
2597
2598// Converted from dcraw's cam_xyz_coeff()
2599// Build the camera RGB to sRGB conversion matrix
2601int dt_colorspaces_conversion_matrices_rgb(const float adobe_XYZ_to_CAM[4][3],
2602 double out_RGB_to_CAM[4][3], double out_CAM_to_RGB[3][4],
2603 const float *embedded_matrix,
2604 double mul[4])
2605{
2606 double RGB_to_CAM[4][3];
2607
2608 float XYZ_to_CAM[4][3];
2609 XYZ_to_CAM[0][0] = NAN;
2610
2611 if(IS_NULL_PTR(embedded_matrix) || isnan(embedded_matrix[0]))
2612 {
2613 for(int k=0; k<4; k++)
2614 for(int i=0; i<3; i++)
2615 XYZ_to_CAM[k][i] = adobe_XYZ_to_CAM[k][i];
2616 }
2617 else
2618 {
2619 // keep in sync with reload_defaults from colorin.c
2620 // embedded matrix is used with higher priority than standard one
2621 XYZ_to_CAM[0][0] = embedded_matrix[0];
2622 XYZ_to_CAM[0][1] = embedded_matrix[1];
2623 XYZ_to_CAM[0][2] = embedded_matrix[2];
2624
2625 XYZ_to_CAM[1][0] = embedded_matrix[3];
2626 XYZ_to_CAM[1][1] = embedded_matrix[4];
2627 XYZ_to_CAM[1][2] = embedded_matrix[5];
2628
2629 XYZ_to_CAM[2][0] = embedded_matrix[6];
2630 XYZ_to_CAM[2][1] = embedded_matrix[7];
2631 XYZ_to_CAM[2][2] = embedded_matrix[8];
2632 }
2633
2634 if(isnan(XYZ_to_CAM[0][0]))
2635 return FALSE;
2636
2637 const double RGB_to_XYZ[3][3] = {
2638 // sRGB D65
2639 { 0.412453, 0.357580, 0.180423 },
2640 { 0.212671, 0.715160, 0.072169 },
2641 { 0.019334, 0.119193, 0.950227 },
2642 };
2643
2644 // Multiply RGB matrix
2645 for(int i = 0; i < 4; i++)
2646 for(int j = 0; j < 3; j++)
2647 {
2648 RGB_to_CAM[i][j] = 0.0f;
2649 for(int k = 0; k < 3; k++)
2650 RGB_to_CAM[i][j] += XYZ_to_CAM[i][k] * RGB_to_XYZ[k][j];
2651 }
2652
2653 // Normalize cam_rgb so that cam_rgb * (1,1,1) is (1,1,1,1)
2654 for(int i = 0; i < 4; i++) {
2655 double num = 0.0f;
2656 for(int j = 0; j < 3; j++)
2657 num += RGB_to_CAM[i][j];
2658 for(int j = 0; j < 3; j++)
2659 RGB_to_CAM[i][j] /= num;
2660 if(mul) mul[i] = 1.0f / num;
2661 }
2662
2663 if(out_RGB_to_CAM)
2664 for(int i = 0; i < 4; i++)
2665 for(int j = 0; j < 3; j++)
2666 out_RGB_to_CAM[i][j] = RGB_to_CAM[i][j];
2667
2668 if(out_CAM_to_RGB)
2669 {
2670 // Invert the matrix
2671 double inverse[4][3];
2672 dt_colorspaces_pseudoinverse (RGB_to_CAM, inverse, 4);
2673 for(int i = 0; i < 3; i++)
2674 for(int j = 0; j < 4; j++)
2675 out_CAM_to_RGB[i][j] = inverse[j][i];
2676 }
2677
2678 return TRUE;
2679}
2680
2681void dt_colorspaces_cygm_apply_coeffs_to_rgb(float *out, const float *in, int num, double RGB_to_CAM[4][3],
2682 double CAM_to_RGB[3][4], dt_aligned_pixel_t coeffs)
2683{
2684 // Create the CAM to RGB with applied WB matrix
2685 double CAM_to_RGB_WB[3][4];
2686 for (int a=0; a<3; a++)
2687 for (int b=0; b<4; b++)
2688 CAM_to_RGB_WB[a][b] = CAM_to_RGB[a][b] * coeffs[b];
2689
2690 // Create the RGB->RGB+WB matrix
2691 double RGB_to_RGB_WB[3][3];
2692 for (int a=0; a<3; a++)
2693 for (int b=0; b<3; b++) {
2694 RGB_to_RGB_WB[a][b] = 0.0f;
2695 for (int c=0; c<4; c++)
2696 RGB_to_RGB_WB[a][b] += CAM_to_RGB_WB[a][c] * RGB_to_CAM[c][b];
2697 }
2699 for(int i = 0; i < num; i++)
2700 {
2701 const float *inpos = &in[i*4];
2702 float *outpos = &out[i*4];
2703 outpos[0]=outpos[1]=outpos[2] = 0.0f;
2704 for (int a=0; a<3; a++)
2705 for (int b=0; b<3; b++)
2706 outpos[a] += RGB_to_RGB_WB[a][b] * inpos[b];
2707 }
2708}
2709
2711void dt_colorspaces_cygm_to_rgb(float *out, int num, double CAM_to_RGB[3][4])
2712{
2714 for(int i = 0; i < num; i++)
2715 {
2716 float *in = &out[i*4];
2717 dt_aligned_pixel_t o = {0.0f,0.0f,0.0f};
2718 for(int c = 0; c < 3; c++)
2719 for(int k = 0; k < 4; k++)
2720 o[c] += CAM_to_RGB[c][k] * in[k];
2721 for(int c = 0; c < 3; c++)
2722 in[c] = o[c];
2723 }
2724}
2725
2726void dt_colorspaces_rgb_to_cygm(float *out, int num, double RGB_to_CAM[4][3])
2727{
2729 for(int i = 0; i < num; i++)
2730 {
2731 float *in = &out[i*3];
2732 dt_aligned_pixel_t o = {0.0f,0.0f,0.0f,0.0f};
2733 for(int c = 0; c < 4; c++)
2734 for(int k = 0; k < 3; k++)
2735 o[c] += RGB_to_CAM[c][k] * in[k];
2736 for(int c = 0; c < 4; c++)
2737 in[c] = o[c];
2738 }
2739}
2740
2741// clang-format off
2742// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2743// vim: shiftwidth=2 expandtab tabstop=2 cindent
2744// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2745// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
uint32_t container(dt_lib_module_t *self)
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static void transform(float *x, float *o, const float *m, const float t_h, const float t_v)
Definition clipping.c:482
static void profile_changed(GtkWidget *widget, gpointer user_data)
Definition colorin.c:511
static dt_profiled_colormatrix_t dt_profiled_colormatrices[]
static dt_profiled_colormatrix_t dt_vendor_colormatrices[]
static dt_profiled_colormatrix_t dt_alternate_colormatrices[]
static const int dt_vendor_colormatrix_cnt
static const int dt_alternate_colormatrix_cnt
static const int dt_profiled_colormatrix_cnt
static cmsHPROFILE dt_colorspaces_create_gamma_rec709_rgb_profile(void)
static const cmsCIExyYTRIPLE Rec2020_Primaries
static const char * _colorspaces_get_base_name(const char *profile)
cmsHPROFILE dt_colorspaces_create_xyzimatrix_profile(float mat[3][3])
static cmsHPROFILE dt_colorspaces_create_linear_rec709_rgb_profile(void)
#define generate_mat3inv_body(c_type, A, B)
#define B(y, x)
__DT_CLONE_TARGETS__ int dt_colorspaces_conversion_matrices_rgb(const float adobe_XYZ_to_CAM[4][3], double out_RGB_to_CAM[4][3], double out_CAM_to_RGB[3][4], const float *embedded_matrix, double mul[4])
#define A(y, x)
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_direction_t direction)
static cmsHPROFILE dt_colorspaces_create_itur_bt1886_rgb_profile(void)
void dt_colorspaces_rgb_to_cygm(float *out, int num, double RGB_to_CAM[4][3])
dt_colorspaces_color_profile_type_t dt_colorspaces_get_input_profile_from_image(int32_t imgid, dt_colorspaces_color_profile_type_t requested, cmsHPROFILE *output, gboolean *new_profile)
Resolve an embedded/matrix input profile for a given image, honoring the requested type when possible...
static const dt_colorspaces_color_profile_t * _get_profile(dt_colorspaces_t *self, dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_direction_t direction)
static const cmsCIExyYTRIPLE ProPhoto_Primaries
void rgb2hsl(const dt_aligned_pixel_t rgb, float *h, float *s, float *l)
static cmsHPROFILE dt_colorspaces_create_lab_profile()
static void _update_display_profile(guchar *tmp_data, gsize size, char *name, size_t name_size)
static cmsHPROFILE dt_colorspaces_create_pq_rec2020_rgb_profile(void)
static const cmsCIExyY D65xyY
static cmsHPROFILE dt_colorspaces_create_xyzmatrix_profile(const float mat[3][3])
static const cmsCIExyYTRIPLE Rec709_Primaries
const dt_colorspaces_color_profile_t * _build_embedded_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *type)
const dt_colorspaces_color_profile_t * dt_colorspaces_get_output_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *over_type, const char *over_filename)
__DT_CLONE_TARGETS__ void dt_colorspaces_transform_rgba_float_image(const cmsHTRANSFORM transform, const float *image_in, float *image_out, const int width, const int height)
int dt_colorspaces_get_matrix_from_output_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg, float *lutb, const int lutsize)
cmsCIEXYZTRIPLE Rec709_Primaries_Prequantized
void dt_colorspaces_update_display_transforms()
static cmsHPROFILE dt_colorspaces_create_linear_infrared_profile(void)
static cmsHPROFILE dt_colorspaces_create_hlg_p3_rgb_profile(void)
static cmsHPROFILE _create_lcms_profile(const char *desc, const char *dmdd, const cmsCIExyY *whitepoint, const cmsCIExyYTRIPLE *primaries, cmsToneCurve *trc, gboolean v2)
static dt_colorspaces_color_profile_t * _create_profile(dt_colorspaces_color_profile_type_t type, cmsHPROFILE profile, const char *name, int in_pos, int out_pos, int display_pos, int category_pos, int work_pos)
static cmsHPROFILE dt_colorspaces_create_srgb_profile()
static cmsHPROFILE dt_colorspaces_create_hlg_rec2020_rgb_profile(void)
const cmsHPROFILE dt_colorspaces_get_embedded_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *type, gboolean *new_profile)
static cmsHPROFILE dt_colorspaces_create_xyz_profile(void)
static cmsHPROFILE dt_colorspaces_create_display_p3_rgb_profile(void)
cmsHPROFILE dt_colorspaces_create_vendor_profile(const char *makermodel)
static double _PQ_fct(double x)
int dt_colorspaces_get_matrix_from_input_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg, float *lutb, const int lutsize)
static void cms_error_handler(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *text)
static cmsHPROFILE dt_colorspaces_create_linear_prophoto_rgb_profile(void)
static gboolean _colorspaces_is_base_name(const char *profile)
int mat3inv(float *const dst, const float *const src)
static __DT_CLONE_TARGETS__ void dt_colorspaces_pseudoinverse(double(*in)[3], double(*out)[3], int size)
void dt_colorspaces_cleanup(dt_colorspaces_t *self)
cmsHPROFILE dt_colorspaces_create_darktable_profile(const char *makermodel)
int dt_colorspaces_conversion_matrices_xyz(const float adobe_XYZ_to_CAM[4][3], float in_XYZ_to_CAM[9], double XYZ_to_CAM[4][3], double CAM_to_XYZ[3][4])
__DT_CLONE_TARGETS__ void dt_colorspaces_cygm_to_rgb(float *out, int num, double CAM_to_RGB[3][4])
static gint _sort_profiles(gconstpointer a, gconstpointer b)
static const cmsCIExyY D50xyY
void dt_colorspaces_cleanup_profile(cmsHPROFILE p)
dt_colorspaces_color_profile_type_t dt_image_find_best_color_profile(int32_t imgid, cmsHPROFILE *output, gboolean *new_profile)
Best effort to find a suitable (input) color profile for a given image, using embedded ICC or EXIF wh...
const dt_colorspaces_color_profile_t * dt_colorspaces_get_work_profile(const int32_t imgid)
static cmsHPROFILE dt_colorspaces_create_adobergb_profile(void)
static cmsHPROFILE dt_colorspaces_create_srgb_profile_v4()
static cmsToneCurve * _colorspaces_create_transfer(int32_t size, double(*fct)(double))
dt_colorspaces_t * dt_colorspaces_init()
cmsHPROFILE dt_colorspaces_get_rgb_profile_from_mem(uint8_t *data, uint32_t size)
static cmsHPROFILE dt_colorspaces_create_linear_rec2020_rgb_profile(void)
static cmsHPROFILE dt_colorspaces_create_pq_p3_rgb_profile(void)
const char * dt_colorspaces_get_name(dt_colorspaces_color_profile_type_t type, const char *filename)
static GList * load_profile_from_dir(const char *subdir)
static void _update_display_transforms(dt_colorspaces_t *self)
static const cmsCIExyYTRIPLE P3_Primaries
static cmsHPROFILE _colorspaces_create_srgb_profile(gboolean v2)
void dt_colorspaces_get_profile_name(cmsHPROFILE p, const char *language, const char *country, char *name, size_t len)
static const cmsCIExyYTRIPLE sRGB_Primaries
static __DT_CLONE_TARGETS__ int dt_colorspaces_get_matrix_from_profile(cmsHPROFILE prof, dt_colormatrix_t matrix, float *lutr, float *lutg, float *lutb, const int lutsize, const int input)
void dt_colorspaces_set_display_profile(const dt_colorspaces_color_profile_type_t profile_type)
cmsHPROFILE dt_colorspaces_create_alternate_profile(const char *makermodel)
static const cmsCIExyYTRIPLE Adobe_Primaries
void hsl2rgb(dt_aligned_pixel_t rgb, float h, float s, float l)
static const cmsCIEXYZ d65
gboolean dt_colorspaces_is_profile_equal(const char *fullname, const char *filename)
static cmsHPROFILE _ensure_rgb_profile(cmsHPROFILE profile)
dt_colorspaces_color_profile_type_t dt_colorspaces_cicp_to_type(const dt_colorspaces_cicp_t *cicp, const char *filename)
void dt_colorspaces_transform_rgba8_to_bgra8(const cmsHTRANSFORM transform, const uint8_t *image_in, uint8_t *image_out, const int width, const int height)
void dt_colorspaces_cygm_apply_coeffs_to_rgb(float *out, const float *in, int num, double RGB_to_CAM[4][3], double CAM_to_RGB[3][4], dt_aligned_pixel_t coeffs)
int mat3inv_float(float *const dst, const float *const src)
static double _HLG_fct(double x)
void dt_colorspaces_transform_rgba_float_row(const cmsHTRANSFORM transform, const float *in, float *out, const int width)
static cmsHPROFILE dt_colorspaces_create_brg_profile()
static void _compute_prequantized_primaries(const cmsCIExyY *whitepoint, const cmsCIExyYTRIPLE *primaries, cmsCIEXYZTRIPLE *primaries_prequantized)
#define TYPE_XYZA_FLT
Definition colorspaces.h:53
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_ADOBERGB
Definition colorspaces.h:85
@ DT_COLORSPACE_PROPHOTO_RGB
@ DT_COLORSPACE_EMBEDDED_MATRIX
Definition colorspaces.h:93
@ DT_COLORSPACE_EMBEDDED_ICC
Definition colorspaces.h:92
@ DT_COLORSPACE_FILE
Definition colorspaces.h:83
@ DT_COLORSPACE_DISPLAY
Definition colorspaces.h:91
@ DT_COLORSPACE_SRGB
Definition colorspaces.h:84
@ DT_COLORSPACE_INFRARED
Definition colorspaces.h:90
@ DT_COLORSPACE_EXPORT
Definition colorspaces.h:99
@ DT_COLORSPACE_LAB
Definition colorspaces.h:89
@ DT_COLORSPACE_REC709
@ DT_COLORSPACE_HLG_P3
@ DT_COLORSPACE_PQ_P3
@ DT_COLORSPACE_PQ_REC2020
@ DT_COLORSPACE_DISPLAY2
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
@ DT_COLORSPACE_HLG_REC2020
@ DT_COLORSPACE_WORK
@ DT_COLORSPACE_LAST
@ DT_COLORSPACE_ENHANCED_MATRIX
Definition colorspaces.h:95
@ DT_COLORSPACE_LIN_REC2020
Definition colorspaces.h:87
@ DT_COLORSPACE_DISPLAY_P3
@ DT_COLORSPACE_STANDARD_MATRIX
Definition colorspaces.h:94
@ DT_COLORSPACE_XYZ
Definition colorspaces.h:88
@ DT_COLORSPACE_BRG
Definition colorspaces.h:98
@ DT_COLORSPACE_VENDOR_MATRIX
Definition colorspaces.h:96
@ DT_COLORSPACE_SOFTPROOF
@ DT_COLORSPACE_ITUR_BT1886
@ DT_COLORSPACE_LIN_REC709
Definition colorspaces.h:86
@ DT_COLORSPACE_ALTERNATE_MATRIX
Definition colorspaces.h:97
dt_colorspaces_profile_direction_t
@ DT_PROFILE_DIRECTION_IN
@ DT_PROFILE_DIRECTION_OUT
@ DT_PROFILE_DIRECTION_DISPLAY
@ DT_PROFILE_DIRECTION_WORK
@ DT_CICP_TRANSFER_CHARACTERISTICS_UNSPECIFIED
@ DT_CICP_TRANSFER_CHARACTERISTICS_LINEAR
@ DT_CICP_TRANSFER_CHARACTERISTICS_HLG
@ DT_CICP_TRANSFER_CHARACTERISTICS_REC2020_12B
@ DT_CICP_TRANSFER_CHARACTERISTICS_REC709
@ DT_CICP_TRANSFER_CHARACTERISTICS_PQ
@ DT_CICP_TRANSFER_CHARACTERISTICS_REC2020_10B
@ DT_CICP_TRANSFER_CHARACTERISTICS_REC601
@ DT_CICP_TRANSFER_CHARACTERISTICS_SRGB
@ DT_PROFILE_GAMUTCHECK
@ DT_PROFILE_NORMAL
@ DT_CICP_COLOR_PRIMARIES_REC2020
@ DT_CICP_COLOR_PRIMARIES_UNSPECIFIED
@ DT_CICP_COLOR_PRIMARIES_P3
@ DT_CICP_COLOR_PRIMARIES_REC709
@ DT_CICP_COLOR_PRIMARIES_XYZ
@ DT_CICP_MATRIX_COEFFICIENTS_REC601
@ DT_CICP_MATRIX_COEFFICIENTS_REC2020_NCL
@ DT_CICP_MATRIX_COEFFICIENTS_UNSPECIFIED
@ DT_CICP_MATRIX_COEFFICIENTS_IDENTITY
@ DT_CICP_MATRIX_COEFFICIENTS_SYCC
@ DT_CICP_MATRIX_COEFFICIENTS_REC709
@ DT_CICP_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL
static dt_aligned_pixel_t rgb
const dt_aligned_pixel_t f
const dt_colormatrix_t dt_aligned_pixel_t out
static const float const float C
const dt_colormatrix_t matrix
const float delta
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
gboolean dt_image_is_monochrome(const dt_image_t *img)
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
int type
char * name
int dt_conf_get_bool(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
int dt_control_running()
Definition control.c:433
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
@ DT_DEBUG_CONTROL
Definition darktable.h:738
@ DT_DEBUG_COLORPROFILE
Definition darktable.h:766
@ DT_DEBUG_IMAGEIO
Definition darktable.h:755
@ DT_DEBUG_DEV
Definition darktable.h:739
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:546
static const dt_aligned_pixel_simd_t sign
Definition darktable.h:573
#define dt_free(ptr)
Definition darktable.h:478
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#define PATH_MAX
Definition darktable.h:1189
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3653
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
@ DT_IMAGE_COLORSPACE_ADOBE_RGB
Definition image.h:181
@ DT_IMAGE_COLORSPACE_SRGB
Definition image.h:180
@ DT_IMAGE_4BAYER
Definition image.h:127
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
int dt_imageio_avif_read_profile(const char *filename, uint8_t **out, dt_colorspaces_cicp_t *cicp)
int dt_imageio_heif_read_profile(const char *filename, uint8_t **out, dt_colorspaces_cicp_t *cicp)
int dt_imageio_j2k_read_profile(const char *filename, uint8_t **out)
int dt_imageio_jpeg_read_profile(dt_imageio_jpeg_t *jpg, uint8_t **out)
int dt_imageio_jpeg_read_header(const char *filename, dt_imageio_jpeg_t *jpg)
int dt_imageio_png_read_profile(const char *filename, uint8_t **out)
int dt_imageio_tiff_read_profile(const char *filename, uint8_t **out)
struct dt_iop_tonecurve_params_t preset
static const float x
const float const int lutsize
float *const restrict const size_t k
#define R
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:33
static int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
Definition matrices.h:36
size_t size
Definition mipmap_cache.c:3
dt_colorspaces_color_profile_type_t color_space
Definition mipmap_cache.c:5
float dt_aligned_pixel_t[4]
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_CONTROL_PROFILE_CHANGED
This signal is raised when the screen profile has changed no param, no returned value.
Definition signal.h:236
struct _GtkWidget GtkWidget
Definition splash.h:29
const float r
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_colorspaces_t * color_profiles
Definition darktable.h:816
GList * iop
Definition darktable.h:789
const struct dt_database_t * db
Definition darktable.h:807
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_image_cache_t * image_cache
Definition darktable.h:805
struct dt_develop_t * develop
Definition darktable.h:798
dt_colorspaces_cicp_matrix_coefficients_t matrix_coefficients
dt_colorspaces_cicp_transfer_characteristics_t transfer_characteristics
dt_colorspaces_cicp_color_primaries_t color_primaries
dt_colorspaces_color_profile_type_t type
cmsHTRANSFORM transform_adobe_rgb_to_display
dt_colorspaces_color_profile_type_t softproof_type
gchar * colord_profile_file
cmsHTRANSFORM transform_display_to_adobe_rgb
cmsHTRANSFORM transform_srgb_to_display
cmsHTRANSFORM transform_xyz_to_display
pthread_rwlock_t xprofile_lock
dt_colorspaces_color_mode_t mode
char softproof_filename[512]
uint8_t * xprofile_data
dt_colorspaces_color_profile_type_t display_type
char display_filename[512]
dt_iop_color_intent_t softproof_intent
dt_iop_color_intent_t display_intent
dt_ui_t * ui
Definition gtk.h:165
int32_t flags
Definition image.h:319
uint32_t profile_size
Definition image.h:341
float d65_color_matrix[9]
Definition image.h:339
dt_image_colorspace_t colorspace
Definition image.h:342
float adobe_XYZ_to_CAM[4][3]
Definition image.h:362
uint8_t * profile
Definition image.h:340
GtkWidget * window
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
char * dt_read_file(const char *const filename, size_t *filesize)
Definition utility.c:893