1 /*
2 This file is part of darktable,
3 copyright (c) 2009--2010 johannes hanika.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdlib.h>
20 #include <stdint.h>
21 #include <string.h>
22 #include <glib.h>
23 #include <math.h>
24 #include <strings.h>
25
26 /*
27 #include "common/darktable.h"
28 #include "control/conf.h"
29 #include "control/control.h"
30 */
31 #include "colormatrices.c"
32 #include "colorspaces.h"
33 //#include "common/debug.h"
34 #include "srgb_tone_curve_values.h"
35 //#include "develop/imageop.h"
36
37
38 /** inverts the given 3x3 matrix */
39 static int
mat3inv(float * const dst,const float * const src)40 mat3inv (float * const dst, const float *const src)
41 {
42 #define A(y, x) src[(y - 1) * 3 + (x - 1)]
43 #define B(y, x) dst[(y - 1) * 3 + (x - 1)]
44
45 const float det =
46 A(1, 1) * (A(3, 3) * A(2, 2) - A(3, 2) * A(2, 3)) -
47 A(2, 1) * (A(3, 3) * A(1, 2) - A(3, 2) * A(1, 3)) +
48 A(3, 1) * (A(2, 3) * A(1, 2) - A(2, 2) * A(1, 3));
49
50 const float epsilon = 1e-7f;
51 if (fabsf(det) < epsilon) return 1;
52
53 const float invDet = 1.f / det;
54
55 B(1, 1) = invDet * (A(3, 3) * A(2, 2) - A(3, 2) * A(2, 3));
56 B(1, 2) = -invDet * (A(3, 3) * A(1, 2) - A(3, 2) * A(1, 3));
57 B(1, 3) = invDet * (A(2, 3) * A(1, 2) - A(2, 2) * A(1, 3));
58
59 B(2, 1) = -invDet * (A(3, 3) * A(2, 1) - A(3, 1) * A(2, 3));
60 B(2, 2) = invDet * (A(3, 3) * A(1, 1) - A(3, 1) * A(1, 3));
61 B(2, 3) = -invDet * (A(2, 3) * A(1, 1) - A(2, 1) * A(1, 3));
62
63 B(3, 1) = invDet * (A(3, 2) * A(2, 1) - A(3, 1) * A(2, 2));
64 B(3, 2) = -invDet * (A(3, 2) * A(1, 1) - A(3, 1) * A(1, 2));
65 B(3, 3) = invDet * (A(2, 2) * A(1, 1) - A(2, 1) * A(1, 2));
66 #undef A
67 #undef B
68 return 0;
69 }
70
71 static void
mat3mulv(float * dst,const float * const mat,const float * const v)72 mat3mulv (float *dst, const float *const mat, const float *const v)
73 {
74 for(int k=0; k<3; k++)
75 {
76 float x=0.0f;
77 for(int i=0; i<3; i++) x += mat[3*k+i] * v[i];
78 dst[k] = x;
79 }
80 }
81
82 static void
mat3mul(float * dst,const float * const m1,const float * const m2)83 mat3mul (float *dst, const float *const m1, const float *const m2)
84 {
85 for(int k=0; k<3; k++)
86 {
87 for(int i=0; i<3; i++)
88 {
89 float x=0.0f;
90 for(int j=0; j<3; j++) x += m1[3*k+j] * m2[3*j+i];
91 dst[3*k+i] = x;
92 }
93 }
94 }
95
96 static int
dt_colorspaces_get_matrix_from_profile(cmsHPROFILE prof,float * matrix,float * lutr,float * lutg,float * lutb,const int lutsize,const int input)97 dt_colorspaces_get_matrix_from_profile (cmsHPROFILE prof, float *matrix, float *lutr, float *lutg, float* lutb, const int lutsize, const int input)
98 {
99 // create an OpenCL processable matrix + tone curves from an cmsHPROFILE:
100
101 // check this first:
102 if(!cmsIsMatrixShaper(prof)) return 1;
103
104 cmsToneCurve* red_curve = cmsReadTag(prof, cmsSigRedTRCTag);
105 cmsToneCurve* green_curve = cmsReadTag(prof, cmsSigGreenTRCTag);
106 cmsToneCurve* blue_curve = cmsReadTag(prof, cmsSigBlueTRCTag);
107
108 cmsCIEXYZ *red_color = cmsReadTag(prof, cmsSigRedColorantTag);
109 cmsCIEXYZ *green_color = cmsReadTag(prof, cmsSigGreenColorantTag);
110 cmsCIEXYZ *blue_color = cmsReadTag(prof, cmsSigBlueColorantTag);
111
112 if(!red_curve || !green_curve || !blue_curve || !red_color || !green_color || !blue_color) return 2;
113
114 matrix[0] = red_color->X;
115 matrix[1] = green_color->X;
116 matrix[2] = blue_color->X;
117 matrix[3] = red_color->Y;
118 matrix[4] = green_color->Y;
119 matrix[5] = blue_color->Y;
120 matrix[6] = red_color->Z;
121 matrix[7] = green_color->Z;
122 matrix[8] = blue_color->Z;
123
124 // some camera ICC profiles claim to have color locations for red, green and blue base colors defined,
125 // but in fact these are all set to zero. we catch this case here.
126 float sum = 0.0f;
127 for(int k=0; k<9; k++) sum += matrix[k];
128 if(sum == 0.0f) return 3;
129
130 if(input)
131 {
132 // mark as linear, if they are:
133 if(cmsIsToneCurveLinear(red_curve)) lutr[0] = -1.0f;
134 else for(int k=0; k<lutsize; k++) lutr[k] = cmsEvalToneCurveFloat(red_curve, k/(lutsize-1.0f));
135 if(cmsIsToneCurveLinear(green_curve)) lutg[0] = -1.0f;
136 else for(int k=0; k<lutsize; k++) lutg[k] = cmsEvalToneCurveFloat(green_curve, k/(lutsize-1.0f));
137 if(cmsIsToneCurveLinear(blue_curve)) lutb[0] = -1.0f;
138 else for(int k=0; k<lutsize; k++) lutb[k] = cmsEvalToneCurveFloat(blue_curve, k/(lutsize-1.0f));
139 }
140 else
141 {
142 // invert profile->XYZ matrix for output profiles
143 float tmp[9];
144 memcpy(tmp, matrix, sizeof(float)*9);
145 if(mat3inv (matrix, tmp)) return 3;
146 // also need to reverse gamma, to apply reverse before matrix multiplication:
147 cmsToneCurve* rev_red = cmsReverseToneCurveEx(0x8000, red_curve);
148 cmsToneCurve* rev_green = cmsReverseToneCurveEx(0x8000, green_curve);
149 cmsToneCurve* rev_blue = cmsReverseToneCurveEx(0x8000, blue_curve);
150 if(!rev_red || !rev_green || !rev_blue)
151 {
152 cmsFreeToneCurve(rev_red);
153 cmsFreeToneCurve(rev_green);
154 cmsFreeToneCurve(rev_blue);
155 return 4;
156 }
157 // pass on tonecurves, in case lutsize > 0:
158 if(cmsIsToneCurveLinear(red_curve)) lutr[0] = -1.0f;
159 else for(int k=0; k<lutsize; k++) lutr[k] = cmsEvalToneCurveFloat(rev_red, k/(lutsize-1.0f));
160 if(cmsIsToneCurveLinear(green_curve)) lutg[0] = -1.0f;
161 else for(int k=0; k<lutsize; k++) lutg[k] = cmsEvalToneCurveFloat(rev_green, k/(lutsize-1.0f));
162 if(cmsIsToneCurveLinear(blue_curve)) lutb[0] = -1.0f;
163 else for(int k=0; k<lutsize; k++) lutb[k] = cmsEvalToneCurveFloat(rev_blue, k/(lutsize-1.0f));
164 cmsFreeToneCurve(rev_red);
165 cmsFreeToneCurve(rev_green);
166 cmsFreeToneCurve(rev_blue);
167 }
168 return 0;
169 }
170
171 int
dt_colorspaces_get_matrix_from_input_profile(cmsHPROFILE prof,float * matrix,float * lutr,float * lutg,float * lutb,const int lutsize)172 dt_colorspaces_get_matrix_from_input_profile (cmsHPROFILE prof, float *matrix, float *lutr, float *lutg, float* lutb, const int lutsize)
173 {
174 return dt_colorspaces_get_matrix_from_profile(prof, matrix, lutr, lutg, lutb, lutsize, 1);
175 }
176
177 int
dt_colorspaces_get_matrix_from_output_profile(cmsHPROFILE prof,float * matrix,float * lutr,float * lutg,float * lutb,const int lutsize)178 dt_colorspaces_get_matrix_from_output_profile (cmsHPROFILE prof, float *matrix, float *lutr, float *lutg, float* lutb, const int lutsize)
179 {
180 return dt_colorspaces_get_matrix_from_profile(prof, matrix, lutr, lutg, lutb, lutsize, 0);
181 }
182
183 cmsHPROFILE
dt_colorspaces_create_lab_profile()184 dt_colorspaces_create_lab_profile()
185 {
186 return cmsCreateLab4Profile(cmsD50_xyY());
187 }
188
189 cmsHPROFILE
dt_colorspaces_create_srgb_profile()190 dt_colorspaces_create_srgb_profile()
191 {
192 cmsHPROFILE hsRGB;
193
194 cmsCIEXYZTRIPLE Colorants =
195 {
196 {0.436066, 0.222488, 0.013916},
197 {0.385147, 0.716873, 0.097076},
198 {0.143066, 0.060608, 0.714096}
199 };
200
201 cmsCIEXYZ black = { 0, 0, 0 };
202 cmsCIEXYZ D65 = { 0.95045, 1, 1.08905 };
203 cmsToneCurve* transferFunction;
204
205 transferFunction = cmsBuildTabulatedToneCurve16(NULL, dt_srgb_tone_curve_values_n, dt_srgb_tone_curve_values);
206
207 hsRGB = cmsCreateProfilePlaceholder(0);
208
209 cmsSetProfileVersion(hsRGB, 2.1);
210
211 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
212 cmsMLUsetASCII(mlu0, "en", "US", "Public Domain");
213 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
214 cmsMLUsetASCII(mlu1, "en", "US", "sRGB");
215 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
216 cmsMLUsetASCII(mlu2, "en", "US", "Darktable");
217 cmsMLU *mlu3 = cmsMLUalloc(NULL, 1);
218 cmsMLUsetASCII(mlu3, "en", "US", "sRGB");
219 // this will only be displayed when the embedded profile is read by for example GIMP
220 cmsWriteTag(hsRGB, cmsSigCopyrightTag, mlu0);
221 cmsWriteTag(hsRGB, cmsSigProfileDescriptionTag, mlu1);
222 cmsWriteTag(hsRGB, cmsSigDeviceMfgDescTag, mlu2);
223 cmsWriteTag(hsRGB, cmsSigDeviceModelDescTag, mlu3);
224 cmsMLUfree(mlu0);
225 cmsMLUfree(mlu1);
226 cmsMLUfree(mlu2);
227 cmsMLUfree(mlu3);
228
229 cmsSetDeviceClass(hsRGB, cmsSigDisplayClass);
230 cmsSetColorSpace(hsRGB, cmsSigRgbData);
231 cmsSetPCS(hsRGB, cmsSigXYZData);
232
233 cmsWriteTag(hsRGB, cmsSigMediaWhitePointTag, &D65);
234 cmsWriteTag(hsRGB, cmsSigMediaBlackPointTag, &black);
235
236 cmsWriteTag(hsRGB, cmsSigRedColorantTag, (void*) &Colorants.Red);
237 cmsWriteTag(hsRGB, cmsSigGreenColorantTag, (void*) &Colorants.Green);
238 cmsWriteTag(hsRGB, cmsSigBlueColorantTag, (void*) &Colorants.Blue);
239
240 cmsWriteTag(hsRGB, cmsSigRedTRCTag, (void*) transferFunction);
241 cmsLinkTag(hsRGB, cmsSigGreenTRCTag, cmsSigRedTRCTag );
242 cmsLinkTag(hsRGB, cmsSigBlueTRCTag, cmsSigRedTRCTag );
243
244 return hsRGB;
245 }
246
247 // Create the ICC virtual profile for adobe rgb space
248 cmsHPROFILE
dt_colorspaces_create_adobergb_profile(void)249 dt_colorspaces_create_adobergb_profile(void)
250 {
251 cmsHPROFILE hAdobeRGB;
252
253 cmsCIEXYZTRIPLE Colorants =
254 {
255 {0.609741, 0.311111, 0.019470},
256 {0.205276, 0.625671, 0.060867},
257 {0.149185, 0.063217, 0.744568}
258 };
259
260 cmsCIEXYZ black = { 0, 0, 0 };
261 cmsCIEXYZ D65 = { 0.95045, 1, 1.08905 };
262 cmsToneCurve* transferFunction;
263
264 // AdobeRGB's "2.2" gamma is technically defined as 2 + 51/256
265 transferFunction = cmsBuildGamma(NULL, 2.19921875);
266
267 hAdobeRGB = cmsCreateProfilePlaceholder(0);
268
269 cmsSetProfileVersion(hAdobeRGB, 2.1);
270
271 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
272 cmsMLUsetASCII(mlu0, "en", "US", "Public Domain");
273 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
274 cmsMLUsetASCII(mlu1, "en", "US", "Adobe RGB (compatible)");
275 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
276 cmsMLUsetASCII(mlu2, "en", "US", "Darktable");
277 cmsMLU *mlu3 = cmsMLUalloc(NULL, 1);
278 cmsMLUsetASCII(mlu3, "en", "US", "Adobe RGB");
279 // this will only be displayed when the embedded profile is read by for example GIMP
280 cmsWriteTag(hAdobeRGB, cmsSigCopyrightTag, mlu0);
281 cmsWriteTag(hAdobeRGB, cmsSigProfileDescriptionTag, mlu1);
282 cmsWriteTag(hAdobeRGB, cmsSigDeviceMfgDescTag, mlu2);
283 cmsWriteTag(hAdobeRGB, cmsSigDeviceModelDescTag, mlu3);
284 cmsMLUfree(mlu0);
285 cmsMLUfree(mlu1);
286 cmsMLUfree(mlu2);
287 cmsMLUfree(mlu3);
288
289 cmsSetDeviceClass(hAdobeRGB, cmsSigDisplayClass);
290 cmsSetColorSpace(hAdobeRGB, cmsSigRgbData);
291 cmsSetPCS(hAdobeRGB, cmsSigXYZData);
292
293 cmsWriteTag(hAdobeRGB, cmsSigMediaWhitePointTag, &D65);
294 cmsWriteTag(hAdobeRGB, cmsSigMediaBlackPointTag, &black);
295
296 cmsWriteTag(hAdobeRGB, cmsSigRedColorantTag, (void*) &Colorants.Red);
297 cmsWriteTag(hAdobeRGB, cmsSigGreenColorantTag, (void*) &Colorants.Green);
298 cmsWriteTag(hAdobeRGB, cmsSigBlueColorantTag, (void*) &Colorants.Blue);
299
300 cmsWriteTag(hAdobeRGB, cmsSigRedTRCTag, (void*) transferFunction);
301 cmsLinkTag(hAdobeRGB, cmsSigGreenTRCTag, cmsSigRedTRCTag );
302 cmsLinkTag(hAdobeRGB, cmsSigBlueTRCTag, cmsSigRedTRCTag );
303
304 return hAdobeRGB;
305 }
306
307 // Create the ICC virtual profile for prophoto rgb space
308 cmsHPROFILE
dt_colorspaces_create_prophotorgb_profile(void)309 dt_colorspaces_create_prophotorgb_profile(void)
310 {
311 cmsHPROFILE hProphotoRGB;
312
313 cmsCIEXYZTRIPLE Colorants = {
314 {0.7976749, 0.2880402, 0.0000000},
315 {0.1351917, 0.7118741, 0.0000000},
316 {0.0313534, 0.0000857, 0.8252100}
317 };
318
319 cmsCIEXYZ black = { 0, 0, 0 };
320 cmsCIEXYZ D50 = { 0.9642, 1.0, 0.8249 };
321
322 cmsToneCurve* transferFunction;
323
324 // ProphotoRGB's "1.8" gamma
325 transferFunction = cmsBuildGamma(NULL, 1.8);
326
327 hProphotoRGB = cmsCreateProfilePlaceholder(0);
328
329 cmsSetProfileVersion(hProphotoRGB, 2.1);
330
331 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
332 cmsMLUsetASCII(mlu0, "en", "US", "Public Domain");
333 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
334 cmsMLUsetASCII(mlu1, "en", "US", "Prophoto RGB (compatible)");
335 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
336 cmsMLUsetASCII(mlu2, "en", "US", "Darktable");
337 cmsMLU *mlu3 = cmsMLUalloc(NULL, 1);
338 cmsMLUsetASCII(mlu3, "en", "US", "Prophoto RGB");
339 // this will only be displayed when the embedded profile is read by for example GIMP
340 cmsWriteTag(hProphotoRGB, cmsSigCopyrightTag, mlu0);
341 cmsWriteTag(hProphotoRGB, cmsSigProfileDescriptionTag, mlu1);
342 cmsWriteTag(hProphotoRGB, cmsSigDeviceMfgDescTag, mlu2);
343 cmsWriteTag(hProphotoRGB, cmsSigDeviceModelDescTag, mlu3);
344 cmsMLUfree(mlu0);
345 cmsMLUfree(mlu1);
346 cmsMLUfree(mlu2);
347 cmsMLUfree(mlu3);
348
349 cmsSetDeviceClass(hProphotoRGB, cmsSigDisplayClass);
350 cmsSetColorSpace(hProphotoRGB, cmsSigRgbData);
351 cmsSetPCS(hProphotoRGB, cmsSigXYZData);
352
353 cmsWriteTag(hProphotoRGB, cmsSigMediaWhitePointTag, &D50);
354 cmsWriteTag(hProphotoRGB, cmsSigMediaBlackPointTag, &black);
355
356 cmsWriteTag(hProphotoRGB, cmsSigRedColorantTag, (void*) &Colorants.Red);
357 cmsWriteTag(hProphotoRGB, cmsSigGreenColorantTag, (void*) &Colorants.Green);
358 cmsWriteTag(hProphotoRGB, cmsSigBlueColorantTag, (void*) &Colorants.Blue);
359
360 cmsWriteTag(hProphotoRGB, cmsSigRedTRCTag, (void*) transferFunction);
361 cmsLinkTag(hProphotoRGB, cmsSigGreenTRCTag, cmsSigRedTRCTag );
362 cmsLinkTag(hProphotoRGB, cmsSigBlueTRCTag, cmsSigRedTRCTag );
363
364 return hProphotoRGB;
365 }
366
367 // Create the ICC virtual profile for adobe rgb space
368 cmsHPROFILE
dt_colorspaces_create_betargb_profile(void)369 dt_colorspaces_create_betargb_profile(void)
370 {
371 cmsHPROFILE hBetaRGB;
372
373 cmsCIEXYZTRIPLE Colorants =
374 {
375 {0.6712537, 0.3032726, 0.0000000},
376 {0.1745834, 0.6637861, 0.0407010},
377 {0.1183829, 0.0329413, 0.7845090}
378 };
379
380 cmsCIEXYZ black = { 0, 0, 0 };
381 cmsCIEXYZ D65 = { 0.95045, 1, 1.08905 };
382 cmsToneCurve* transferFunction;
383
384 transferFunction = cmsBuildGamma(NULL, 2.2);
385
386 hBetaRGB = cmsCreateProfilePlaceholder(0);
387
388 cmsSetProfileVersion(hBetaRGB, 2.1);
389
390 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
391 cmsMLUsetASCII(mlu0, "en", "US", "Public Domain");
392 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
393 cmsMLUsetASCII(mlu1, "en", "US", "Beta RGB (compatible)");
394 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
395 cmsMLUsetASCII(mlu2, "en", "US", "darktable");
396 cmsMLU *mlu3 = cmsMLUalloc(NULL, 1);
397 cmsMLUsetASCII(mlu3, "en", "US", "Beta RGB");
398 // this will only be displayed when the embedded profile is read by for example GIMP
399 cmsWriteTag(hBetaRGB, cmsSigCopyrightTag, mlu0);
400 cmsWriteTag(hBetaRGB, cmsSigProfileDescriptionTag, mlu1);
401 cmsWriteTag(hBetaRGB, cmsSigDeviceMfgDescTag, mlu2);
402 cmsWriteTag(hBetaRGB, cmsSigDeviceModelDescTag, mlu3);
403 cmsMLUfree(mlu0);
404 cmsMLUfree(mlu1);
405 cmsMLUfree(mlu2);
406 cmsMLUfree(mlu3);
407
408 cmsSetDeviceClass(hBetaRGB, cmsSigDisplayClass);
409 cmsSetColorSpace(hBetaRGB, cmsSigRgbData);
410 cmsSetPCS(hBetaRGB, cmsSigXYZData);
411
412 cmsWriteTag(hBetaRGB, cmsSigMediaWhitePointTag, &D65);
413 cmsWriteTag(hBetaRGB, cmsSigMediaBlackPointTag, &black);
414
415 cmsWriteTag(hBetaRGB, cmsSigRedColorantTag, (void*) &Colorants.Red);
416 cmsWriteTag(hBetaRGB, cmsSigGreenColorantTag, (void*) &Colorants.Green);
417 cmsWriteTag(hBetaRGB, cmsSigBlueColorantTag, (void*) &Colorants.Blue);
418
419 cmsWriteTag(hBetaRGB, cmsSigRedTRCTag, (void*) transferFunction);
420 cmsLinkTag(hBetaRGB, cmsSigGreenTRCTag, cmsSigRedTRCTag );
421 cmsLinkTag(hBetaRGB, cmsSigBlueTRCTag, cmsSigRedTRCTag );
422
423 return hBetaRGB;
424 }
425
426
427 static cmsToneCurve*
build_linear_gamma(void)428 build_linear_gamma(void)
429 {
430 double Parameters[2];
431
432 Parameters[0] = 1.0;
433 Parameters[1] = 0;
434
435 return cmsBuildParametricToneCurve(0, 1, Parameters);
436 }
437
438 static float
cbrt_5f(float f)439 cbrt_5f(float f)
440 {
441 uint32_t* p = (uint32_t *) &f;
442 *p = *p/3 + 709921077;
443 return f;
444 }
445
446 static float
cbrta_halleyf(const float a,const float R)447 cbrta_halleyf(const float a, const float R)
448 {
449 const float a3 = a*a*a;
450 const float b = a * (a3 + R + R) / (a3 + a3 + R);
451 return b;
452 }
453
454 static float
lab_f(const float x)455 lab_f(const float x)
456 {
457 const float epsilon = 216.0f/24389.0f;
458 const float kappa = 24389.0f/27.0f;
459 if(x > epsilon)
460 {
461 // approximate cbrtf(x):
462 const float a = cbrt_5f(x);
463 return cbrta_halleyf(a, x);
464 }
465 else return (kappa*x + 16.0f)/116.0f;
466 }
467
468 void
dt_XYZ_to_Lab(const float * XYZ,float * Lab)469 dt_XYZ_to_Lab(const float *XYZ, float *Lab)
470 {
471 const float d50[3] = { 0.9642, 1.0, 0.8249 };
472 const float f[3] = { lab_f(XYZ[0]/d50[0]), lab_f(XYZ[1]/d50[1]), lab_f(XYZ[2]/d50[2]) };
473 Lab[0] = 116.0f * f[1] - 16.0f;
474 Lab[1] = 500.0f*(f[0] - f[1]);
475 Lab[2] = 200.0f*(f[1] - f[2]);
476 }
477
478 static float
lab_f_inv(const float x)479 lab_f_inv(const float x)
480 {
481 const float epsilon = 0.20689655172413796; // cbrtf(216.0f/24389.0f);
482 const float kappa = 24389.0f/27.0f;
483 if(x > epsilon) return x*x*x;
484 else return (116.0f*x - 16.0f)/kappa;
485 }
486
487 void
dt_Lab_to_XYZ(const float * Lab,float * XYZ)488 dt_Lab_to_XYZ(const float *Lab, float *XYZ)
489 {
490 const float d50[3] = { 0.9642, 1.0, 0.8249 };
491 const float fy = (Lab[0] + 16.0f)/116.0f;
492 const float fx = Lab[1]/500.0f + fy;
493 const float fz = fy - Lab[2]/200.0f;
494 XYZ[0] = d50[0]*lab_f_inv(fx);
495 XYZ[1] = d50[1]*lab_f_inv(fy);
496 XYZ[2] = d50[2]*lab_f_inv(fz);
497 }
498
499
500 int
dt_colorspaces_get_darktable_matrix(const char * makermodel,float * matrix)501 dt_colorspaces_get_darktable_matrix(const char *makermodel, float *matrix)
502 {
503 dt_profiled_colormatrix_t *preset = NULL;
504 for(int k=0; k<dt_profiled_colormatrix_cnt; k++)
505 {
506 if(!strcasecmp(makermodel, dt_profiled_colormatrices[k].makermodel))
507 {
508 preset = dt_profiled_colormatrices + k;
509 break;
510 }
511 }
512 if(!preset) return -1;
513
514 const float wxyz = preset->white[0]+ preset->white[1]+ preset->white[2];
515 const float rxyz = preset->rXYZ[0] + preset->rXYZ[1] + preset->rXYZ[2];
516 const float gxyz = preset->gXYZ[0] + preset->gXYZ[1] + preset->gXYZ[2];
517 const float bxyz = preset->bXYZ[0] + preset->bXYZ[1] + preset->bXYZ[2];
518
519 const float xn = preset->white[0]/wxyz;
520 const float yn = preset->white[1]/wxyz;
521 const float xr = preset->rXYZ[0]/rxyz;
522 const float yr = preset->rXYZ[1]/rxyz;
523 const float xg = preset->gXYZ[0]/gxyz;
524 const float yg = preset->gXYZ[1]/gxyz;
525 const float xb = preset->bXYZ[0]/bxyz;
526 const float yb = preset->bXYZ[1]/bxyz;
527
528 const float primaries[9] = {xr, xg, xb,
529 yr, yg, yb,
530 1.0f-xr-yr, 1.0f-xg-yg, 1.0f-xb-yb
531 };
532
533 float result[9];
534 if(mat3inv(result, primaries)) return -1;
535
536 const float whitepoint[3] = {xn/yn, 1.0f, (1.0f-xn-yn)/yn};
537 float coeff[3];
538
539 // get inverse primary whitepoint
540 mat3mulv(coeff, result, whitepoint);
541
542
543 float tmp[9] = { coeff[0]*xr, coeff[1]*xg, coeff[2]*xb,
544 coeff[0]*yr, coeff[1]*yg, coeff[2]*yb,
545 coeff[0]*(1.0f-xr-yr), coeff[1]*(1.0f-xg-yg), coeff[2]*(1.0f-xb-yb)
546 };
547
548 // input whitepoint[] in XYZ with Y normalized to 1.0f
549 const float dn[3] = { preset->white[0]/(float)preset->white[1], 1.0f, preset->white[2]/(float)preset->white[1]};
550 const float lam_rigg[9] = { 0.8951, 0.2664, -0.1614,
551 -0.7502, 1.7135, 0.0367,
552 0.0389, -0.0685, 1.0296
553 };
554 const float d50[3] = { 0.9642, 1.0, 0.8249 };
555
556
557 // adapt to d50
558 float chad_inv[9];
559 if(mat3inv(chad_inv, lam_rigg)) return -1;
560
561 float cone_src_rgb[3], cone_dst_rgb[3];
562 mat3mulv(cone_src_rgb, lam_rigg, dn);
563 mat3mulv(cone_dst_rgb, lam_rigg, d50);
564
565 const float cone[9] = { cone_dst_rgb[0]/cone_src_rgb[0], 0.0f, 0.0f,
566 0.0f, cone_dst_rgb[1]/cone_src_rgb[1], 0.0f,
567 0.0f, 0.0f, cone_dst_rgb[2]/cone_src_rgb[2]
568 };
569
570 float tmp2[9];
571 float bradford[9];
572 mat3mul(tmp2, cone, lam_rigg);
573 mat3mul(bradford, chad_inv, tmp2);
574
575 mat3mul(matrix, bradford, tmp);
576 return 0;
577 }
578
579 cmsHPROFILE
dt_colorspaces_create_alternate_profile(const char * makermodel)580 dt_colorspaces_create_alternate_profile(const char *makermodel)
581 {
582 dt_profiled_colormatrix_t *preset = NULL;
583 for(int k=0; k<dt_alternate_colormatrix_cnt; k++)
584 {
585 if(!strcmp(makermodel, dt_alternate_colormatrices[k].makermodel))
586 {
587 preset = dt_alternate_colormatrices + k;
588 break;
589 }
590 }
591 if(!preset) return NULL;
592
593 const float wxyz = preset->white[0]+preset->white[1]+preset->white[2];
594 const float rxyz = preset->rXYZ[0] +preset->rXYZ[1] +preset->rXYZ[2];
595 const float gxyz = preset->gXYZ[0] +preset->gXYZ[1] +preset->gXYZ[2];
596 const float bxyz = preset->bXYZ[0] +preset->bXYZ[1] +preset->bXYZ[2];
597 cmsCIExyY WP = {preset->white[0]/wxyz, preset->white[1]/wxyz, 1.0};
598 cmsCIExyYTRIPLE XYZPrimaries =
599 {
600 {preset->rXYZ[0]/rxyz, preset->rXYZ[1]/rxyz, 1.0},
601 {preset->gXYZ[0]/gxyz, preset->gXYZ[1]/gxyz, 1.0},
602 {preset->bXYZ[0]/bxyz, preset->bXYZ[1]/bxyz, 1.0}
603 };
604 cmsToneCurve *Gamma[3];
605 cmsHPROFILE hp;
606
607 Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma();
608
609 hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma);
610 cmsFreeToneCurve(Gamma[0]);
611 if (hp == NULL) return NULL;
612
613 char name[512];
614 snprintf(name, sizeof(name), "darktable alternate %s", makermodel);
615 cmsSetProfileVersion(hp, 2.1);
616 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
617 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
618 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
619 cmsMLUsetASCII(mlu1, "en", "US", name);
620 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
621 cmsMLUsetASCII(mlu2, "en", "US", name);
622 cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0);
623 cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1);
624 // this will only be displayed when the embedded profile is read by for example GIMP
625 cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2);
626 cmsMLUfree(mlu0);
627 cmsMLUfree(mlu1);
628 cmsMLUfree(mlu2);
629
630 return hp;
631 }
632
633 cmsHPROFILE
dt_colorspaces_create_vendor_profile(const char * makermodel)634 dt_colorspaces_create_vendor_profile(const char *makermodel)
635 {
636 dt_profiled_colormatrix_t *preset = NULL;
637 for(int k=0; k<dt_vendor_colormatrix_cnt; k++)
638 {
639 if(!strcmp(makermodel, dt_vendor_colormatrices[k].makermodel))
640 {
641 preset = dt_vendor_colormatrices + k;
642 break;
643 }
644 }
645 if(!preset) return NULL;
646
647 const float wxyz = preset->white[0]+preset->white[1]+preset->white[2];
648 const float rxyz = preset->rXYZ[0] +preset->rXYZ[1] +preset->rXYZ[2];
649 const float gxyz = preset->gXYZ[0] +preset->gXYZ[1] +preset->gXYZ[2];
650 const float bxyz = preset->bXYZ[0] +preset->bXYZ[1] +preset->bXYZ[2];
651 cmsCIExyY WP = {preset->white[0]/wxyz, preset->white[1]/wxyz, 1.0};
652 cmsCIExyYTRIPLE XYZPrimaries =
653 {
654 {preset->rXYZ[0]/rxyz, preset->rXYZ[1]/rxyz, 1.0},
655 {preset->gXYZ[0]/gxyz, preset->gXYZ[1]/gxyz, 1.0},
656 {preset->bXYZ[0]/bxyz, preset->bXYZ[1]/bxyz, 1.0}
657 };
658 cmsToneCurve *Gamma[3];
659 cmsHPROFILE hp;
660
661 Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma();
662
663 hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma);
664 cmsFreeToneCurve(Gamma[0]);
665 if (hp == NULL) return NULL;
666
667 char name[512];
668 snprintf(name, sizeof(name), "darktable vendor %s", makermodel);
669 cmsSetProfileVersion(hp, 2.1);
670 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
671 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
672 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
673 cmsMLUsetASCII(mlu1, "en", "US", name);
674 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
675 cmsMLUsetASCII(mlu2, "en", "US", name);
676 cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0);
677 cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1);
678 // this will only be displayed when the embedded profile is read by for example GIMP
679 cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2);
680 cmsMLUfree(mlu0);
681 cmsMLUfree(mlu1);
682 cmsMLUfree(mlu2);
683
684 return hp;
685 }
686
687 cmsHPROFILE
dt_colorspaces_create_darktable_profile(const char * makermodel)688 dt_colorspaces_create_darktable_profile(const char *makermodel)
689 {
690 dt_profiled_colormatrix_t *preset = NULL;
691 for(int k=0; k<dt_profiled_colormatrix_cnt; k++)
692 {
693 if(!strcasecmp(makermodel, dt_profiled_colormatrices[k].makermodel))
694 {
695 preset = dt_profiled_colormatrices + k;
696 break;
697 }
698 }
699 if(!preset) return NULL;
700
701 const float wxyz = preset->white[0]+preset->white[1]+preset->white[2];
702 const float rxyz = preset->rXYZ[0] +preset->rXYZ[1] +preset->rXYZ[2];
703 const float gxyz = preset->gXYZ[0] +preset->gXYZ[1] +preset->gXYZ[2];
704 const float bxyz = preset->bXYZ[0] +preset->bXYZ[1] +preset->bXYZ[2];
705 cmsCIExyY WP = {preset->white[0]/wxyz, preset->white[1]/wxyz, 1.0};
706 cmsCIExyYTRIPLE XYZPrimaries =
707 {
708 {preset->rXYZ[0]/rxyz, preset->rXYZ[1]/rxyz, 1.0},
709 {preset->gXYZ[0]/gxyz, preset->gXYZ[1]/gxyz, 1.0},
710 {preset->bXYZ[0]/bxyz, preset->bXYZ[1]/bxyz, 1.0}
711 };
712 cmsToneCurve *Gamma[3];
713 cmsHPROFILE hp;
714
715 Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma();
716
717 hp = cmsCreateRGBProfile(&WP, &XYZPrimaries, Gamma);
718 cmsFreeToneCurve(Gamma[0]);
719 if (hp == NULL) return NULL;
720
721 char name[512];
722 snprintf(name, sizeof(name), "Darktable profiled %s", makermodel);
723 cmsSetProfileVersion(hp, 2.1);
724 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
725 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
726 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
727 cmsMLUsetASCII(mlu1, "en", "US", name);
728 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
729 cmsMLUsetASCII(mlu2, "en", "US", name);
730 cmsWriteTag(hp, cmsSigDeviceMfgDescTag, mlu0);
731 cmsWriteTag(hp, cmsSigDeviceModelDescTag, mlu1);
732 // this will only be displayed when the embedded profile is read by for example GIMP
733 cmsWriteTag(hp, cmsSigProfileDescriptionTag, mlu2);
734 cmsMLUfree(mlu0);
735 cmsMLUfree(mlu1);
736 cmsMLUfree(mlu2);
737
738 return hp;
739 }
740
741 cmsHPROFILE
dt_colorspaces_create_xyz_profile(void)742 dt_colorspaces_create_xyz_profile(void)
743 {
744 cmsHPROFILE hXYZ = cmsCreateXYZProfile();
745 // revert some settings which prevent us from using XYZ as output profile:
746 cmsSetDeviceClass(hXYZ, cmsSigDisplayClass);
747 cmsSetColorSpace(hXYZ, cmsSigRgbData);
748 cmsSetPCS(hXYZ, cmsSigXYZData);
749 cmsSetHeaderRenderingIntent(hXYZ, INTENT_PERCEPTUAL);
750
751 if (hXYZ == NULL) return NULL;
752
753 cmsSetProfileVersion(hXYZ, 2.1);
754 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
755 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
756 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
757 cmsMLUsetASCII(mlu1, "en", "US", "linear XYZ");
758 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
759 cmsMLUsetASCII(mlu2, "en", "US", "Darktable linear XYZ");
760 cmsWriteTag(hXYZ, cmsSigDeviceMfgDescTag, mlu0);
761 cmsWriteTag(hXYZ, cmsSigDeviceModelDescTag, mlu1);
762 // this will only be displayed when the embedded profile is read by for example GIMP
763 cmsWriteTag(hXYZ, cmsSigProfileDescriptionTag, mlu2);
764 cmsMLUfree(mlu0);
765 cmsMLUfree(mlu1);
766 cmsMLUfree(mlu2);
767
768 return hXYZ;
769 }
770
771 cmsHPROFILE
dt_colorspaces_create_linear_rgb_profile(void)772 dt_colorspaces_create_linear_rgb_profile(void)
773 {
774 cmsCIExyY D65;
775 cmsCIExyYTRIPLE Rec709Primaries =
776 {
777 {0.6400, 0.3300, 1.0},
778 {0.3000, 0.6000, 1.0},
779 {0.1500, 0.0600, 1.0}
780 };
781 cmsToneCurve *Gamma[3];
782 cmsHPROFILE hsRGB;
783
784 cmsWhitePointFromTemp(&D65, 6504.0);
785 Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma();
786
787 hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma);
788 cmsFreeToneCurve(Gamma[0]);
789 if (hsRGB == NULL) return NULL;
790
791 cmsSetProfileVersion(hsRGB, 2.1);
792 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
793 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
794 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
795 cmsMLUsetASCII(mlu1, "en", "US", "linear rec709 rgb");
796 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
797 cmsMLUsetASCII(mlu2, "en", "US", "Darktable linear Rec709 RGB");
798 cmsWriteTag(hsRGB, cmsSigDeviceMfgDescTag, mlu0);
799 cmsWriteTag(hsRGB, cmsSigDeviceModelDescTag, mlu1);
800 // this will only be displayed when the embedded profile is read by for example GIMP
801 cmsWriteTag(hsRGB, cmsSigProfileDescriptionTag, mlu2);
802 cmsMLUfree(mlu0);
803 cmsMLUfree(mlu1);
804 cmsMLUfree(mlu2);
805
806 return hsRGB;
807 }
808
809 cmsHPROFILE
dt_colorspaces_create_linear_infrared_profile(void)810 dt_colorspaces_create_linear_infrared_profile(void)
811 {
812 // linear rgb with r and b swapped:
813 cmsCIExyY D65;
814 cmsCIExyYTRIPLE Rec709Primaries =
815 {
816 {0.1500, 0.0600, 1.0},
817 {0.3000, 0.6000, 1.0},
818 {0.6400, 0.3300, 1.0}
819 };
820 cmsToneCurve *Gamma[3];
821 cmsHPROFILE hsRGB;
822
823 cmsWhitePointFromTemp(&D65, 6504.0);
824 Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma();
825
826 hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma);
827 cmsFreeToneCurve(Gamma[0]);
828 if (hsRGB == NULL) return NULL;
829
830 cmsSetProfileVersion(hsRGB, 2.1);
831 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
832 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
833 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
834 cmsMLUsetASCII(mlu1, "en", "US", "linear infrared bgr");
835 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
836 cmsMLUsetASCII(mlu2, "en", "US", "Darktable Linear Infrared BGR");
837 cmsWriteTag(hsRGB, cmsSigDeviceMfgDescTag, mlu0);
838 cmsWriteTag(hsRGB, cmsSigDeviceModelDescTag, mlu1);
839 // this will only be displayed when the embedded profile is read by for example GIMP
840 cmsWriteTag(hsRGB, cmsSigProfileDescriptionTag, mlu2);
841 cmsMLUfree(mlu0);
842 cmsMLUfree(mlu1);
843 cmsMLUfree(mlu2);
844
845 return hsRGB;
846 }
847
848 /*
849 int
850 dt_colorspaces_find_profile(char *filename, size_t filename_len, const char *profile, const char *inout)
851 {
852 char datadir[DT_MAX_PATH_LEN];
853 dt_loc_get_user_config_dir(datadir, DT_MAX_PATH_LEN);
854 snprintf(filename, filename_len, "%s/color/%s/%s", datadir, inout, profile);
855 if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR))
856 {
857 dt_loc_get_datadir(datadir, DT_MAX_PATH_LEN);
858 snprintf(filename, filename_len, "%s/color/%s/%s", datadir, inout, profile);
859 if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) return 1;
860 }
861 return 0;
862 }
863
864
865 cmsHPROFILE
866 dt_colorspaces_create_output_profile(const int imgid)
867 {
868 // find the colorout module -- the pointer stays valid until darktable shuts down
869 static dt_iop_module_so_t *colorout = NULL;
870 if(colorout == NULL)
871 {
872 GList *modules = g_list_first(darktable.iop);
873 while(modules)
874 {
875 dt_iop_module_so_t *module = (dt_iop_module_so_t*)(modules->data);
876 if(!strcmp(module->op, "colorout"))
877 {
878 colorout = module;
879 break;
880 }
881 modules = g_list_next(modules);
882 }
883 }
884
885 char profile[1024];
886 profile[0] = '\0';
887 // db lookup colorout params, and dt_conf_() for override
888 gchar *overprofile = dt_conf_get_string("plugins/lighttable/export/iccprofile");
889 if(colorout && colorout->get_p && (!overprofile || !strcmp(overprofile, "image")))
890 {
891 sqlite3_stmt *stmt;
892 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT op_params FROM history WHERE imgid=?1 AND operation='colorout' ORDER BY num DESC LIMIT 1", -1, &stmt, NULL);
893 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
894 if(sqlite3_step(stmt) == SQLITE_ROW)
895 {
896 // use introspection to get the profile name from the binary params blob
897 const void *params = sqlite3_column_blob(stmt, 0);
898 char *iccprofile = colorout->get_p(params, "iccprofile");
899 g_strlcpy(profile, iccprofile, sizeof(profile));
900 }
901 sqlite3_finalize(stmt);
902 }
903 if(!overprofile && profile[0] == '\0')
904 {
905 g_strlcpy(profile, "sRGB", sizeof(profile));
906 }
907 else if(profile[0] == '\0')
908 {
909 g_strlcpy(profile, overprofile, sizeof(profile));
910 }
911
912 g_free(overprofile);
913
914 cmsHPROFILE output = NULL;
915
916 if(!strcmp(profile, "sRGB"))
917 output = dt_colorspaces_create_srgb_profile();
918 else if(!strcmp(profile, "linear_rgb"))
919 output = dt_colorspaces_create_linear_rgb_profile();
920 else if(!strcmp(profile, "XYZ"))
921 output = dt_colorspaces_create_xyz_profile();
922 else if(!strcmp(profile, "adobergb"))
923 output = dt_colorspaces_create_adobergb_profile();
924 else if(!strcmp(profile, "X profile"))
925 {
926 pthread_rwlock_rdlock(&darktable.control->xprofile_lock);
927 if(darktable.control->xprofile_data)
928 output = cmsOpenProfileFromMem(darktable.control->xprofile_data, darktable.control->xprofile_size);
929 pthread_rwlock_unlock(&darktable.control->xprofile_lock);
930 }
931 else
932 {
933 // else: load file name
934 char filename[DT_MAX_PATH_LEN];
935 dt_colorspaces_find_profile(filename, DT_MAX_PATH_LEN, profile, "out");
936 output = cmsOpenProfileFromFile(filename, "r");
937 }
938 if(!output) output = dt_colorspaces_create_srgb_profile();
939 return output;
940 }
941 */
942
943 cmsHPROFILE
dt_colorspaces_create_cmatrix_profile(float cmatrix[3][4])944 dt_colorspaces_create_cmatrix_profile(float cmatrix[3][4])
945 {
946 float mat[3][3];
947 // sRGB D65, the linear part:
948 const float rgb_to_xyz[3][3] =
949 {
950 {0.4124564, 0.3575761, 0.1804375},
951 {0.2126729, 0.7151522, 0.0721750},
952 {0.0193339, 0.1191920, 0.9503041}
953 };
954
955 for(int c=0; c<3; c++) for(int j=0; j<3; j++)
956 {
957 mat[c][j] = 0;
958 for(int k=0; k<3; k++) mat[c][j] += rgb_to_xyz[c][k]*cmatrix[k][j];
959 }
960 return dt_colorspaces_create_xyzmatrix_profile(mat);
961 }
962
963 cmsHPROFILE
dt_colorspaces_create_xyzimatrix_profile(float mat[3][3])964 dt_colorspaces_create_xyzimatrix_profile(float mat[3][3])
965 {
966 // mat: xyz -> cam
967 float imat[3][3];
968 mat3inv ((float *)imat, (float *)mat);
969 return dt_colorspaces_create_xyzmatrix_profile(imat);
970 }
971
972 cmsHPROFILE
dt_colorspaces_create_xyzmatrix_profile(float mat[3][3])973 dt_colorspaces_create_xyzmatrix_profile(float mat[3][3])
974 {
975 // mat: cam -> xyz
976 cmsCIExyY D65;
977 float x[3], y[3];
978 for(int k=0; k<3; k++)
979 {
980 const float norm = mat[0][k] + mat[1][k] + mat[2][k];
981 x[k] = mat[0][k] / norm;
982 y[k] = mat[1][k] / norm;
983 }
984 cmsCIExyYTRIPLE CameraPrimaries =
985 {
986 {x[0], y[0], 1.0},
987 {x[1], y[1], 1.0},
988 {x[2], y[2], 1.0}
989 };
990 cmsHPROFILE cmat;
991
992 cmsWhitePointFromTemp(&D65, 6504.0);
993
994 cmsToneCurve *Gamma[3];
995 Gamma[0] = Gamma[1] = Gamma[2] = build_linear_gamma();
996 cmat = cmsCreateRGBProfile(&D65, &CameraPrimaries, Gamma);
997 if (cmat == NULL) return NULL;
998 cmsFreeToneCurve(Gamma[0]);
999
1000 cmsSetProfileVersion(cmat, 2.1);
1001 cmsMLU *mlu0 = cmsMLUalloc(NULL, 1);
1002 cmsMLUsetASCII(mlu0, "en", "US", "(dt internal)");
1003 cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
1004 cmsMLUsetASCII(mlu1, "en", "US", "color matrix built-in");
1005 cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
1006 cmsMLUsetASCII(mlu2, "en", "US", "color matrix built-in");
1007 cmsWriteTag(cmat, cmsSigDeviceMfgDescTag, mlu0);
1008 cmsWriteTag(cmat, cmsSigDeviceModelDescTag, mlu1);
1009 // this will only be displayed when the embedded profile is read by for example GIMP
1010 cmsWriteTag(cmat, cmsSigProfileDescriptionTag, mlu2);
1011 cmsMLUfree(mlu0);
1012 cmsMLUfree(mlu1);
1013 cmsMLUfree(mlu2);
1014
1015 return cmat;
1016 }
1017
1018 void
dt_colorspaces_cleanup_profile(cmsHPROFILE p)1019 dt_colorspaces_cleanup_profile(cmsHPROFILE p)
1020 {
1021 cmsCloseProfile(p);
1022 }
1023
1024 void
dt_colorspaces_get_makermodel(char * makermodel,size_t makermodel_len,const char * const maker,const char * const model)1025 dt_colorspaces_get_makermodel(char *makermodel, size_t makermodel_len, const char *const maker, const char *const model)
1026 {
1027 // if first word in maker == first word in model, use just model.
1028 const char *c, *d;
1029 char *e;
1030 c = maker;
1031 d = model;
1032 int match = 1;
1033 while(*c != ' ' && c < maker + strlen(maker)) if(*(c++) != *(d++))
1034 {
1035 match = 0;
1036 break;
1037 }
1038 if(match)
1039 {
1040 snprintf(makermodel, makermodel_len, "%s", model);
1041 }
1042 else
1043 {
1044 // else need to append first word of the maker:
1045 c = maker;
1046 d = model;
1047 for(e=makermodel; c<maker+strlen(maker) && *c != ' '; c++,e++) *e = *c;
1048 // separate with space
1049 *(e++) = ' ';
1050 // and continue with model.
1051 // replace MAXXUM with DYNAX for wb presets.
1052 if(!strcmp(maker, "MINOLTA") && !strncmp(model, "MAXXUM", 6))
1053 snprintf(e, makermodel_len - (d-maker), "DYNAX %s", model+7);
1054 // need FinePix gone from some fuji cameras to match dcraws description:
1055 else if(!strncmp(model, "FinePix", 7))
1056 snprintf(e, makermodel_len - (d-maker), "%s", model + 8);
1057 else snprintf(e, makermodel_len - (d-maker), "%s", model);
1058 }
1059 // strip trailing spaces
1060 e = makermodel + strlen(makermodel) - 1;
1061 while (e > makermodel && *e == ' ') e--;
1062 e[1] = '\0';
1063 }
1064
1065 void
dt_colorspaces_get_makermodel_split(char * makermodel,size_t makermodel_len,char ** modelo,const char * const maker,const char * const model)1066 dt_colorspaces_get_makermodel_split(char *makermodel, size_t makermodel_len, char **modelo, const char *const maker, const char *const model)
1067 {
1068 dt_colorspaces_get_makermodel(makermodel, makermodel_len, maker, model);
1069 *modelo = makermodel;
1070 for(; **modelo != ' ' && *modelo < makermodel + strlen(makermodel); (*modelo)++);
1071 **modelo = '\0';
1072 (*modelo)++;
1073 }
1074
1075 void
dt_colorspaces_get_profile_name(cmsHPROFILE p,const char * language,const char * country,char * name,size_t len)1076 dt_colorspaces_get_profile_name(cmsHPROFILE p, const char *language, const char *country, char *name, size_t len)
1077 {
1078 cmsUInt32Number size;
1079 gchar *buf = NULL;
1080 wchar_t *wbuf = NULL;
1081 gchar *utf8 = NULL;
1082
1083 size = cmsGetProfileInfoASCII(p, cmsInfoDescription, language, country, NULL, 0);
1084 if(size == 0)
1085 goto error;
1086
1087 buf = (char*)malloc(sizeof(char) * (size + 1));
1088 size = cmsGetProfileInfoASCII(p, cmsInfoDescription, language, country, buf, size);
1089 if(size == 0)
1090 goto error;
1091
1092 // most unix like systems should work with this, but at least Windows doesn't
1093 if(sizeof(wchar_t) != 4 || g_utf8_validate(buf, -1, NULL))
1094 g_strlcpy(name, buf, len); // better a little weird than totally borked
1095 else
1096 {
1097 wbuf = (wchar_t*)malloc(sizeof(wchar_t) * (size + 1));
1098 size = cmsGetProfileInfo(p, cmsInfoDescription, language, country, wbuf, sizeof(wchar_t) * size);
1099 if(size == 0)
1100 goto error;
1101 utf8 = g_ucs4_to_utf8((gunichar*)wbuf, -1, NULL, NULL, NULL);
1102 if(!utf8)
1103 goto error;
1104 g_strlcpy(name, utf8, len);
1105 }
1106
1107 free(buf);
1108 free(wbuf);
1109 g_free(utf8);
1110 return;
1111
1112 error:
1113 if(buf)
1114 g_strlcpy(name, buf, len); // better a little weird than totally borked
1115 else
1116 *name = '\0'; // nothing to do here
1117 free(buf);
1118 free(wbuf);
1119 g_free(utf8);
1120 }
1121
rgb2hsl(const float rgb[3],float * h,float * s,float * l)1122 void rgb2hsl(const float rgb[3],float *h,float *s,float *l)
1123 {
1124 const float r=rgb[0], g=rgb[1], b=rgb[2];
1125 float pmax=fmax(r,fmax(g,b));
1126 float pmin=fmin(r,fmin(g,b));
1127 float delta=(pmax-pmin);
1128
1129 float hv=0,sv=0,lv=(pmin+pmax)/2.0;
1130
1131 if(pmax!=pmin)
1132 {
1133 sv=lv<0.5?delta/(pmax+pmin):delta/(2.0-pmax-pmin);
1134
1135 if(pmax==r) hv=(g-b)/delta;
1136 else if(pmax==g) hv=2.0+(b-r)/delta;
1137 else if(pmax==b) hv=4.0+(r-g)/delta;
1138 hv/=6.0;
1139 if(hv<0.0) hv+=1.0;
1140 else if(hv>1.0) hv-=1.0;
1141 }
1142 *h=hv;
1143 *s=sv;
1144 *l=lv;
1145 }
1146
hue2rgb(float m1,float m2,float hue)1147 static inline float hue2rgb(float m1,float m2,float hue)
1148 {
1149 if(hue<0.0) hue+=1.0;
1150 else if(hue>1.0) hue-=1.0;
1151
1152 if( hue < 1.0/6.0) return (m1+(m2-m1)*hue*6.0);
1153 else if(hue < 1.0/2.0) return m2;
1154 else if(hue < 2.0/3.0) return (m1+(m2-m1)*((2.0/3.0)-hue)*6.0);
1155 else return m1;
1156 }
1157
hsl2rgb(float rgb[3],float h,float s,float l)1158 void hsl2rgb(float rgb[3],float h,float s,float l)
1159 {
1160 float m1,m2;
1161 if( s==0)
1162 {
1163 rgb[0]=rgb[1]=rgb[2]=l;
1164 return;
1165 }
1166 m2=l<0.5?l*(1.0+s):l+s-l*s;
1167 m1=(2.0*l-m2);
1168 rgb[0] = hue2rgb(m1,m2,h + (1.0/3.0));
1169 rgb[1] = hue2rgb(m1,m2,h);
1170 rgb[2] = hue2rgb(m1,m2,h - (1.0/3.0));
1171
1172 }
1173
1174 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
1175 // vim: shiftwidth=2 expandtab tabstop=2 cindent
1176 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;
1177