Skip to content

Commit

Permalink
Split heif_color_profile() to separate module
Browse files Browse the repository at this point in the history
Simplify conditional compilation.
  • Loading branch information
qarkai authored and caclark committed Jan 26, 2025
1 parent 590a5c2 commit 3e63f92
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 290 deletions.
330 changes: 330 additions & 0 deletions src/color-man-heif.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
/*
* Copyright (C) 2006 John Ellis
* Copyright (C) 2008 - 2016 The Geeqie Team
*
* Author: John Ellis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "color-man-heif.h"

#include <config.h>

#if HAVE_LCMS && HAVE_HEIF
/*** color support enabled ***/

#include <cmath>
#include <cstdint>
#include <cstring>
#include <vector>

#include <glib-object.h>

#if HAVE_LCMS2
# include <lcms2.h>
#else
# include <lcms.h>
#endif

#include <libheif/heif_cxx.h>

namespace
{

G_DEFINE_AUTOPTR_CLEANUP_FUNC(heif_color_profile_nclx, heif_nclx_color_profile_free)

cmsToneCurve *colorspaces_create_transfer(int32_t size, double (*fct)(double))
{
std::vector<float> values;
values.reserve(size);
for(int32_t i = 0; i < size; ++i)
{
const double x = static_cast<float>(i) / (size - 1);
const double y = std::min(fct(x), 1.0);
values.push_back(static_cast<float>(y));
}

return cmsBuildTabulatedToneCurveFloat(nullptr, size, values.data());
}

// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-F.pdf
// Hybrid Log-Gamma
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 - A * ln(4.0 * A)

double e = std::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);
}

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 = std::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
*/
guchar *nclx_to_lcms_profile(const heif_color_profile_nclx *nclx, guint &profile_len)
{
const gchar *primaries_name = "";
const gchar *trc_name = "";
cmsHPROFILE *profile = nullptr;
cmsCIExyY whitepoint;
cmsCIExyYTRIPLE primaries;
cmsToneCurve *curve[3];
cmsUInt32Number size;
guint8 *data = nullptr;

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 == nullptr)
{
return nullptr;
}

if (nclx->color_primaries == heif_color_primaries_unspecified)
{
return nullptr;
}

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 nullptr;
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(nullptr, 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 (nullptr, 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 (nullptr, 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 (nullptr, 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(nullptr, 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, nullptr, &size))
{
data = static_cast<guint8 *>(g_malloc(size));
if (cmsSaveProfileToMem(profile, data, &size))
{
profile_len = size;
}
cmsCloseProfile(profile);
return static_cast<guchar *>(data);
}

cmsCloseProfile(profile);
}

return nullptr;
}

} // namespace

guchar *heif_color_profile(const gchar *path, guint &profile_len)
{
heif::Context ctx{};

try
{
ctx.read_from_file(path);

heif::ImageHandle image_handle = ctx.get_primary_image_handle();
heif_image_handle *handle = image_handle.get_raw_image_handle();

if (heif_image_handle_get_color_profile_type(handle) == heif_color_profile_type_prof)
{
profile_len = heif_image_handle_get_raw_color_profile_size(handle);
auto *data = static_cast<guint8 *>(g_malloc0(profile_len));

heif_error error = heif_image_handle_get_raw_color_profile(handle, data);
if (error.code) throw heif::Error(error);

DEBUG_1("heif color profile type: prof");

return static_cast<guchar *>(data);
}

g_autoptr(heif_color_profile_nclx) nclx_cp = heif_nclx_color_profile_alloc();

heif_error error = heif_image_handle_get_nclx_color_profile(handle, &nclx_cp);
if (error.code) throw heif::Error(error);

return nclx_to_lcms_profile(nclx_cp, profile_len);
}
catch (const heif::Error &error)
{
if (error.get_code() != heif_error_Color_profile_does_not_exist)
{
log_printf("warning: heif reader error: %d (%s)\n",
error.get_code(), error.get_message().c_str());
}
}

return nullptr;
}

#else /* define HAVE_LCMS && HAVE_HEIF */
/*** color support not enabled ***/

guchar *heif_color_profile(const gchar *, guint &)
{
return nullptr;
}

#endif /* define HAVE_LCMS && HAVE_HEIF */
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
30 changes: 30 additions & 0 deletions src/color-man-heif.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2006 John Ellis
* Copyright (C) 2008 - 2016 The Geeqie Team
*
* Author: John Ellis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef COLOR_MAN_HEIF_H
#define COLOR_MAN_HEIF_H

#include <glib.h>

guchar *heif_color_profile(const gchar *path, guint &profile_len);

#endif
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
Loading

0 comments on commit 3e63f92

Please sign in to comment.