Remove UNUSED macro
[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 #include <vector>
29
30 #ifdef HAVE_LCMS
31 /*** color support enabled ***/
32
33 #ifdef HAVE_LCMS2
34 #include <lcms2.h>
35 #else
36 #include <lcms.h>
37 #endif
38
39
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()
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()
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 = nullptr;
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 = nullptr;
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 nullptr;
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 nullptr;
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()
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 nullptr;
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 #pragma GCC diagnostic push
295 #pragma GCC diagnostic ignored "-Wunused-function"
296 static void color_man_done_unused(ColorMan *cm, ColorManReturnType type)
297 {
298         if (cm->func_done)
299                 {
300                 cm->func_done(cm, type, cm->func_done_data);
301                 }
302 }
303 #pragma GCC diagnostic pop
304
305 void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
306 {
307         ColorManCache *cc;
308         guchar *pix;
309         gint rs;
310         gint i;
311         gint pixbuf_width, pixbuf_height;
312
313
314         pixbuf_width = gdk_pixbuf_get_width(pixbuf);
315         pixbuf_height = gdk_pixbuf_get_height(pixbuf);
316
317         cc = static_cast<ColorManCache *>(cm->profile);
318
319         pix = gdk_pixbuf_get_pixels(pixbuf);
320         rs = gdk_pixbuf_get_rowstride(pixbuf);
321
322         /** @FIXME: x,y expected to be = 0. Maybe this is not the right place for scaling */
323         w = w * scale_factor();
324         h = h * scale_factor();
325
326         w = MIN(w, pixbuf_width - x);
327         h = MIN(h, pixbuf_height - y);
328
329         pix += x * ((cc->has_alpha) ? 4 : 3);
330         for (i = 0; i < h; i++)
331                 {
332                 guchar *pbuf;
333
334                 pbuf = pix + ((y + i) * rs);
335
336                 cmsDoTransform(cc->transform, pbuf, pbuf, w);
337                 }
338
339 }
340
341 #pragma GCC diagnostic push
342 #pragma GCC diagnostic ignored "-Wunused-function"
343 static gboolean color_man_idle_cb_unused(gpointer data)
344 {
345         ColorMan *cm = static_cast<ColorMan *>(data);
346         gint width, height;
347         gint rh;
348
349         if (!cm->pixbuf) return FALSE;
350
351         if (cm->imd &&
352             cm->pixbuf != image_get_pixbuf(cm->imd))
353                 {
354                 cm->idle_id = 0;
355                 color_man_done_unused(cm, COLOR_RETURN_IMAGE_CHANGED);
356                 return FALSE;
357                 }
358
359         width = gdk_pixbuf_get_width(cm->pixbuf);
360         height = gdk_pixbuf_get_height(cm->pixbuf);
361
362         if (cm->row > height)
363                 {
364                 if (!cm->incremental_sync && cm->imd)
365                         {
366                         image_area_changed(cm->imd, 0, 0, width, height);
367                         }
368
369                 cm->idle_id = 0;
370                 color_man_done_unused(cm, COLOR_RETURN_SUCCESS);
371                 return FALSE;
372                 }
373
374         rh = COLOR_MAN_CHUNK_SIZE / width + 1;
375         color_man_correct_region(cm, cm->pixbuf, 0, cm->row, width, rh);
376         if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, 0, cm->row, width, rh);
377         cm->row += rh;
378
379         return TRUE;
380 }
381 #pragma GCC diagnostic pop
382
383 static ColorMan *color_man_new_real(ImageWindow *imd, GdkPixbuf *pixbuf,
384                                     ColorManProfileType input_type, const gchar *input_file,
385                                     guchar *input_data, guint input_data_len,
386                                     ColorManProfileType screen_type, const gchar *screen_file,
387                                     guchar *screen_data, guint screen_data_len)
388 {
389         ColorMan *cm;
390         gboolean has_alpha;
391
392         if (imd) pixbuf = image_get_pixbuf(imd);
393
394         cm = g_new0(ColorMan, 1);
395         cm->imd = imd;
396         cm->pixbuf = pixbuf;
397         if (cm->pixbuf) g_object_ref(cm->pixbuf);
398
399         has_alpha = pixbuf ? gdk_pixbuf_get_has_alpha(pixbuf) : FALSE;
400
401         cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len,
402                                           screen_type, screen_file, screen_data, screen_data_len, has_alpha);
403         if (!cm->profile)
404                 {
405                 color_man_free(cm);
406                 return nullptr;
407                 }
408
409         return cm;
410 }
411
412 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
413                         ColorManProfileType input_type, const gchar *input_file,
414                         ColorManProfileType screen_type, const gchar *screen_file,
415                         guchar *screen_data, guint screen_data_len)
416 {
417         return color_man_new_real(imd, pixbuf,
418                                   input_type, input_file, nullptr, 0,
419                                   screen_type, screen_file, screen_data, screen_data_len);
420 }
421
422 #pragma GCC diagnostic push
423 #pragma GCC diagnostic ignored "-Wunused-function"
424 void color_man_start_bg_unused(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
425 {
426         cm->func_done = done_func;
427         cm->func_done_data = done_data;
428         cm->idle_id = g_idle_add(color_man_idle_cb_unused, cm);
429 }
430 #pragma GCC diagnostic pop
431
432 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
433                                  guchar *input_data, guint input_data_len,
434                                  ColorManProfileType screen_type, const gchar *screen_file,
435                                  guchar *screen_data, guint screen_data_len)
436 {
437         return color_man_new_real(imd, pixbuf,
438                                   COLOR_PROFILE_MEM, nullptr, input_data, input_data_len,
439                                   screen_type, screen_file, screen_data, screen_data_len);
440 }
441
442 static gchar *color_man_get_profile_name(ColorManProfileType type, cmsHPROFILE profile)
443 {
444         switch (type)
445                 {
446                 case COLOR_PROFILE_SRGB:
447                         return g_strdup(_("sRGB"));
448                 case COLOR_PROFILE_ADOBERGB:
449                         return g_strdup(_("Adobe RGB compatible"));
450                         break;
451                 case COLOR_PROFILE_MEM:
452                 case COLOR_PROFILE_FILE:
453                         if (profile)
454                                 {
455 #ifdef HAVE_LCMS2
456                                 char buffer[20];
457                                 buffer[0] = '\0';
458                                 cmsGetProfileInfoASCII(profile, cmsInfoDescription, "en", "US", buffer, 20);
459                                 buffer[19] = '\0'; /* Just to be sure */
460                                 return g_strdup(buffer);
461 #else
462                                 return g_strdup(cmsTakeProductName(profile));
463 #endif
464                                 }
465                         return g_strdup(_("Custom profile"));
466                         break;
467                 case COLOR_PROFILE_NONE:
468                 default:
469                         return g_strdup("");
470                 }
471 }
472
473 gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile)
474 {
475         ColorManCache *cc;
476         if (!cm) return FALSE;
477
478         cc = static_cast<ColorManCache *>(cm->profile);
479
480         if (image_profile) *image_profile = color_man_get_profile_name(cc->profile_in_type, cc->profile_in);
481         if (screen_profile) *screen_profile = color_man_get_profile_name(cc->profile_out_type, cc->profile_out);
482         return TRUE;
483 }
484
485 void color_man_free(ColorMan *cm)
486 {
487         if (!cm) return;
488
489         if (cm->idle_id) g_source_remove(cm->idle_id);
490         if (cm->pixbuf) g_object_unref(cm->pixbuf);
491
492         color_man_cache_unref(static_cast<ColorManCache *>(cm->profile));
493
494         g_free(cm);
495 }
496
497 void color_man_update()
498 {
499         color_man_cache_reset();
500 }
501
502 #ifdef HAVE_HEIF
503 #include <cmath>
504 #include <libheif/heif.h>
505
506 static cmsToneCurve* colorspaces_create_transfer(int32_t size, double (*fct)(double))
507 {
508         std::vector<float> values;
509         values.reserve(size);
510         for(int32_t i = 0; i < size; ++i)
511                 {
512                 const double x = static_cast<float>(i) / (size - 1);
513                 const double y = MIN(fct(x), 1.0f);
514                 values[i] = static_cast<float>(y);
515                 }
516
517         return cmsBuildTabulatedToneCurveFloat(nullptr, size, values.data());
518 }
519
520 // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-F.pdf
521 // Hybrid Log-Gamma
522 static double HLG_fct(double x)
523 {
524         static const double Beta  = 0.04;
525         static const double RA    = 5.591816309728916; // 1.0 / A where A = 0.17883277
526         static const double B     = 0.28466892; // 1.0 - 4.0 * A
527         static const double C     = 0.5599107295; // 0,5 â€“aln(4a)
528
529         double e = MAX(x * (1.0 - Beta) + Beta, 0.0);
530
531         if(e == 0.0) return 0.0;
532
533         const double sign = e;
534         e = fabs(e);
535
536         double res = 0.0;
537
538         if(e <= 0.5)
539                 {
540                 res = e * e / 3.0;
541                 }
542         else
543                 {
544                 res = (exp((e - C) * RA) + B) / 12.0;
545                 }
546
547         return copysign(res, sign);
548 }
549
550 static double PQ_fct(double x)
551 {
552         static const double M1 = 2610.0 / 16384.0;
553         static const double M2 = (2523.0 / 4096.0) * 128.0;
554         static const double C1 = 3424.0 / 4096.0;
555         static const double C2 = (2413.0 / 4096.0) * 32.0;
556         static const double C3 = (2392.0 / 4096.0) * 32.0;
557
558         if(x == 0.0) return 0.0;
559         const double sign = x;
560         x = fabs(x);
561
562         const double xpo = pow(x, 1.0 / M2);
563         const double num = MAX(xpo - C1, 0.0);
564         const double den = C2 - C3 * xpo;
565         const double res = pow(num / den, 1.0 / M1);
566
567         return copysign(res, sign);
568 }
569
570 /**
571  * @brief
572  * @param nclx
573  * @param profile_len
574  * @returns
575  *
576  * Copied from: gimp/libgimpcolor/gimpcolorprofile.c
577  */
578 static guchar *nclx_to_lcms_profile(const struct heif_color_profile_nclx *nclx, guint *profile_len)
579 {
580         const gchar *primaries_name = "";
581         const gchar *trc_name = "";
582         cmsHPROFILE *profile = nullptr;
583         cmsCIExyY whitepoint;
584         cmsCIExyYTRIPLE primaries;
585         cmsToneCurve *curve[3];
586         cmsUInt32Number size;
587         guint8 *data = nullptr;
588
589         cmsFloat64Number srgb_parameters[5] =
590         { 2.4, 1.0 / 1.055,  0.055 / 1.055, 1.0 / 12.92, 0.04045 };
591
592         cmsFloat64Number rec709_parameters[5] =
593         { 2.2, 1.0 / 1.099,  0.099 / 1.099, 1.0 / 4.5, 0.081 };
594
595         if (nclx == nullptr)
596                 {
597                 return nullptr;
598                 }
599
600         if (nclx->color_primaries == heif_color_primaries_unspecified)
601                 {
602                 return nullptr;
603                 }
604
605         whitepoint.x = nclx->color_primary_white_x;
606         whitepoint.y = nclx->color_primary_white_y;
607         whitepoint.Y = 1.0f;
608
609         primaries.Red.x = nclx->color_primary_red_x;
610         primaries.Red.y = nclx->color_primary_red_y;
611         primaries.Red.Y = 1.0f;
612
613         primaries.Green.x = nclx->color_primary_green_x;
614         primaries.Green.y = nclx->color_primary_green_y;
615         primaries.Green.Y = 1.0f;
616
617         primaries.Blue.x = nclx->color_primary_blue_x;
618         primaries.Blue.y = nclx->color_primary_blue_y;
619         primaries.Blue.Y = 1.0f;
620
621         switch (nclx->color_primaries)
622                 {
623                 case heif_color_primaries_ITU_R_BT_709_5:
624                         primaries_name = "BT.709";
625                         break;
626                 case   heif_color_primaries_ITU_R_BT_470_6_System_M:
627                         primaries_name = "BT.470-6 System M";
628                         break;
629                 case heif_color_primaries_ITU_R_BT_470_6_System_B_G:
630                         primaries_name = "BT.470-6 System BG";
631                         break;
632                 case heif_color_primaries_ITU_R_BT_601_6:
633                         primaries_name = "BT.601";
634                         break;
635                 case heif_color_primaries_SMPTE_240M:
636                         primaries_name = "SMPTE 240M";
637                         break;
638                 case heif_color_primaries_generic_film:
639                         primaries_name = "Generic film";
640                         break;
641                 case heif_color_primaries_ITU_R_BT_2020_2_and_2100_0:
642                         primaries_name = "BT.2020";
643                         break;
644                 case heif_color_primaries_SMPTE_ST_428_1:
645                         primaries_name = "SMPTE ST 428-1";
646                         break;
647                 case heif_color_primaries_SMPTE_RP_431_2:
648                         primaries_name = "SMPTE RP 431-2";
649                         break;
650                 case heif_color_primaries_SMPTE_EG_432_1:
651                         primaries_name = "SMPTE EG 432-1 (DCI P3)";
652                         break;
653                 case heif_color_primaries_EBU_Tech_3213_E:
654                         primaries_name = "EBU Tech. 3213-E";
655                         break;
656                 default:
657                         log_printf("nclx unsupported color_primaries value: %d\n", nclx->color_primaries);
658                         return nullptr;
659                         break;
660                 }
661
662         DEBUG_1("nclx primaries: %s: ", primaries_name);
663
664         switch (nclx->transfer_characteristics)
665                 {
666                 case heif_transfer_characteristic_ITU_R_BT_709_5:
667                         curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve(nullptr, 4, rec709_parameters);
668                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
669                         cmsFreeToneCurve(curve[0]);
670                         trc_name = "Rec709 RGB";
671                         break;
672                 case heif_transfer_characteristic_ITU_R_BT_470_6_System_M:
673                         curve[0] = curve[1] = curve[2] = cmsBuildGamma (nullptr, 2.2f);
674                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
675                         cmsFreeToneCurve(curve[0]);
676                         trc_name = "Gamma2.2 RGB";
677                         break;
678                 case heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G:
679                         curve[0] = curve[1] = curve[2] = cmsBuildGamma (nullptr, 2.8f);
680                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
681                         cmsFreeToneCurve(curve[0]);
682                         trc_name = "Gamma2.8 RGB";
683                         break;
684                 case heif_transfer_characteristic_linear:
685                         curve[0] = curve[1] = curve[2] = cmsBuildGamma (nullptr, 1.0f);
686                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
687                         cmsFreeToneCurve(curve[0]);
688                         trc_name = "linear RGB";
689                         break;
690                 case heif_transfer_characteristic_ITU_R_BT_2100_0_HLG:
691                         curve[0] = curve[1] = curve[2] = colorspaces_create_transfer(4096, HLG_fct);
692                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
693                         cmsFreeToneCurve(curve[0]);
694                         trc_name = "HLG Rec2020 RGB";
695                         break;
696                 case heif_transfer_characteristic_ITU_R_BT_2100_0_PQ:
697                         curve[0] = curve[1] = curve[2] = colorspaces_create_transfer(4096, PQ_fct);
698                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
699                         cmsFreeToneCurve(curve[0]);
700                         trc_name = "PQ Rec2020 RGB";
701                         break;
702                 case heif_transfer_characteristic_IEC_61966_2_1:
703                 /* same as default */
704                 default:
705                         curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve(nullptr, 4, srgb_parameters);
706                         profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
707                         cmsFreeToneCurve(curve[0]);
708                         trc_name = "sRGB-TRC RGB";
709                         break;
710                 }
711
712         DEBUG_1("nclx transfer characteristic: %s", trc_name);
713
714         if (profile)
715                 {
716                 if (cmsSaveProfileToMem(profile, nullptr, &size))
717                         {
718                         data = static_cast<guint8 *>(g_malloc(size));
719                         if (cmsSaveProfileToMem(profile, data, &size))
720                                 {
721                                 *profile_len = size;
722                                 }
723                         cmsCloseProfile(profile);
724                         return static_cast<guchar *>(data);
725                         }
726                 else
727                         {
728                         cmsCloseProfile(profile);
729                         return nullptr;
730                         }
731                 }
732         else
733                 {
734                 return nullptr;
735                 }
736 }
737
738 guchar *heif_color_profile(FileData *fd, guint *profile_len)
739 {
740         struct heif_context* ctx;
741         struct heif_error error_code;
742         struct heif_image_handle* handle;
743         struct heif_color_profile_nclx *nclxcp;
744         gint profile_type;
745         guchar *profile;
746         cmsUInt32Number size;
747         guint8 *data = nullptr;
748
749         ctx = heif_context_alloc();
750         error_code = heif_context_read_from_file(ctx, fd->path, nullptr);
751
752         if (error_code.code)
753                 {
754                 log_printf("warning: heif reader error: %s\n", error_code.message);
755                 heif_context_free(ctx);
756                 return nullptr;
757                 }
758
759         error_code = heif_context_get_primary_image_handle(ctx, &handle);
760         if (error_code.code)
761                 {
762                 log_printf("warning: heif reader error: %s\n", error_code.message);
763                 heif_context_free(ctx);
764                 return nullptr;
765                 }
766
767         nclxcp = heif_nclx_color_profile_alloc();
768         profile_type = heif_image_handle_get_color_profile_type(handle);
769
770         if (profile_type == heif_color_profile_type_prof)
771                 {
772                 size = heif_image_handle_get_raw_color_profile_size(handle);
773                 *profile_len = size;
774                 data = static_cast<guint8 *>(g_malloc0(size));
775                 error_code = heif_image_handle_get_raw_color_profile(handle, data);
776                 if (error_code.code)
777                         {
778                         log_printf("warning: heif reader error: %s\n", error_code.message);
779                         heif_context_free(ctx);
780                         heif_nclx_color_profile_free(nclxcp);
781                         return nullptr;
782                         }
783
784                 DEBUG_1("heif color profile type: prof");
785                 heif_context_free(ctx);
786                 heif_nclx_color_profile_free(nclxcp);
787
788                 return static_cast<guchar *>(data);
789                 }
790         else
791                 {
792                 error_code = heif_image_handle_get_nclx_color_profile(handle, &nclxcp);
793                 if (error_code.code)
794                         {
795                         log_printf("warning: heif reader error: %s\n", error_code.message);
796                         heif_context_free(ctx);
797                         heif_nclx_color_profile_free(nclxcp);
798                         return nullptr;
799                         }
800
801                 profile = nclx_to_lcms_profile(nclxcp, profile_len);
802                 }
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: */