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