color_man_cache_reset();
}
+#ifdef HAVE_HEIF
+#include <libheif/heif.h>
+#include <math.h>
+
+static cmsToneCurve* colorspaces_create_transfer(int32_t size, double (*fct)(double))
+{
+ float *values = static_cast<float *>(g_malloc(sizeof(float) * size));
+
+ for(int32_t i = 0; i < size; ++i)
+ {
+ const double x = (float)i / (size - 1);
+ const double y = MIN(fct(x), 1.0f);
+ values[i] = (float)y;
+ }
+
+ cmsToneCurve* result = cmsBuildTabulatedToneCurveFloat(NULL, size, values);
+ g_free(values);
+ return result;
+}
+
+// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-F.pdf
+// Hybrid Log-Gamma
+static double HLG_fct(double x)
+{
+ static const double Beta = 0.04;
+ static const double RA = 5.591816309728916; // 1.0 / A where A = 0.17883277
+ static const double B = 0.28466892; // 1.0 - 4.0 * A
+ static const double C = 0.5599107295; // 0,5 –aln(4a)
+
+ double e = MAX(x * (1.0 - Beta) + Beta, 0.0);
+
+ if(e == 0.0) return 0.0;
+
+ const double sign = e;
+ e = fabs(e);
+
+ double res = 0.0;
+
+ if(e <= 0.5)
+ {
+ res = e * e / 3.0;
+ }
+ else
+ {
+ res = (exp((e - C) * RA) + B) / 12.0;
+ }
+
+ return copysign(res, sign);
+}
+
+static double PQ_fct(double x)
+{
+ static const double M1 = 2610.0 / 16384.0;
+ static const double M2 = (2523.0 / 4096.0) * 128.0;
+ static const double C1 = 3424.0 / 4096.0;
+ static const double C2 = (2413.0 / 4096.0) * 32.0;
+ static const double C3 = (2392.0 / 4096.0) * 32.0;
+
+ if(x == 0.0) return 0.0;
+ const double sign = x;
+ x = fabs(x);
+
+ const double xpo = pow(x, 1.0 / M2);
+ const double num = MAX(xpo - C1, 0.0);
+ const double den = C2 - C3 * xpo;
+ const double res = pow(num / den, 1.0 / M1);
+
+ return copysign(res, sign);
+}
+
+/**
+ * @brief
+ * @param nclx
+ * @param profile_len
+ * @returns
+ *
+ * Copied from: gimp/libgimpcolor/gimpcolorprofile.c
+ */
+static guchar *nclx_to_lcms_profile(const struct heif_color_profile_nclx *nclx, guint *profile_len)
+{
+ const gchar *primaries_name = "";
+ const gchar *trc_name = "";
+ cmsHPROFILE *profile = NULL;
+ cmsCIExyY whitepoint;
+ cmsCIExyYTRIPLE primaries;
+ cmsToneCurve *curve[3];
+ cmsUInt32Number size;
+ guint8 *data = NULL;
+
+ cmsFloat64Number srgb_parameters[5] =
+ { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
+
+ cmsFloat64Number rec709_parameters[5] =
+ { 2.2, 1.0 / 1.099, 0.099 / 1.099, 1.0 / 4.5, 0.081 };
+
+ if (nclx == NULL)
+ {
+ return NULL;
+ }
+
+ if (nclx->color_primaries == heif_color_primaries_unspecified)
+ {
+ return NULL;
+ }
+
+ whitepoint.x = nclx->color_primary_white_x;
+ whitepoint.y = nclx->color_primary_white_y;
+ whitepoint.Y = 1.0f;
+
+ primaries.Red.x = nclx->color_primary_red_x;
+ primaries.Red.y = nclx->color_primary_red_y;
+ primaries.Red.Y = 1.0f;
+
+ primaries.Green.x = nclx->color_primary_green_x;
+ primaries.Green.y = nclx->color_primary_green_y;
+ primaries.Green.Y = 1.0f;
+
+ primaries.Blue.x = nclx->color_primary_blue_x;
+ primaries.Blue.y = nclx->color_primary_blue_y;
+ primaries.Blue.Y = 1.0f;
+
+ switch (nclx->color_primaries)
+ {
+ case heif_color_primaries_ITU_R_BT_709_5:
+ primaries_name = "BT.709";
+ break;
+ case heif_color_primaries_ITU_R_BT_470_6_System_M:
+ primaries_name = "BT.470-6 System M";
+ break;
+ case heif_color_primaries_ITU_R_BT_470_6_System_B_G:
+ primaries_name = "BT.470-6 System BG";
+ break;
+ case heif_color_primaries_ITU_R_BT_601_6:
+ primaries_name = "BT.601";
+ break;
+ case heif_color_primaries_SMPTE_240M:
+ primaries_name = "SMPTE 240M";
+ break;
+ case heif_color_primaries_generic_film:
+ primaries_name = "Generic film";
+ break;
+ case heif_color_primaries_ITU_R_BT_2020_2_and_2100_0:
+ primaries_name = "BT.2020";
+ break;
+ case heif_color_primaries_SMPTE_ST_428_1:
+ primaries_name = "SMPTE ST 428-1";
+ break;
+ case heif_color_primaries_SMPTE_RP_431_2:
+ primaries_name = "SMPTE RP 431-2";
+ break;
+ case heif_color_primaries_SMPTE_EG_432_1:
+ primaries_name = "SMPTE EG 432-1 (DCI P3)";
+ break;
+ case heif_color_primaries_EBU_Tech_3213_E:
+ primaries_name = "EBU Tech. 3213-E";
+ break;
+ default:
+ log_printf("nclx unsupported color_primaries value: %d\n", nclx->color_primaries);
+ return NULL;
+ break;
+ }
+
+ DEBUG_1("nclx primaries: %s: ", primaries_name);
+
+ switch (nclx->transfer_characteristics)
+ {
+ case heif_transfer_characteristic_ITU_R_BT_709_5:
+ curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve(NULL, 4, rec709_parameters);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "Rec709 RGB";
+ break;
+ case heif_transfer_characteristic_ITU_R_BT_470_6_System_M:
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.2f);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "Gamma2.2 RGB";
+ break;
+ case heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G:
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.8f);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "Gamma2.8 RGB";
+ break;
+ case heif_transfer_characteristic_linear:
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0f);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "linear RGB";
+ break;
+ case heif_transfer_characteristic_ITU_R_BT_2100_0_HLG:
+ curve[0] = curve[1] = curve[2] = colorspaces_create_transfer(4096, HLG_fct);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "HLG Rec2020 RGB";
+ break;
+ case heif_transfer_characteristic_ITU_R_BT_2100_0_PQ:
+ curve[0] = curve[1] = curve[2] = colorspaces_create_transfer(4096, PQ_fct);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "PQ Rec2020 RGB";
+ break;
+ case heif_transfer_characteristic_IEC_61966_2_1:
+ /* same as default */
+ default:
+ curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters);
+ profile = static_cast<cmsHPROFILE *>(cmsCreateRGBProfile(&whitepoint, &primaries, curve));
+ cmsFreeToneCurve(curve[0]);
+ trc_name = "sRGB-TRC RGB";
+ break;
+ }
+
+ DEBUG_1("nclx transfer characteristic: %s", trc_name);
+
+ if (profile)
+ {
+ if (cmsSaveProfileToMem(profile, NULL, &size))
+ {
+ data = static_cast<guint8 *>(g_malloc(size));
+ if (cmsSaveProfileToMem(profile, data, &size))
+ {
+ *profile_len = size;
+ }
+ cmsCloseProfile(profile);
+ return (guchar *)data;
+ }
+ else
+ {
+ cmsCloseProfile(profile);
+ return NULL;
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+guchar *heif_color_profile(FileData *fd, guint *profile_len)
+{
+ struct heif_context* ctx;
+ struct heif_error error_code;
+ struct heif_image_handle* handle;
+ struct heif_color_profile_nclx *nclxcp;
+ gint profile_type;
+ guchar *profile;
+ cmsUInt32Number size;
+ guint8 *data = NULL;
+
+ ctx = heif_context_alloc();
+ error_code = heif_context_read_from_file(ctx, fd->path, NULL);
+
+ if (error_code.code)
+ {
+ log_printf("warning: heif reader error: %s\n", error_code.message);
+ heif_context_free(ctx);
+ return NULL;
+ }
+
+ error_code = heif_context_get_primary_image_handle(ctx, &handle);
+ if (error_code.code)
+ {
+ log_printf("warning: heif reader error: %s\n", error_code.message);
+ heif_context_free(ctx);
+ return NULL;
+ }
+
+ nclxcp = heif_nclx_color_profile_alloc();
+ profile_type = heif_image_handle_get_color_profile_type(handle);
+
+ if (profile_type == heif_color_profile_type_prof)
+ {
+ size = heif_image_handle_get_raw_color_profile_size(handle);
+ *profile_len = size;
+ data = static_cast<guint8 *>(g_malloc0(size));
+ error_code = heif_image_handle_get_raw_color_profile(handle, data);
+ if (error_code.code)
+ {
+ log_printf("warning: heif reader error: %s\n", error_code.message);
+ heif_context_free(ctx);
+ heif_nclx_color_profile_free(nclxcp);
+ return NULL;
+ }
+
+ DEBUG_1("heif color profile type: prof");
+ heif_context_free(ctx);
+ heif_nclx_color_profile_free(nclxcp);
+
+ return (guchar *)data;
+ }
+ else
+ {
+ error_code = heif_image_handle_get_nclx_color_profile(handle, &nclxcp);
+ if (error_code.code)
+ {
+ log_printf("warning: heif reader error: %s\n", error_code.message);
+ heif_context_free(ctx);
+ heif_nclx_color_profile_free(nclxcp);
+ return NULL;
+ }
+
+ profile = nclx_to_lcms_profile(nclxcp, profile_len);
+ }
+
+ heif_context_free(ctx);
+ heif_nclx_color_profile_free(nclxcp);
+
+ return (guchar *)profile;
+}
+#else
+guchar *heif_color_profile(FileData *UNUSED(fd), guint *UNUSED(profile_len))
+{
+ return NULL;
+}
+#endif
+
#else /* define HAVE_LCMS */
/*** color support not enabled ***/
return FALSE;
}
+guchar *heif_color_profile(FileData *UNUSED(fd), guint *UNUSED(profile_len))
+{
+ return NULL;
+}
+
#endif /* define HAVE_LCMS */
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */