PIKApp/libpikacolor/pikacolorprofile.c

1827 lines
55 KiB
C
Raw Normal View History

2023-09-26 00:35:21 +02:00
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* pikacolorprofile.c
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
* Elle Stone <ellestone@ninedegreesbelow.com>
* Øyvind Kolås <pippin@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <lcms2.h>
#include <gio/gio.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "pikacolortypes.h"
#include "pikacolorprofile.h"
#include "libpika/libpika-intl.h"
#ifndef TYPE_RGBA_DBL
#define TYPE_RGBA_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(0))
#endif
#ifndef TYPE_GRAYA_HALF_FLT
#define TYPE_GRAYA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2))
#endif
#ifndef TYPE_GRAYA_FLT
#define TYPE_GRAYA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(4))
#endif
#ifndef TYPE_GRAYA_DBL
#define TYPE_GRAYA_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(0))
#endif
#ifndef TYPE_CMYKA_DBL
#define TYPE_CMYKA_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(0))
#endif
#ifndef TYPE_CMYKA_HALF_FLT
#define TYPE_CMYKA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(2))
#endif
#ifndef TYPE_CMYKA_FLT
#define TYPE_CMYKA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(4))
#endif
#ifndef TYPE_CMYKA_16
#define TYPE_CMYKA_16 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(2))
#endif
/**
* SECTION: pikacolorprofile
* @title: PikaColorProfile
* @short_description: Definitions and Functions relating to LCMS.
*
* Definitions and Functions relating to LCMS.
**/
/**
* PikaColorProfile:
*
* Simply a typedef to #gpointer, but actually is a cmsHPROFILE. It's
* used in public PIKA APIs in order to avoid having to include LCMS
* headers.
**/
struct _PikaColorProfilePrivate
{
cmsHPROFILE lcms_profile;
guint8 *data;
gsize length;
gchar *description;
gchar *manufacturer;
gchar *model;
gchar *copyright;
gchar *label;
gchar *summary;
};
static void pika_color_profile_finalize (GObject *object);
G_DEFINE_TYPE_WITH_PRIVATE (PikaColorProfile, pika_color_profile, G_TYPE_OBJECT)
#define parent_class pika_color_profile_parent_class
#define PIKA_COLOR_PROFILE_ERROR pika_color_profile_error_quark ()
static GQuark
pika_color_profile_error_quark (void)
{
static GQuark quark = 0;
if (G_UNLIKELY (quark == 0))
quark = g_quark_from_static_string ("pika-color-profile-error-quark");
return quark;
}
static void
pika_color_profile_class_init (PikaColorProfileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pika_color_profile_finalize;
}
static void
pika_color_profile_init (PikaColorProfile *profile)
{
profile->priv = pika_color_profile_get_instance_private (profile);
}
static void
pika_color_profile_finalize (GObject *object)
{
PikaColorProfile *profile = PIKA_COLOR_PROFILE (object);
g_clear_pointer (&profile->priv->lcms_profile, cmsCloseProfile);
g_clear_pointer (&profile->priv->data, g_free);
profile->priv->length = 0;
g_clear_pointer (&profile->priv->description, g_free);
g_clear_pointer (&profile->priv->manufacturer, g_free);
g_clear_pointer (&profile->priv->model, g_free);
g_clear_pointer (&profile->priv->copyright, g_free);
g_clear_pointer (&profile->priv->label, g_free);
g_clear_pointer (&profile->priv->summary, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* pika_color_profile_new_from_file:
* @file: a #GFile
* @error: return location for #GError
*
* This function opens an ICC color profile from @file.
*
* Returns: (nullable): the #PikaColorProfile, or %NULL. On error, %NULL is
* returned and @error is set.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_from_file (GFile *file,
GError **error)
{
PikaColorProfile *profile = NULL;
cmsHPROFILE lcms_profile = NULL;
guint8 *data = NULL;
gsize length = 0;
gchar *path;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
path = g_file_get_path (file);
if (path)
{
GMappedFile *mapped;
mapped = g_mapped_file_new (path, FALSE, error);
g_free (path);
if (! mapped)
return NULL;
length = g_mapped_file_get_length (mapped);
data = g_memdup2 (g_mapped_file_get_contents (mapped), length);
lcms_profile = cmsOpenProfileFromMem (data, length);
g_mapped_file_unref (mapped);
}
else
{
GFileInfo *info;
info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE,
NULL, error);
if (info)
{
GInputStream *input;
2023-09-26 01:54:03 +02:00
length = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
2023-09-26 00:35:21 +02:00
data = g_malloc (length);
g_object_unref (info);
input = G_INPUT_STREAM (g_file_read (file, NULL, error));
if (input)
{
gsize bytes_read;
if (g_input_stream_read_all (input, data, length,
&bytes_read, NULL, error) &&
bytes_read == length)
{
lcms_profile = cmsOpenProfileFromMem (data, length);
}
g_object_unref (input);
}
}
}
if (lcms_profile)
{
profile = g_object_new (PIKA_TYPE_COLOR_PROFILE, NULL);
profile->priv->lcms_profile = lcms_profile;
profile->priv->data = data;
profile->priv->length = length;
}
else
{
if (data)
g_free (data);
if (error && *error == NULL)
{
g_set_error (error, PIKA_COLOR_PROFILE_ERROR, 0,
_("'%s' does not appear to be an ICC color profile"),
pika_file_get_utf8_name (file));
}
}
return profile;
}
/**
* pika_color_profile_new_from_icc_profile:
* @data: (array length=length): The memory containing an ICC profile
* @length: length of the profile in memory, in bytes
* @error: return location for #GError
*
* This function opens an ICC color profile from memory. On error,
* %NULL is returned and @error is set.
*
* Returns: (nullable): the #PikaColorProfile, or %NULL.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_from_icc_profile (const guint8 *data,
gsize length,
GError **error)
{
cmsHPROFILE lcms_profile = 0;
PikaColorProfile *profile = NULL;
g_return_val_if_fail (data != NULL || length == 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (length > 0)
lcms_profile = cmsOpenProfileFromMem (data, length);
if (lcms_profile)
{
profile = g_object_new (PIKA_TYPE_COLOR_PROFILE, NULL);
profile->priv->lcms_profile = lcms_profile;
profile->priv->data = g_memdup2 (data, length);
profile->priv->length = length;
}
else
{
g_set_error_literal (error, PIKA_COLOR_PROFILE_ERROR, 0,
_("Data does not appear to be an ICC color profile"));
}
return profile;
}
/**
* pika_color_profile_new_from_lcms_profile:
* @lcms_profile: an LCMS cmsHPROFILE pointer
* @error: return location for #GError
*
* This function creates a PikaColorProfile from a cmsHPROFILE. On
* error, %NULL is returned and @error is set. The passed
* @lcms_profile pointer is not retained by the created
* #PikaColorProfile.
*
* Returns: (nullable): the #PikaColorProfile, or %NULL.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_from_lcms_profile (gpointer lcms_profile,
GError **error)
{
cmsUInt32Number size;
g_return_val_if_fail (lcms_profile != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (cmsSaveProfileToMem (lcms_profile, NULL, &size))
{
guint8 *data = g_malloc (size);
if (cmsSaveProfileToMem (lcms_profile, data, &size))
{
gsize length = size;
lcms_profile = cmsOpenProfileFromMem (data, length);
if (lcms_profile)
{
PikaColorProfile *profile;
profile = g_object_new (PIKA_TYPE_COLOR_PROFILE, NULL);
profile->priv->lcms_profile = lcms_profile;
profile->priv->data = data;
profile->priv->length = length;
return profile;
}
}
g_free (data);
}
g_set_error_literal (error, PIKA_COLOR_PROFILE_ERROR, 0,
_("Could not save color profile to memory"));
return NULL;
}
/**
* pika_color_profile_save_to_file:
* @profile: a #PikaColorProfile
* @file: a #GFile
* @error: return location for #GError
*
* This function saves @profile to @file as ICC profile.
*
* Returns: %TRUE on success, %FALSE if an error occurred.
*
* Since: 2.10
**/
gboolean
pika_color_profile_save_to_file (PikaColorProfile *profile,
GFile *file,
GError **error)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_file_replace_contents (file,
(const gchar *) profile->priv->data,
profile->priv->length,
NULL, FALSE,
G_FILE_CREATE_NONE,
NULL,
NULL,
error);
}
/**
* pika_color_profile_get_icc_profile:
* @profile: a #PikaColorProfile
* @length: (out): return location for the number of bytes
*
* This function returns @profile as ICC profile data. The returned
* memory belongs to @profile and must not be modified or freed.
*
* Returns: (array length=length): a pointer to the IIC profile data.
*
* Since: 2.10
**/
const guint8 *
pika_color_profile_get_icc_profile (PikaColorProfile *profile,
gsize *length)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
g_return_val_if_fail (length != NULL, NULL);
*length = profile->priv->length;
return profile->priv->data;
}
/**
* pika_color_profile_get_lcms_profile:
* @profile: a #PikaColorProfile
*
* This function returns @profile's cmsHPROFILE. The returned
* value belongs to @profile and must not be modified or freed.
*
* Returns: a pointer to the cmsHPROFILE.
*
* Since: 2.10
**/
gpointer
pika_color_profile_get_lcms_profile (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
return profile->priv->lcms_profile;
}
static gchar *
pika_color_profile_get_info (PikaColorProfile *profile,
cmsInfoType info)
{
cmsUInt32Number size;
gchar *text = NULL;
size = cmsGetProfileInfoASCII (profile->priv->lcms_profile, info,
"en", "US", NULL, 0);
if (size > 0)
{
gchar *data = g_new (gchar, size + 1);
size = cmsGetProfileInfoASCII (profile->priv->lcms_profile, info,
"en", "US", data, size);
if (size > 0)
text = pika_any_to_utf8 (data, -1, NULL);
g_free (data);
}
return text;
}
/**
* pika_color_profile_get_description:
* @profile: a #PikaColorProfile
*
* Returns: a string containing @profile's description. The
* returned value belongs to @profile and must not be
* modified or freed.
*
* Since: 2.10
**/
const gchar *
pika_color_profile_get_description (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (! profile->priv->description)
profile->priv->description =
pika_color_profile_get_info (profile, cmsInfoDescription);
return profile->priv->description;
}
/**
* pika_color_profile_get_manufacturer:
* @profile: a #PikaColorProfile
*
* Returns: a string containing @profile's manufacturer. The
* returned value belongs to @profile and must not be
* modified or freed.
*
* Since: 2.10
**/
const gchar *
pika_color_profile_get_manufacturer (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (! profile->priv->manufacturer)
profile->priv->manufacturer =
pika_color_profile_get_info (profile, cmsInfoManufacturer);
return profile->priv->manufacturer;
}
/**
* pika_color_profile_get_model:
* @profile: a #PikaColorProfile
*
* Returns: a string containing @profile's model. The returned
* value belongs to @profile and must not be modified or
* freed.
*
* Since: 2.10
**/
const gchar *
pika_color_profile_get_model (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (! profile->priv->model)
profile->priv->model =
pika_color_profile_get_info (profile, cmsInfoModel);
return profile->priv->model;
}
/**
* pika_color_profile_get_copyright:
* @profile: a #PikaColorProfile
*
* Returns: a string containing @profile's copyright. The
* returned value belongs to @profile and must not be
* modified or freed.
*
* Since: 2.10
**/
const gchar *
pika_color_profile_get_copyright (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (! profile->priv->copyright)
profile->priv->copyright =
pika_color_profile_get_info (profile, cmsInfoCopyright);
return profile->priv->copyright;
}
/**
* pika_color_profile_get_label:
* @profile: a #PikaColorProfile
*
* This function returns a string containing @profile's "title", a
* string that can be used to label the profile in a user interface.
*
* Unlike pika_color_profile_get_description(), this function always
* returns a string (as a fallback, it returns "(unnamed profile)").
*
* Returns: the @profile's label. The returned value belongs to
* @profile and must not be modified or freed.
*
* Since: 2.10
**/
const gchar *
pika_color_profile_get_label (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (! profile->priv->label)
{
const gchar *label = pika_color_profile_get_description (profile);
if (! label || ! strlen (label))
label = _("(unnamed profile)");
profile->priv->label = g_strdup (label);
}
return profile->priv->label;
}
/**
* pika_color_profile_get_summary:
* @profile: a #PikaColorProfile
*
* This function return a string containing a multi-line summary of
* @profile's description, model, manufacturer and copyright, to be
* used as detailed information about the profile in a user
* interface.
*
* Returns: the @profile's summary. The returned value belongs to
* @profile and must not be modified or freed.
*
* Since: 2.10
**/
const gchar *
pika_color_profile_get_summary (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (! profile->priv->summary)
{
GString *string = g_string_new (NULL);
const gchar *text;
text = pika_color_profile_get_description (profile);
if (text)
g_string_append (string, text);
text = pika_color_profile_get_model (profile);
if (text)
{
if (string->len > 0)
g_string_append (string, "\n");
g_string_append_printf (string, _("Model: %s"), text);
}
text = pika_color_profile_get_manufacturer (profile);
if (text)
{
if (string->len > 0)
g_string_append (string, "\n");
g_string_append_printf (string, _("Manufacturer: %s"), text);
}
text = pika_color_profile_get_copyright (profile);
if (text)
{
if (string->len > 0)
g_string_append (string, "\n");
g_string_append_printf (string, _("Copyright: %s"), text);
}
profile->priv->summary = g_string_free (string, FALSE);
}
return profile->priv->summary;
}
/**
* pika_color_profile_is_equal:
* @profile1: a #PikaColorProfile
* @profile2: a #PikaColorProfile
*
* Compares two profiles.
*
* Returns: %TRUE if the profiles are equal, %FALSE otherwise.
*
* Since: 2.10
**/
gboolean
pika_color_profile_is_equal (PikaColorProfile *profile1,
PikaColorProfile *profile2)
{
const gsize header_len = sizeof (cmsICCHeader);
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile1), FALSE);
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile2), FALSE);
return profile1 == profile2 ||
(profile1->priv->length == profile2->priv->length &&
memcmp (profile1->priv->data + header_len,
profile2->priv->data + header_len,
profile1->priv->length - header_len) == 0);
}
/**
* pika_color_profile_is_rgb:
* @profile: a #PikaColorProfile
*
* Returns: %TRUE if the profile's color space is RGB, %FALSE
* otherwise.
*
* Since: 2.10
**/
gboolean
pika_color_profile_is_rgb (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE);
return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigRgbData);
}
/**
* pika_color_profile_is_gray:
* @profile: a #PikaColorProfile
*
* Returns: %TRUE if the profile's color space is grayscale, %FALSE
* otherwise.
*
* Since: 2.10
**/
gboolean
pika_color_profile_is_gray (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE);
return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigGrayData);
}
/**
* pika_color_profile_is_cmyk:
* @profile: a #PikaColorProfile
*
* Returns: %TRUE if the profile's color space is CMYK, %FALSE
* otherwise.
*
* Since: 2.10
**/
gboolean
pika_color_profile_is_cmyk (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE);
return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigCmykData);
}
/**
* pika_color_profile_is_linear:
* @profile: a #PikaColorProfile
*
* This function determines is the ICC profile represented by a PikaColorProfile
* is a linear RGB profile or not, some profiles that are LUTs though linear
* will also return FALSE;
*
* Returns: %TRUE if the profile is a matrix shaping profile with linear
* TRCs, %FALSE otherwise.
*
* Since: 2.10
**/
gboolean
pika_color_profile_is_linear (PikaColorProfile *profile)
{
cmsHPROFILE prof;
cmsToneCurve *curve;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE);
prof = profile->priv->lcms_profile;
if (! cmsIsMatrixShaper (prof))
return FALSE;
if (cmsIsCLUT (prof, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT))
return FALSE;
if (cmsIsCLUT (prof, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT))
return FALSE;
if (pika_color_profile_is_rgb (profile))
{
curve = cmsReadTag(prof, cmsSigRedTRCTag);
if (curve == NULL || ! cmsIsToneCurveLinear (curve))
return FALSE;
curve = cmsReadTag (prof, cmsSigGreenTRCTag);
if (curve == NULL || ! cmsIsToneCurveLinear (curve))
return FALSE;
curve = cmsReadTag (prof, cmsSigBlueTRCTag);
if (curve == NULL || ! cmsIsToneCurveLinear (curve))
return FALSE;
}
else if (pika_color_profile_is_gray (profile))
{
curve = cmsReadTag(prof, cmsSigGrayTRCTag);
if (curve == NULL || ! cmsIsToneCurveLinear (curve))
return FALSE;
}
else
{
return FALSE;
}
return TRUE;
}
static void
pika_color_profile_set_tag (cmsHPROFILE profile,
cmsTagSignature sig,
const gchar *tag)
{
cmsMLU *mlu;
mlu = cmsMLUalloc (NULL, 1);
cmsMLUsetASCII (mlu, "en", "US", tag);
cmsWriteTag (profile, sig, mlu);
cmsMLUfree (mlu);
}
static gboolean
pika_color_profile_get_rgb_matrix_colorants (PikaColorProfile *profile,
PikaMatrix3 *matrix)
{
cmsHPROFILE lcms_profile;
cmsCIEXYZ *red;
cmsCIEXYZ *green;
cmsCIEXYZ *blue;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE);
lcms_profile = profile->priv->lcms_profile;
red = cmsReadTag (lcms_profile, cmsSigRedColorantTag);
green = cmsReadTag (lcms_profile, cmsSigGreenColorantTag);
blue = cmsReadTag (lcms_profile, cmsSigBlueColorantTag);
if (red && green && blue)
{
if (matrix)
{
matrix->coeff[0][0] = red->X;
matrix->coeff[0][1] = red->Y;
matrix->coeff[0][2] = red->Z;
matrix->coeff[1][0] = green->X;
matrix->coeff[1][1] = green->Y;
matrix->coeff[1][2] = green->Z;
matrix->coeff[2][0] = blue->X;
matrix->coeff[2][1] = blue->Y;
matrix->coeff[2][2] = blue->Z;
}
return TRUE;
}
return FALSE;
}
static void
pika_color_profile_make_tag (cmsHPROFILE profile,
cmsTagSignature sig,
const gchar *pika_tag,
const gchar *pika_prefix,
const gchar *pika_prefix_alt,
const gchar *original_tag)
{
if (! original_tag || ! strlen (original_tag) ||
! strcmp (original_tag, pika_tag))
{
/* if there is no original tag (or it is the same as the new
* tag), just use the new tag
*/
pika_color_profile_set_tag (profile, sig, pika_tag);
}
else
{
/* otherwise prefix the existing tag with a pika prefix
* indicating that the profile has been generated
*/
if (g_str_has_prefix (original_tag, pika_prefix))
{
/* don't add multiple PIKA prefixes */
pika_color_profile_set_tag (profile, sig, original_tag);
}
else if (pika_prefix_alt &&
g_str_has_prefix (original_tag, pika_prefix_alt))
{
/* replace PIKA prefix_alt by prefix */
gchar *new_tag = g_strconcat (pika_prefix,
original_tag + strlen (pika_prefix_alt),
NULL);
pika_color_profile_set_tag (profile, sig, new_tag);
g_free (new_tag);
}
else
{
gchar *new_tag = g_strconcat (pika_prefix,
original_tag,
NULL);
pika_color_profile_set_tag (profile, sig, new_tag);
g_free (new_tag);
}
}
}
static PikaColorProfile *
pika_color_profile_new_from_color_profile (PikaColorProfile *profile,
gboolean linear)
{
PikaColorProfile *new_profile;
cmsHPROFILE target_profile;
PikaMatrix3 matrix = { { { 0, } } };
cmsCIEXYZ *whitepoint;
cmsToneCurve *curve;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
if (pika_color_profile_is_rgb (profile))
{
if (! pika_color_profile_get_rgb_matrix_colorants (profile, &matrix))
return NULL;
}
else if (! pika_color_profile_is_gray (profile))
{
return NULL;
}
whitepoint = cmsReadTag (profile->priv->lcms_profile,
cmsSigMediaWhitePointTag);
target_profile = cmsCreateProfilePlaceholder (0);
cmsSetProfileVersion (target_profile, 4.3);
cmsSetDeviceClass (target_profile, cmsSigDisplayClass);
cmsSetPCS (target_profile, cmsSigXYZData);
cmsWriteTag (target_profile, cmsSigMediaWhitePointTag, whitepoint);
if (linear)
{
/* linear light */
curve = cmsBuildGamma (NULL, 1.00);
pika_color_profile_make_tag (target_profile, cmsSigProfileDescriptionTag,
"linear TRC from unnamed profile",
"linear TRC from ",
"sRGB TRC from ",
pika_color_profile_get_description (profile));
}
else
{
cmsFloat64Number srgb_parameters[5] =
{ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
/* sRGB curve */
curve = cmsBuildParametricToneCurve (NULL, 4, srgb_parameters);
pika_color_profile_make_tag (target_profile, cmsSigProfileDescriptionTag,
"sRGB TRC from unnamed profile",
"sRGB TRC from ",
"linear TRC from ",
pika_color_profile_get_description (profile));
}
if (pika_color_profile_is_rgb (profile))
{
cmsCIEXYZ red;
cmsCIEXYZ green;
cmsCIEXYZ blue;
cmsSetColorSpace (target_profile, cmsSigRgbData);
red.X = matrix.coeff[0][0];
red.Y = matrix.coeff[0][1];
red.Z = matrix.coeff[0][2];
green.X = matrix.coeff[1][0];
green.Y = matrix.coeff[1][1];
green.Z = matrix.coeff[1][2];
blue.X = matrix.coeff[2][0];
blue.Y = matrix.coeff[2][1];
blue.Z = matrix.coeff[2][2];
cmsWriteTag (target_profile, cmsSigRedColorantTag, &red);
cmsWriteTag (target_profile, cmsSigGreenColorantTag, &green);
cmsWriteTag (target_profile, cmsSigBlueColorantTag, &blue);
cmsWriteTag (target_profile, cmsSigRedTRCTag, curve);
cmsWriteTag (target_profile, cmsSigGreenTRCTag, curve);
cmsWriteTag (target_profile, cmsSigBlueTRCTag, curve);
}
else
{
cmsSetColorSpace (target_profile, cmsSigGrayData);
cmsWriteTag (target_profile, cmsSigGrayTRCTag, curve);
}
cmsFreeToneCurve (curve);
pika_color_profile_make_tag (target_profile, cmsSigDeviceMfgDescTag,
"PIKA",
"PIKA from ", NULL,
pika_color_profile_get_manufacturer (profile));
pika_color_profile_make_tag (target_profile, cmsSigDeviceModelDescTag,
"Generated by PIKA",
"PIKA from ", NULL,
pika_color_profile_get_model (profile));
pika_color_profile_make_tag (target_profile, cmsSigCopyrightTag,
"Public Domain",
"PIKA from ", NULL,
pika_color_profile_get_copyright (profile));
new_profile = pika_color_profile_new_from_lcms_profile (target_profile, NULL);
cmsCloseProfile (target_profile);
return new_profile;
}
/**
* pika_color_profile_new_srgb_trc_from_color_profile:
* @profile: a #PikaColorProfile
*
* This function creates a new RGB #PikaColorProfile with a sRGB gamma
* TRC and @profile's RGB chromacities and whitepoint.
*
* Returns: (nullable) (transfer full): the new #PikaColorProfile, or %NULL if
* @profile is not an RGB profile or not matrix-based.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_srgb_trc_from_color_profile (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
return pika_color_profile_new_from_color_profile (profile, FALSE);
}
/**
* pika_color_profile_new_linear_from_color_profile:
* @profile: a #PikaColorProfile
*
* This function creates a new RGB #PikaColorProfile with a linear TRC
* and @profile's RGB chromacities and whitepoint.
*
* Returns: (nullable) (transfer full): the new #PikaColorProfile, or %NULL if
* @profile is not an RGB profile or not matrix-based.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_linear_from_color_profile (PikaColorProfile *profile)
{
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
return pika_color_profile_new_from_color_profile (profile, TRUE);
}
static cmsHPROFILE *
pika_color_profile_new_rgb_srgb_internal (void)
{
cmsHPROFILE profile;
/* white point is D65 from the sRGB specs */
cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
/* primaries are ITUR BT.7095 (xYY), which are also the primaries
* from the sRGB specs, modified to properly account for hexadecimal
* quantization during the profile making process.
*/
cmsCIExyYTRIPLE primaries =
{
/* R { 0.6400, 0.3300, 1.0 }, */
/* G { 0.3000, 0.6000, 1.0 }, */
/* B { 0.1500, 0.0600, 1.0 } */
/* R */ { 0.639998686, 0.330010138, 1.0 },
/* G */ { 0.300003784, 0.600003357, 1.0 },
/* B */ { 0.150002046, 0.059997204, 1.0 }
};
cmsFloat64Number srgb_parameters[5] =
{ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
cmsToneCurve *curve[3];
/* sRGB curve */
curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
srgb_parameters);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
pika_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
"PIKA built-in sRGB");
pika_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
"PIKA");
pika_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
"sRGB");
pika_color_profile_set_tag (profile, cmsSigCopyrightTag,
"Public Domain");
/* The following line produces a V2 profile with a point curve TRC.
* Profiles with point curve TRCs can't be used in LCMS2 unbounded
* mode ICC profile conversions. A V2 profile might be appropriate
* for embedding in sRGB images saved to disk, if the image is to be
* opened by an image editing application that doesn't understand V4
* profiles.
*
* cmsSetProfileVersion (srgb_profile, 2.1);
*/
return profile;
}
/**
* pika_color_profile_new_rgb_srgb:
*
* This function is a replacement for cmsCreate_sRGBProfile() and
* returns an sRGB profile that is functionally the same as the
* ArgyllCMS sRGB.icm profile. "Functionally the same" means it has
* the same red, green, and blue colorants and the V4 "chad"
* equivalent of the ArgyllCMS V2 white point. The profile TRC is also
* functionally equivalent to the ArgyllCMS sRGB.icm TRC and is the
* same as the LCMS sRGB built-in profile TRC.
*
* The actual primaries in the sRGB specification are
* red xy: {0.6400, 0.3300, 1.0}
* green xy: {0.3000, 0.6000, 1.0}
* blue xy: {0.1500, 0.0600, 1.0}
*
* The sRGB primaries given below are "pre-quantized" to compensate
* for hexadecimal quantization during the profile-making process.
* Unless the profile-making code compensates for this quantization,
* the resulting profile's red, green, and blue colorants will deviate
* slightly from the correct XYZ values.
*
* LCMS2 doesn't compensate for hexadecimal quantization. The
* "pre-quantized" primaries below were back-calculated from the
* ArgyllCMS sRGB.icm profile. The resulting sRGB profile's colorants
* exactly matches the ArgyllCMS sRGB.icm profile colorants.
*
* Returns: the sRGB #PikaColorProfile.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_rgb_srgb (void)
{
static PikaColorProfile *profile = NULL;
const guint8 *data;
gsize length = 0;
if (G_UNLIKELY (profile == NULL))
{
cmsHPROFILE lcms_profile = pika_color_profile_new_rgb_srgb_internal ();
profile = pika_color_profile_new_from_lcms_profile (lcms_profile, NULL);
cmsCloseProfile (lcms_profile);
}
data = pika_color_profile_get_icc_profile (profile, &length);
return pika_color_profile_new_from_icc_profile (data, length, NULL);
}
static cmsHPROFILE
pika_color_profile_new_rgb_srgb_linear_internal (void)
{
cmsHPROFILE profile;
/* white point is D65 from the sRGB specs */
cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
/* primaries are ITUR BT.7095 (xYY), which are also the primaries
* from the sRGB specs, modified to properly account for hexadecimal
* quantization during the profile making process.
*/
cmsCIExyYTRIPLE primaries =
{
/* R { 0.6400, 0.3300, 1.0 }, */
/* G { 0.3000, 0.6000, 1.0 }, */
/* B { 0.1500, 0.0600, 1.0 } */
/* R */ { 0.639998686, 0.330010138, 1.0 },
/* G */ { 0.300003784, 0.600003357, 1.0 },
/* B */ { 0.150002046, 0.059997204, 1.0 }
};
cmsToneCurve *curve[3];
/* linear light */
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
pika_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
"PIKA built-in Linear sRGB");
pika_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
"PIKA");
pika_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
"Linear sRGB");
pika_color_profile_set_tag (profile, cmsSigCopyrightTag,
"Public Domain");
return profile;
}
/**
* pika_color_profile_new_rgb_srgb_linear:
*
* This function creates a profile for babl_model("RGB"). Please
* somebody write something smarter here.
*
* Returns: the linear RGB #PikaColorProfile.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_rgb_srgb_linear (void)
{
static PikaColorProfile *profile = NULL;
const guint8 *data;
gsize length = 0;
if (G_UNLIKELY (profile == NULL))
{
cmsHPROFILE lcms_profile = pika_color_profile_new_rgb_srgb_linear_internal ();
profile = pika_color_profile_new_from_lcms_profile (lcms_profile, NULL);
cmsCloseProfile (lcms_profile);
}
data = pika_color_profile_get_icc_profile (profile, &length);
return pika_color_profile_new_from_icc_profile (data, length, NULL);
}
static cmsHPROFILE *
pika_color_profile_new_rgb_adobe_internal (void)
{
cmsHPROFILE profile;
/* white point is D65 from the sRGB specs */
cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
/* AdobeRGB1998 and sRGB have the same white point.
*
* The primaries below are technically correct, but because of
* hexadecimal rounding these primaries don't make a profile that
* matches the original.
*
* cmsCIExyYTRIPLE primaries = {
* { 0.6400, 0.3300, 1.0 },
* { 0.2100, 0.7100, 1.0 },
* { 0.1500, 0.0600, 1.0 }
* };
*/
cmsCIExyYTRIPLE primaries =
{
{ 0.639996511, 0.329996864, 1.0 },
{ 0.210005295, 0.710004866, 1.0 },
{ 0.149997606, 0.060003644, 1.0 }
};
cmsToneCurve *curve[3];
/* gamma 2.2 */
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.19921875);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
pika_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
"Compatible with Adobe RGB (1998)");
pika_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
"PIKA");
pika_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
"Compatible with Adobe RGB (1998)");
pika_color_profile_set_tag (profile, cmsSigCopyrightTag,
"Public Domain");
return profile;
}
/**
* pika_color_profile_new_rgb_adobe:
*
* This function creates a profile compatible with AbobeRGB (1998).
*
* Returns: the AdobeRGB-compatible #PikaColorProfile.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_rgb_adobe (void)
{
static PikaColorProfile *profile = NULL;
const guint8 *data;
gsize length = 0;
if (G_UNLIKELY (profile == NULL))
{
cmsHPROFILE lcms_profile = pika_color_profile_new_rgb_adobe_internal ();
profile = pika_color_profile_new_from_lcms_profile (lcms_profile, NULL);
cmsCloseProfile (lcms_profile);
}
data = pika_color_profile_get_icc_profile (profile, &length);
return pika_color_profile_new_from_icc_profile (data, length, NULL);
}
static cmsHPROFILE *
pika_color_profile_new_d65_gray_srgb_trc_internal (void)
{
cmsHPROFILE profile;
/* white point is D65 from the sRGB specs */
cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
cmsFloat64Number srgb_parameters[5] =
{ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
cmsToneCurve *curve = cmsBuildParametricToneCurve (NULL, 4,
srgb_parameters);
profile = cmsCreateGrayProfile (&whitepoint, curve);
cmsFreeToneCurve (curve);
pika_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
"PIKA built-in D65 Grayscale with sRGB TRC");
pika_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
"PIKA");
pika_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
"D65 Grayscale with sRGB TRC");
pika_color_profile_set_tag (profile, cmsSigCopyrightTag,
"Public Domain");
return profile;
}
/**
* pika_color_profile_new_d65_gray_srgb_trc
*
* This function creates a grayscale #PikaColorProfile with an
* sRGB TRC. See pika_color_profile_new_rgb_srgb().
*
* Returns: the sRGB-gamma grayscale #PikaColorProfile.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_d65_gray_srgb_trc (void)
{
static PikaColorProfile *profile = NULL;
const guint8 *data;
gsize length = 0;
if (G_UNLIKELY (profile == NULL))
{
cmsHPROFILE lcms_profile = pika_color_profile_new_d65_gray_srgb_trc_internal ();
profile = pika_color_profile_new_from_lcms_profile (lcms_profile, NULL);
cmsCloseProfile (lcms_profile);
}
data = pika_color_profile_get_icc_profile (profile, &length);
return pika_color_profile_new_from_icc_profile (data, length, NULL);
}
static cmsHPROFILE
pika_color_profile_new_d65_gray_linear_internal (void)
{
cmsHPROFILE profile;
/* white point is D65 from the sRGB specs */
cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
cmsToneCurve *curve = cmsBuildGamma (NULL, 1.0);
profile = cmsCreateGrayProfile (&whitepoint, curve);
cmsFreeToneCurve (curve);
pika_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
"PIKA built-in D65 Linear Grayscale");
pika_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
"PIKA");
pika_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
"D65 Linear Grayscale");
pika_color_profile_set_tag (profile, cmsSigCopyrightTag,
"Public Domain");
return profile;
}
/**
* pika_color_profile_new_d65_gray_srgb_gray:
*
* This function creates a profile for babl_model("Y"). Please
* somebody write something smarter here.
*
* Returns: the linear grayscale #PikaColorProfile.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_d65_gray_linear (void)
{
static PikaColorProfile *profile = NULL;
const guint8 *data;
gsize length = 0;
if (G_UNLIKELY (profile == NULL))
{
cmsHPROFILE lcms_profile = pika_color_profile_new_d65_gray_linear_internal ();
profile = pika_color_profile_new_from_lcms_profile (lcms_profile, NULL);
cmsCloseProfile (lcms_profile);
}
data = pika_color_profile_get_icc_profile (profile, &length);
return pika_color_profile_new_from_icc_profile (data, length, NULL);
}
static cmsHPROFILE *
pika_color_profile_new_d50_gray_lab_trc_internal (void)
{
cmsHPROFILE profile;
/* white point is D50 from the ICC profile illuminant specs */
cmsCIExyY whitepoint = {0.345702915, 0.358538597, 1.0};
cmsFloat64Number lab_parameters[5] =
{ 3.0, 1.0 / 1.16, 0.16 / 1.16, 2700.0 / 24389.0, 0.08000 };
cmsToneCurve *curve = cmsBuildParametricToneCurve (NULL, 4,
lab_parameters);
profile = cmsCreateGrayProfile (&whitepoint, curve);
cmsFreeToneCurve (curve);
pika_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
"PIKA built-in D50 Grayscale with LAB L TRC");
pika_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
"PIKA");
pika_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
"D50 Grayscale with LAB L TRC");
pika_color_profile_set_tag (profile, cmsSigCopyrightTag,
"Public Domain");
return profile;
}
/**
* pika_color_profile_new_d50_gray_lab_trc
*
* This function creates a grayscale #PikaColorProfile with the
* D50 ICC profile illuminant as the profile white point and the
* LAB companding curve as the TRC.
*
* Returns: a gray profile with the D50 ICC profile illuminant
* as the profile white point and the LAB companding curve as the TRC.
* as the TRC.
*
* Since: 2.10
**/
PikaColorProfile *
pika_color_profile_new_d50_gray_lab_trc (void)
{
static PikaColorProfile *profile = NULL;
const guint8 *data;
gsize length = 0;
if (G_UNLIKELY (profile == NULL))
{
cmsHPROFILE lcms_profile = pika_color_profile_new_d50_gray_lab_trc_internal ();
profile = pika_color_profile_new_from_lcms_profile (lcms_profile, NULL);
cmsCloseProfile (lcms_profile);
}
data = pika_color_profile_get_icc_profile (profile, &length);
return pika_color_profile_new_from_icc_profile (data, length, NULL);
}
/**
* pika_color_profile_get_space:
* @profile: a #PikaColorProfile
* @intent: a #PikaColorRenderingIntent
* @error: return location for #GError
*
* This function returns the #Babl space of @profile, for the
* specified @intent.
*
* Returns: the new #Babl space.
*
* Since: 2.10.6
**/
const Babl *
pika_color_profile_get_space (PikaColorProfile *profile,
PikaColorRenderingIntent intent,
GError **error)
{
const Babl *space;
const gchar *babl_error = NULL;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
space = babl_space_from_icc ((const gchar *) profile->priv->data,
profile->priv->length,
(BablIccIntent) intent,
&babl_error);
if (! space)
g_set_error (error, PIKA_COLOR_PROFILE_ERROR, 0,
"%s: %s",
pika_color_profile_get_label (profile), babl_error);
return space;
}
/**
* pika_color_profile_get_format:
* @profile: a #PikaColorProfile
* @format: a #Babl format
* @intent: a #PikaColorRenderingIntent
* @error: return location for #GError
*
* This function takes a #PikaColorProfile and a #Babl format and
* returns a new #Babl format with @profile's RGB primaries and TRC,
* and @format's pixel layout.
*
* Returns: the new #Babl format.
*
* Since: 2.10
**/
const Babl *
pika_color_profile_get_format (PikaColorProfile *profile,
const Babl *format,
PikaColorRenderingIntent intent,
GError **error)
{
const Babl *space;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), NULL);
g_return_val_if_fail (format != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
space = pika_color_profile_get_space (profile, intent, error);
if (! space)
return NULL;
return babl_format_with_space ((const gchar *) format, space);
}
/**
* pika_color_profile_get_lcms_format:
* @format: a #Babl format
* @lcms_format: return location for an lcms format
*
* This function takes a #Babl format and returns the lcms format to
* be used with that @format. It also returns a #Babl format to be
* used instead of the passed @format, which usually is the same as
* @format, unless lcms doesn't support @format.
*
* Note that this function currently only supports RGB, RGBA, R'G'B',
* R'G'B'A, Y, YA, Y', Y'A and the cairo-RGB24 and cairo-ARGB32 formats.
*
* Returns: (nullable): the #Babl format to be used instead of @format, or %NULL
* if the passed @format is not supported at all.
*
* Since: 2.10
**/
const Babl *
pika_color_profile_get_lcms_format (const Babl *format,
guint32 *lcms_format)
{
const Babl *output_format = NULL;
const Babl *type;
const Babl *model;
const Babl *space;
gboolean has_alpha;
gboolean rgb = FALSE;
gboolean gray = FALSE;
gboolean cmyk = FALSE;
gboolean linear = FALSE;
gboolean srgb_trc = FALSE;
g_return_val_if_fail (format != NULL, NULL);
g_return_val_if_fail (lcms_format != NULL, NULL);
has_alpha = babl_format_has_alpha (format);
type = babl_format_get_type (format, 0);
model = babl_format_get_model (format);
space = babl_format_get_space (format);
if (format == babl_format ("cairo-RGB24"))
{
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
*lcms_format = TYPE_BGRA_8;
#else
*lcms_format = TYPE_ARGB_8;
#endif
return format;
}
else if (format == babl_format ("cairo-ARGB32"))
{
rgb = TRUE;
}
else if (model == babl_model ("RGB") ||
model == babl_model ("RGBA") ||
model == babl_model ("RaGaBaA"))
{
rgb = TRUE;
linear = TRUE;
}
else if (model == babl_model ("R~G~B~") ||
model == babl_model ("R~G~B~A") ||
model == babl_model ("R~aG~aB~aA"))
{
rgb = TRUE;
srgb_trc = TRUE;
}
else if (model == babl_model ("R'G'B'") ||
model == babl_model ("R'G'B'A") ||
model == babl_model ("R'aG'aB'aA"))
{
rgb = TRUE;
}
else if (model == babl_model ("Y") ||
model == babl_model ("YA") ||
model == babl_model ("YaA"))
{
gray = TRUE;
linear = TRUE;
}
else if (model == babl_model ("Y~") ||
model == babl_model ("Y~A") ||
model == babl_model ("Y~aA"))
{
gray = TRUE;
srgb_trc = TRUE;
}
else if (model == babl_model ("Y'") ||
model == babl_model ("Y'A") ||
model == babl_model ("Y'aA"))
{
gray = TRUE;
}
else if (model == babl_model ("CMYK"))
#if 0
/* FIXME missing from babl */
|| model == babl_model ("CMYKA"))
#endif
{
cmyk = TRUE;
}
else if (model == babl_model ("CIE Lab") ||
model == babl_model ("CIE Lab alpha") ||
model == babl_model ("CIE LCH(ab)") ||
model == babl_model ("CIE LCH(ab) alpha"))
{
if (has_alpha)
{
*lcms_format = TYPE_RGBA_FLT;
return babl_format_with_space ("RGBA float", space);
}
else
{
*lcms_format = TYPE_RGB_FLT;
return babl_format_with_space ("RGB float", space);
}
}
else if (babl_format_is_palette (format))
{
if (has_alpha)
{
*lcms_format = TYPE_RGBA_8;
return babl_format_with_space ("R'G'B'A u8", space);
}
else
{
*lcms_format = TYPE_RGB_8;
return babl_format_with_space ("R'G'B' u8", space);
}
}
else
{
g_printerr ("format not supported: %s\n"
"has_alpha = %s\n"
"type = %s\n"
"model = %s\n",
babl_get_name (format),
has_alpha ? "TRUE" : "FALSE",
babl_get_name (type),
babl_get_name (model));
g_return_val_if_reached (NULL);
}
*lcms_format = 0;
#define FIND_FORMAT_FOR_TYPE(babl_t, lcms_t) \
do \
{ \
if (has_alpha) \
{ \
if (rgb) \
{ \
*lcms_format = TYPE_RGBA_##lcms_t; \
\
if (linear) \
output_format = babl_format_with_space ("RGBA " babl_t, \
space); \
else if (srgb_trc) \
output_format = babl_format_with_space ("R~G~B~A " babl_t, \
space); \
else \
output_format = babl_format_with_space ("R'G'B'A " babl_t, \
space); \
} \
else if (gray) \
{ \
*lcms_format = TYPE_GRAYA_##lcms_t; \
\
if (linear) \
output_format = babl_format_with_space ("YA " babl_t, \
space); \
else if (srgb_trc) \
output_format = babl_format_with_space ("Y~A " babl_t, \
space); \
else \
output_format = babl_format_with_space ("Y'A " babl_t, \
space); \
} \
else if (cmyk) \
{ \
*lcms_format = TYPE_CMYKA_##lcms_t; \
\
output_format = format; \
} \
} \
else \
{ \
if (rgb) \
{ \
*lcms_format = TYPE_RGB_##lcms_t; \
\
if (linear) \
output_format = babl_format_with_space ("RGB " babl_t, \
space); \
else if (srgb_trc) \
output_format = babl_format_with_space ("R~G~B~ " babl_t, \
space); \
else \
output_format = babl_format_with_space ("R'G'B' " babl_t, \
space); \
} \
else if (gray) \
{ \
*lcms_format = TYPE_GRAY_##lcms_t; \
\
if (linear) \
output_format = babl_format_with_space ("Y " babl_t, \
space); \
else if (srgb_trc) \
output_format = babl_format_with_space ("Y~ " babl_t, \
space); \
else \
output_format = babl_format_with_space ("Y' " babl_t, \
space); \
} \
else if (cmyk) \
{ \
*lcms_format = TYPE_CMYK_##lcms_t; \
\
output_format = format; \
} \
} \
} \
while (FALSE)
if (type == babl_type ("u8"))
FIND_FORMAT_FOR_TYPE ("u8", 8);
else if (type == babl_type ("u16"))
FIND_FORMAT_FOR_TYPE ("u16", 16);
else if (type == babl_type ("half")) /* 16-bit floating point (half) */
FIND_FORMAT_FOR_TYPE ("half", HALF_FLT);
else if (type == babl_type ("float"))
FIND_FORMAT_FOR_TYPE ("float", FLT);
else if (type == babl_type ("double"))
FIND_FORMAT_FOR_TYPE ("double", DBL);
if (*lcms_format == 0)
{
g_printerr ("%s: format %s not supported, "
"falling back to float\n",
G_STRFUNC, babl_get_name (format));
rgb = ! gray;
FIND_FORMAT_FOR_TYPE ("float", FLT);
g_return_val_if_fail (output_format != NULL, NULL);
}
#undef FIND_FORMAT_FOR_TYPE
return output_format;
}