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