Update GitHub actions file
[geeqie.git] / src / color-man.cc
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "color-man.h"
24
25 #include "image.h"
26 #include "ui-fileops.h"
27
28
29 #ifdef HAVE_LCMS
30 /*** color support enabled ***/
31
32 #ifdef HAVE_LCMS2
33 #include <lcms2.h>
34 #else
35 #include <lcms.h>
36 #endif
37
38
39 typedef struct _ColorManCache ColorManCache;
40 struct _ColorManCache {
41         cmsHPROFILE   profile_in;
42         cmsHPROFILE   profile_out;
43         cmsHTRANSFORM transform;
44
45         ColorManProfileType profile_in_type;
46         gchar *profile_in_file;
47
48         ColorManProfileType profile_out_type;
49         gchar *profile_out_file;
50
51         gboolean has_alpha;
52
53         gint refcount;
54 };
55
56 /* pixels to transform per idle call */
57 #define COLOR_MAN_CHUNK_SIZE 81900
58
59
60 static void color_man_lib_init(void)
61 {
62         static gboolean init_done = FALSE;
63
64         if (init_done) return;
65         init_done = TRUE;
66
67 #ifndef HAVE_LCMS2
68         cmsErrorAction(LCMS_ERROR_IGNORE);
69 #endif
70 }
71
72 static cmsHPROFILE color_man_create_adobe_comp(void)
73 {
74         /* ClayRGB1998 is AdobeRGB compatible */
75 #include "ClayRGB1998_icc.h"
76         return cmsOpenProfileFromMem(ClayRGB1998_icc, ClayRGB1998_icc_len);
77 }
78
79 /*
80  *-------------------------------------------------------------------
81  * color transform cache
82  *-------------------------------------------------------------------
83  */
84
85 static GList *cm_cache_list = NULL;
86
87
88 static void color_man_cache_ref(ColorManCache *cc)
89 {
90         if (!cc) return;
91
92         cc->refcount++;
93 }
94
95 static void color_man_cache_unref(ColorManCache *cc)
96 {
97         if (!cc) return;
98
99         cc->refcount--;
100         if (cc->refcount < 1)
101                 {
102                 if (cc->transform) cmsDeleteTransform(cc->transform);
103                 if (cc->profile_in) cmsCloseProfile(cc->profile_in);
104                 if (cc->profile_out) cmsCloseProfile(cc->profile_out);
105
106                 g_free(cc->profile_in_file);
107                 g_free(cc->profile_out_file);
108
109                 g_free(cc);
110                 }
111 }
112
113 static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file,
114                                                 guchar *data, guint data_len)
115 {
116         cmsHPROFILE profile = NULL;
117
118         switch (type)
119                 {
120                 case COLOR_PROFILE_FILE:
121                         if (file)
122                                 {
123                                 gchar *pathl;
124
125                                 pathl = path_from_utf8(file);
126                                 profile = cmsOpenProfileFromFile(pathl, "r");
127                                 g_free(pathl);
128                                 }
129                         break;
130                 case COLOR_PROFILE_SRGB:
131                         profile = cmsCreate_sRGBProfile();
132                         break;
133                 case COLOR_PROFILE_ADOBERGB:
134                         profile = color_man_create_adobe_comp();
135                         break;
136                 case COLOR_PROFILE_MEM:
137                         if (data)
138                                 {
139                                 profile = cmsOpenProfileFromMem(data, data_len);
140                                 }
141                         break;
142                 case COLOR_PROFILE_NONE:
143                 default:
144                         break;
145                 }
146
147         return profile;
148 }
149
150 static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
151                                           guchar *in_data, guint in_data_len,
152                                           ColorManProfileType out_type, const gchar *out_file,
153                                           guchar *out_data, guint out_data_len,
154                                           gboolean has_alpha)
155 {
156         ColorManCache *cc;
157
158         color_man_lib_init();
159
160         cc = g_new0(ColorManCache, 1);
161         cc->refcount = 1;
162
163         cc->profile_in_type = in_type;
164         cc->profile_in_file = g_strdup(in_file);
165
166         cc->profile_out_type = out_type;
167         cc->profile_out_file = g_strdup(out_file);
168
169         cc->has_alpha = has_alpha;
170
171         cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file,
172                                                       in_data, in_data_len);
173         cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file,
174                                                        out_data, out_data_len);
175
176         if (!cc->profile_in || !cc->profile_out)
177                 {
178                 DEBUG_1("failed to load color profile for %s: %d %s",
179                                   (!cc->profile_in) ? "input" : "screen",
180                                   (!cc->profile_in) ? cc->profile_in_type : cc->profile_out_type,
181                                   (!cc->profile_in) ? cc->profile_in_file : cc->profile_out_file);
182
183                 color_man_cache_unref(cc);
184                 return NULL;
185                 }
186
187         cc->transform = cmsCreateTransform(cc->profile_in,
188                                            (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
189                                            cc->profile_out,
190                                            (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
191                                            options->color_profile.render_intent, 0);
192
193         if (!cc->transform)
194                 {
195                 DEBUG_1("failed to create color profile transform");
196
197                 color_man_cache_unref(cc);
198                 return NULL;
199                 }
200
201         if (cc->profile_in_type != COLOR_PROFILE_MEM && cc->profile_out_type != COLOR_PROFILE_MEM )
202                 {
203                 cm_cache_list = g_list_append(cm_cache_list, cc);
204                 color_man_cache_ref(cc);
205                 }
206
207         return cc;
208 }
209
210 static void color_man_cache_free(ColorManCache *cc)
211 {
212         if (!cc) return;
213
214         cm_cache_list = g_list_remove(cm_cache_list, cc);
215         color_man_cache_unref(cc);
216 }
217
218 static void color_man_cache_reset(void)
219 {
220         while (cm_cache_list)
221                 {
222                 ColorManCache *cc;
223
224                 cc = static_cast<ColorManCache *>(cm_cache_list->data);
225                 color_man_cache_free(cc);
226                 }
227 }
228
229 static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gchar *in_file,
230                                            ColorManProfileType out_type, const gchar *out_file,
231                                            gboolean has_alpha)
232 {
233         GList *work;
234
235         work = cm_cache_list;
236         while (work)
237                 {
238                 ColorManCache *cc;
239                 gboolean match = FALSE;
240
241                 cc = static_cast<ColorManCache *>(work->data);
242                 work = work->next;
243
244                 if (cc->profile_in_type == in_type &&
245                     cc->profile_out_type == out_type &&
246                     cc->has_alpha == has_alpha)
247                         {
248                         match = TRUE;
249                         }
250
251                 if (match && cc->profile_in_type == COLOR_PROFILE_FILE)
252                         {
253                         match = (cc->profile_in_file && in_file &&
254                                  strcmp(cc->profile_in_file, in_file) == 0);
255                         }
256                 if (match && cc->profile_out_type == COLOR_PROFILE_FILE)
257                         {
258                         match = (cc->profile_out_file && out_file &&
259                                  strcmp(cc->profile_out_file, out_file) == 0);
260                         }
261
262                 if (match) return cc;
263                 }
264
265         return NULL;
266 }
267
268 static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
269                                           guchar *in_data, guint in_data_len,
270                                           ColorManProfileType out_type, const gchar *out_file,
271                                           guchar *out_data, guint out_data_len,
272                                           gboolean has_alpha)
273 {
274         ColorManCache *cc;
275
276         cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
277         if (cc)
278                 {
279                 color_man_cache_ref(cc);
280                 return cc;
281                 }
282
283         return color_man_cache_new(in_type, in_file, in_data, in_data_len,
284                                    out_type, out_file, out_data, out_data_len, has_alpha);
285 }
286
287
288 /*
289  *-------------------------------------------------------------------
290  * color manager
291  *-------------------------------------------------------------------
292  */
293
294 //static void color_man_done(ColorMan *cm, ColorManReturnType type)
295 //{
296         //if (cm->func_done)
297                 //{
298                 //cm->func_done(cm, type, cm->func_done_data);
299                 //}
300 //}
301
302 void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
303 {
304         ColorManCache *cc;
305         guchar *pix;
306         gint rs;
307         gint i;
308         gint pixbuf_width, pixbuf_height;
309
310
311         pixbuf_width = gdk_pixbuf_get_width(pixbuf);
312         pixbuf_height = gdk_pixbuf_get_height(pixbuf);
313
314         cc = static_cast<ColorManCache *>(cm->profile);
315
316         pix = gdk_pixbuf_get_pixels(pixbuf);
317         rs = gdk_pixbuf_get_rowstride(pixbuf);
318
319         /** @FIXME: x,y expected to be = 0. Maybe this is not the right place for scaling */
320         w = w * scale_factor();
321         h = h * scale_factor();
322
323         w = MIN(w, pixbuf_width - x);
324         h = MIN(h, pixbuf_height - y);
325
326         pix += x * ((cc->has_alpha) ? 4 : 3);
327         for (i = 0; i < h; i++)
328                 {
329                 guchar *pbuf;
330
331                 pbuf = pix + ((y + i) * rs);
332
333                 cmsDoTransform(cc->transform, pbuf, pbuf, w);
334                 }
335
336 }
337
338 //static gboolean color_man_idle_cb(gpointer data)
339 //{
340         //ColorMan *cm = static_cast<ColorMan *>(data);
341         //gint width, height;
342         //gint rh;
343
344         //if (!cm->pixbuf) return FALSE;
345
346         //if (cm->imd &&
347             //cm->pixbuf != image_get_pixbuf(cm->imd))
348                 //{
349                 //cm->idle_id = 0;
350                 //color_man_done(cm, COLOR_RETURN_IMAGE_CHANGED);
351                 //return FALSE;
352                 //}
353
354         //width = gdk_pixbuf_get_width(cm->pixbuf);
355         //height = gdk_pixbuf_get_height(cm->pixbuf);
356
357         //if (cm->row > height)
358                 //{
359                 //if (!cm->incremental_sync && cm->imd)
360                         //{
361                         //image_area_changed(cm->imd, 0, 0, width, height);
362                         //}
363
364                 //cm->idle_id = 0;
365                 //color_man_done(cm, COLOR_RETURN_SUCCESS);
366                 //return FALSE;
367                 //}
368
369         //rh = COLOR_MAN_CHUNK_SIZE / width + 1;
370         //color_man_correct_region(cm, cm->pixbuf, 0, cm->row, width, rh);
371         //if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, 0, cm->row, width, rh);
372         //cm->row += rh;
373
374         //return TRUE;
375 //}
376
377 static ColorMan *color_man_new_real(ImageWindow *imd, GdkPixbuf *pixbuf,
378                                     ColorManProfileType input_type, const gchar *input_file,
379                                     guchar *input_data, guint input_data_len,
380                                     ColorManProfileType screen_type, const gchar *screen_file,
381                                     guchar *screen_data, guint screen_data_len)
382 {
383         ColorMan *cm;
384         gboolean has_alpha;
385
386         if (imd) pixbuf = image_get_pixbuf(imd);
387
388         cm = g_new0(ColorMan, 1);
389         cm->imd = imd;
390         cm->pixbuf = pixbuf;
391         if (cm->pixbuf) g_object_ref(cm->pixbuf);
392
393         has_alpha = pixbuf ? gdk_pixbuf_get_has_alpha(pixbuf) : FALSE;
394
395         cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len,
396                                           screen_type, screen_file, screen_data, screen_data_len, has_alpha);
397         if (!cm->profile)
398                 {
399                 color_man_free(cm);
400                 return NULL;
401                 }
402
403         return cm;
404 }
405
406 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
407                         ColorManProfileType input_type, const gchar *input_file,
408                         ColorManProfileType screen_type, const gchar *screen_file,
409                         guchar *screen_data, guint screen_data_len)
410 {
411         return color_man_new_real(imd, pixbuf,
412                                   input_type, input_file, NULL, 0,
413                                   screen_type, screen_file, screen_data, screen_data_len);
414 }
415
416 //void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
417 //{
418         //cm->func_done = done_func;
419         //cm->func_done_data = done_data;
420         //cm->idle_id = g_idle_add(color_man_idle_cb, cm);
421 //}
422
423 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
424                                  guchar *input_data, guint input_data_len,
425                                  ColorManProfileType screen_type, const gchar *screen_file,
426                                  guchar *screen_data, guint screen_data_len)
427 {
428         return color_man_new_real(imd, pixbuf,
429                                   COLOR_PROFILE_MEM, NULL, input_data, input_data_len,
430                                   screen_type, screen_file, screen_data, screen_data_len);
431 }
432
433 static gchar *color_man_get_profile_name(ColorManProfileType type, cmsHPROFILE profile)
434 {
435         switch (type)
436                 {
437                 case COLOR_PROFILE_SRGB:
438                         return g_strdup(_("sRGB"));
439                 case COLOR_PROFILE_ADOBERGB:
440                         return g_strdup(_("Adobe RGB compatible"));
441                         break;
442                 case COLOR_PROFILE_MEM:
443                 case COLOR_PROFILE_FILE:
444                         if (profile)
445                                 {
446 #ifdef HAVE_LCMS2
447                                 char buffer[20];
448                                 buffer[0] = '\0';
449                                 cmsGetProfileInfoASCII(profile, cmsInfoDescription, "en", "US", buffer, 20);
450                                 buffer[19] = '\0'; /* Just to be sure */
451                                 return g_strdup(buffer);
452 #else
453                                 return g_strdup(cmsTakeProductName(profile));
454 #endif
455                                 }
456                         return g_strdup(_("Custom profile"));
457                         break;
458                 case COLOR_PROFILE_NONE:
459                 default:
460                         return g_strdup("");
461                 }
462 }
463
464 gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile)
465 {
466         ColorManCache *cc;
467         if (!cm) return FALSE;
468
469         cc = static_cast<ColorManCache *>(cm->profile);
470
471         if (image_profile) *image_profile = color_man_get_profile_name(cc->profile_in_type, cc->profile_in);
472         if (screen_profile) *screen_profile = color_man_get_profile_name(cc->profile_out_type, cc->profile_out);
473         return TRUE;
474 }
475
476 void color_man_free(ColorMan *cm)
477 {
478         if (!cm) return;
479
480         if (cm->idle_id) g_source_remove(cm->idle_id);
481         if (cm->pixbuf) g_object_unref(cm->pixbuf);
482
483         color_man_cache_unref(static_cast<ColorManCache *>(cm->profile));
484
485         g_free(cm);
486 }
487
488 void color_man_update(void)
489 {
490         color_man_cache_reset();
491 }
492
493 #ifdef HAVE_HEIF
494 #include <libheif/heif.h>
495 #include <math.h>
496
497 static cmsToneCurve* colorspaces_create_transfer(int32_t size, double (*fct)(double))
498 {
499         float *values = static_cast<float *>(g_malloc(sizeof(float) * size));
500
501         for(int32_t i = 0; i < size; ++i)
502                 {
503                 const double x = (float)i / (size - 1);
504                 const double y = MIN(fct(x), 1.0f);
505                 values[i] = (float)y;
506                 }
507
508         cmsToneCurve* result = cmsBuildTabulatedToneCurveFloat(NULL, size, values);
509         g_free(values);
510         return result;
511 }
512
513 // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-F.pdf
514 // Hybrid Log-Gamma
515 static double HLG_fct(double x)
516 {
517         static const double Beta  = 0.04;
518         static const double RA    = 5.591816309728916; // 1.0 / A where A = 0.17883277
519         static const double B     = 0.28466892; // 1.0 - 4.0 * A
520         static const double C     = 0.5599107295; // 0,5 â€“aln(4a)
521
522         double e = MAX(x * (1.0 - Beta) + Beta, 0.0);
523
524         if(e == 0.0) return 0.0;
525
526         const double sign = e;
527         e = fabs(e);
528
529         double res = 0.0;
530
531         if(e <= 0.5)
532                 {
533                 res = e * e / 3.0;
534                 }
535         else
536                 {
537                 res = (exp((e - C) * RA) + B) / 12.0;
538                 }
539
540         return copysign(res, sign);
541 }
542
543 static double PQ_fct(double x)
544 {
545         static const double M1 = 2610.0 / 16384.0;
546         static const double M2 = (2523.0 / 4096.0) * 128.0;
547         static const double C1 = 3424.0 / 4096.0;
548         static const double C2 = (2413.0 / 4096.0) * 32.0;
549         static const double C3 = (2392.0 / 4096.0) * 32.0;
550
551         if(x == 0.0) return 0.0;
552         const double sign = x;
553         x = fabs(x);
554
555         const double xpo = pow(x, 1.0 / M2);
556         const double num = MAX(xpo - C1, 0.0);
557         const double den = C2 - C3 * xpo;
558         const double res = pow(num / den, 1.0 / M1);
559
560         return copysign(res, sign);
561 }
562
563 /**
564  * @brief
565  * @param nclx
566  * @param profile_len
567  * @returns
568  *
569  * Copied from: gimp/libgimpcolor/gimpcolorprofile.c
570  */
571 static guchar *nclx_to_lcms_profile(const struct heif_color_profile_nclx *nclx, guint *profile_len)
572 {
573         const gchar *primaries_name = "";
574         const gchar *trc_name = "";
575         cmsHPROFILE *profile = NULL;
576         cmsCIExyY whitepoint;
577         cmsCIExyYTRIPLE primaries;
578         cmsToneCurve *curve[3];
579         cmsUInt32Number size;
580         guint8 *data = NULL;
581
582         cmsFloat64Number srgb_parameters[5] =
583         { 2.4, 1.0 / 1.055,  0.055 / 1.055, 1.0 / 12.92, 0.04045 };
584
585         cmsFloat64Number rec709_parameters[5] =
586         { 2.2, 1.0 / 1.099,  0.099 / 1.099, 1.0 / 4.5, 0.081 };
587
588         if (nclx == NULL)
589                 {
590                 return NULL;
591                 }
592
593         if (nclx->color_primaries == heif_color_primaries_unspecified)
594                 {
595                 return NULL;
596                 }
597
598         whitepoint.x = nclx->color_primary_white_x;
599         whitepoint.y = nclx->color_primary_white_y;
600         whitepoint.Y = 1.0f;
601
602         primaries.Red.x = nclx->color_primary_red_x;
603         primaries.Red.y = nclx->color_primary_red_y;
604         primaries.Red.Y = 1.0f;
605
606         primaries.Green.x = nclx->color_primary_green_x;
607         primaries.Green.y = nclx->color_primary_green_y;
608         primaries.Green.Y = 1.0f;
609
610         primaries.Blue.x = nclx->color_primary_blue_x;
611         primaries.Blue.y = nclx->color_primary_blue_y;
612         primaries.Blue.Y = 1.0f;
613
614         switch (nclx->color_primaries)
615                 {
616                 case heif_color_primaries_ITU_R_BT_709_5:
617                         primaries_name = "BT.709";
618                         break;
619                 case   heif_color_primaries_ITU_R_BT_470_6_System_M:
620                         primaries_name = "BT.470-6 System M";
621                         break;
622                 case heif_color_primaries_ITU_R_BT_470_6_System_B_G:
623                         primaries_name = "BT.470-6 System BG";
624                         break;
625                 case heif_color_primaries_ITU_R_BT_601_6:
626                         primaries_name = "BT.601";
627                         break;
628                 case heif_color_primaries_SMPTE_240M:
629                         primaries_name = "SMPTE 240M";
630                         break;
631                 case heif_color_primaries_generic_film:
632                         primaries_name = "Generic film";
633                         break;
634                 case heif_color_primaries_ITU_R_BT_2020_2_and_2100_0:
635                         primaries_name = "BT.2020";
636                         break;
637                 case heif_color_primaries_SMPTE_ST_428_1:
638                         primaries_name = "SMPTE ST 428-1";
639                         break;
640                 case heif_color_primaries_SMPTE_RP_431_2:
641                         primaries_name = "SMPTE RP 431-2";
642                         break;
643                 case heif_color_primaries_SMPTE_EG_432_1:
644                         primaries_name = "SMPTE EG 432-1 (DCI P3)";
645                         break;
646                 case heif_color_primaries_EBU_Tech_3213_E:
647                         primaries_name = "EBU Tech. 3213-E";
648                         break;
649                 default:
650                         log_printf("nclx unsupported color_primaries value: %d\n", nclx->color_primaries);
651                         return NULL;
652                         break;
653                 }
654
655         DEBUG_1("nclx primaries: %s: ", primaries_name);
656
657         switch (nclx->transfer_characteristics)
658                 {
659                 case heif_transfer_characteristic_ITU_R_BT_709_5:
660                         curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve(NULL, 4, rec709_parameters);
661                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
662                         cmsFreeToneCurve(curve[0]);
663                         trc_name = "Rec709 RGB";
664                         break;
665                 case heif_transfer_characteristic_ITU_R_BT_470_6_System_M:
666                         curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.2f);
667                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
668                         cmsFreeToneCurve(curve[0]);
669                         trc_name = "Gamma2.2 RGB";
670                         break;
671                 case heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G:
672                         curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.8f);
673                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
674                         cmsFreeToneCurve(curve[0]);
675                         trc_name = "Gamma2.8 RGB";
676                         break;
677                 case heif_transfer_characteristic_linear:
678                         curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0f);
679                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
680                         cmsFreeToneCurve(curve[0]);
681                         trc_name = "linear RGB";
682                         break;
683                 case heif_transfer_characteristic_ITU_R_BT_2100_0_HLG:
684                         curve[0] = curve[1] = curve[2] = colorspaces_create_transfer(4096, HLG_fct);
685                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
686                         cmsFreeToneCurve(curve[0]);
687                         trc_name = "HLG Rec2020 RGB";
688                         break;
689                 case heif_transfer_characteristic_ITU_R_BT_2100_0_PQ:
690                         curve[0] = curve[1] = curve[2] = colorspaces_create_transfer(4096, PQ_fct);
691                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
692                         cmsFreeToneCurve(curve[0]);
693                         trc_name = "PQ Rec2020 RGB";
694                         break;
695                 case heif_transfer_characteristic_IEC_61966_2_1:
696                 /* same as default */
697                 default:
698                         curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters);
699                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
700                         cmsFreeToneCurve(curve[0]);
701                         trc_name = "sRGB-TRC RGB";
702                         break;
703                 }
704
705         DEBUG_1("nclx transfer characteristic: %s", trc_name);
706
707         if (profile)
708                 {
709                 if (cmsSaveProfileToMem(profile, NULL, &size))
710                         {
711                         data = static_cast<guint8 *>(g_malloc(size));
712                         if (cmsSaveProfileToMem(profile, data, &size))
713                                 {
714                                 *profile_len = size;
715                                 }
716                         cmsCloseProfile(profile);
717                         return (guchar *)data;
718                         }
719                 else
720                         {
721                         cmsCloseProfile(profile);
722                         return NULL;
723                         }
724                 }
725         else
726                 {
727                 return NULL;
728                 }
729 }
730
731 guchar *heif_color_profile(FileData *fd, guint *profile_len)
732 {
733         struct heif_context* ctx;
734         struct heif_error error_code;
735         struct heif_image_handle* handle;
736         struct heif_color_profile_nclx *nclxcp;
737         gint profile_type;
738         guchar *profile;
739         cmsUInt32Number size;
740         guint8 *data = NULL;
741
742         ctx = heif_context_alloc();
743         error_code = heif_context_read_from_file(ctx, fd->path, NULL);
744
745         if (error_code.code)
746                 {
747                 log_printf("warning: heif reader error: %s\n", error_code.message);
748                 heif_context_free(ctx);
749                 return NULL;
750                 }
751
752         error_code = heif_context_get_primary_image_handle(ctx, &handle);
753         if (error_code.code)
754                 {
755                 log_printf("warning: heif reader error: %s\n", error_code.message);
756                 heif_context_free(ctx);
757                 return NULL;
758                 }
759
760         nclxcp = heif_nclx_color_profile_alloc();
761         profile_type = heif_image_handle_get_color_profile_type(handle);
762
763         if (profile_type == heif_color_profile_type_prof)
764                 {
765                 size = heif_image_handle_get_raw_color_profile_size(handle);
766                 *profile_len = size;
767                 data = static_cast<guint8 *>(g_malloc0(size));
768                 error_code = heif_image_handle_get_raw_color_profile(handle, data);
769                 if (error_code.code)
770                         {
771                         log_printf("warning: heif reader error: %s\n", error_code.message);
772                         heif_context_free(ctx);
773                         heif_nclx_color_profile_free(nclxcp);
774                         return NULL;
775                         }
776
777                 DEBUG_1("heif color profile type: prof");
778                 heif_context_free(ctx);
779                 heif_nclx_color_profile_free(nclxcp);
780
781                 return (guchar *)data;
782                 }
783         else
784                 {
785                 error_code = heif_image_handle_get_nclx_color_profile(handle, &nclxcp);
786                 if (error_code.code)
787                         {
788                         log_printf("warning: heif reader error: %s\n", error_code.message);
789                         heif_context_free(ctx);
790                         heif_nclx_color_profile_free(nclxcp);
791                         return NULL;
792                         }
793
794                 profile = nclx_to_lcms_profile(nclxcp, profile_len);
795                 }
796
797         heif_context_free(ctx);
798         heif_nclx_color_profile_free(nclxcp);
799
800         return (guchar *)profile;
801 }
802 #else
803 guchar *heif_color_profile(FileData *UNUSED(fd), guint *UNUSED(profile_len))
804 {
805         return NULL;
806 }
807 #endif
808
809 #else /* define HAVE_LCMS */
810 /*** color support not enabled ***/
811
812
813 ColorMan *color_man_new(ImageWindow *UNUSED(imd), GdkPixbuf *UNUSED(pixbuf),
814                         ColorManProfileType UNUSED(input_type), const gchar *UNUSED(input_file),
815                         ColorManProfileType UNUSED(screen_type), const gchar *UNUSED(screen_file),
816                         guchar *UNUSED(screen_data), guint UNUSED(screen_data_len))
817 {
818         /* no op */
819         return NULL;
820 }
821
822 ColorMan *color_man_new_embedded(ImageWindow *UNUSED(imd), GdkPixbuf *UNUSED(pixbuf),
823                                  guchar *UNUSED(input_data), guint UNUSED(input_data_len),
824                                  ColorManProfileType UNUSED(screen_type), const gchar *UNUSED(screen_file),
825                                  guchar *UNUSED(screen_data), guint UNUSED(screen_data_len))
826 {
827         /* no op */
828         return NULL;
829 }
830
831 void color_man_free(ColorMan *UNUSED(cm))
832 {
833         /* no op */
834 }
835
836 void color_man_update(void)
837 {
838         /* no op */
839 }
840
841 void color_man_correct_region(ColorMan *UNUSED(cm), GdkPixbuf *UNUSED(pixbuf), gint UNUSED(x), gint UNUSED(y), gint UNUSED(w), gint UNUSED(h))
842 {
843         /* no op */
844 }
845
846 void color_man_start_bg(ColorMan *UNUSED(cm), ColorManDoneFunc UNUSED(done_func), gpointer UNUSED(done_data))
847 {
848         /* no op */
849 }
850
851 gboolean color_man_get_status(ColorMan *UNUSED(cm), gchar **UNUSED(image_profile), gchar **UNUSED(screen_profile))
852 {
853         return FALSE;
854 }
855
856 guchar *heif_color_profile(FileData *UNUSED(fd), guint *UNUSED(profile_len))
857 {
858         return NULL;
859 }
860
861 #endif /* define HAVE_LCMS */
862 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */