PIKApp/libpikacolor/pikacolorprofile.c

1827 lines
55 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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;
length = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
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;
}