summaryrefslogtreecommitdiffstats
path: root/libgimpcolor/gimpcolorprofile.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgimpcolor/gimpcolorprofile.c')
-rw-r--r--libgimpcolor/gimpcolorprofile.c1789
1 files changed, 1789 insertions, 0 deletions
diff --git a/libgimpcolor/gimpcolorprofile.c b/libgimpcolor/gimpcolorprofile.c
new file mode 100644
index 0000000..69236b4
--- /dev/null
+++ b/libgimpcolor/gimpcolorprofile.c
@@ -0,0 +1,1789 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolorprofile.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 "libgimpbase/gimpbase.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpcolorprofile.h"
+
+#include "libgimp/libgimp-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: gimpcolorprofile
+ * @title: GimpColorProfile
+ * @short_description: Definitions and Functions relating to LCMS.
+ *
+ * Definitions and Functions relating to LCMS.
+ **/
+
+/**
+ * GimpColorProfile:
+ *
+ * Simply a typedef to #gpointer, but actually is a cmsHPROFILE. It's
+ * used in public GIMP APIs in order to avoid having to include LCMS
+ * headers.
+ **/
+
+
+struct _GimpColorProfilePrivate
+{
+ cmsHPROFILE lcms_profile;
+ guint8 *data;
+ gsize length;
+
+ gchar *description;
+ gchar *manufacturer;
+ gchar *model;
+ gchar *copyright;
+ gchar *label;
+ gchar *summary;
+};
+
+
+static void gimp_color_profile_finalize (GObject *object);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorProfile, gimp_color_profile, G_TYPE_OBJECT)
+
+#define parent_class gimp_color_profile_parent_class
+
+
+#define GIMP_COLOR_PROFILE_ERROR gimp_color_profile_error_quark ()
+
+static GQuark
+gimp_color_profile_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("gimp-color-profile-error-quark");
+
+ return quark;
+}
+
+static void
+gimp_color_profile_class_init (GimpColorProfileClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_color_profile_finalize;
+}
+
+static void
+gimp_color_profile_init (GimpColorProfile *profile)
+{
+ profile->priv = gimp_color_profile_get_instance_private (profile);
+}
+
+static void
+gimp_color_profile_finalize (GObject *object)
+{
+ GimpColorProfile *profile = GIMP_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);
+}
+
+
+/**
+ * gimp_color_profile_new_from_file:
+ * @file: a #GFile
+ * @error: return location for #GError
+ *
+ * This function opens an ICC color profile from @file.
+ *
+ * Return value: the #GimpColorProfile, or %NULL. On error, %NULL is
+ * returned and @error is set.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_from_file (GFile *file,
+ GError **error)
+{
+ GimpColorProfile *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_memdup (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_size (info);
+ 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 (GIMP_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, GIMP_COLOR_PROFILE_ERROR, 0,
+ _("'%s' does not appear to be an ICC color profile"),
+ gimp_file_get_utf8_name (file));
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_from_icc_profile:
+ * @data: pointer to 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.
+ *
+ * Return value: the #GimpColorProfile, or %NULL.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_from_icc_profile (const guint8 *data,
+ gsize length,
+ GError **error)
+{
+ cmsHPROFILE lcms_profile = 0;
+ GimpColorProfile *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 (GIMP_TYPE_COLOR_PROFILE, NULL);
+
+ profile->priv->lcms_profile = lcms_profile;
+ profile->priv->data = g_memdup (data, length);
+ profile->priv->length = length;
+ }
+ else
+ {
+ g_set_error_literal (error, GIMP_COLOR_PROFILE_ERROR, 0,
+ _("Data does not appear to be an ICC color profile"));
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_from_lcms_profile:
+ * @lcms_profile: an LCMS cmsHPROFILE pointer
+ * @error: return location for #GError
+ *
+ * This function creates a GimpColorProfile from a cmsHPROFILE. On
+ * error, %NULL is returned and @error is set. The passed
+ * @lcms_profile pointer is not retained by the created
+ * #GimpColorProfile.
+ *
+ * Return value: the #GimpColorProfile, or %NULL.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_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)
+ {
+ GimpColorProfile *profile;
+
+ profile = g_object_new (GIMP_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, GIMP_COLOR_PROFILE_ERROR, 0,
+ _("Could not save color profile to memory"));
+
+ return NULL;
+}
+
+/**
+ * gimp_color_profile_save_to_file:
+ * @profile: a #GimpColorProfile
+ * @file: a #GFile
+ * @error: return location for #GError
+ *
+ * This function saves @profile to @file as ICC profile.
+ *
+ * Return value: %TRUE on success, %FALSE if an error occurred.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_save_to_file (GimpColorProfile *profile,
+ GFile *file,
+ GError **error)
+{
+ g_return_val_if_fail (GIMP_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);
+}
+
+/**
+ * gimp_color_profile_get_icc_profile:
+ * @profile: a #GimpColorProfile
+ * @length: 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.
+ *
+ * Return value: a pointer to the IIC profile data.
+ *
+ * Since: 2.10
+ **/
+const guint8 *
+gimp_color_profile_get_icc_profile (GimpColorProfile *profile,
+ gsize *length)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+ g_return_val_if_fail (length != NULL, NULL);
+
+ *length = profile->priv->length;
+
+ return profile->priv->data;
+}
+
+/**
+ * gimp_color_profile_get_lcms_profile:
+ * @profile: a #GimpColorProfile
+ *
+ * This function returns @profile's cmsHPROFILE. The returned
+ * value belongs to @profile and must not be modified or freed.
+ *
+ * Return value: a pointer to the cmsHPROFILE.
+ *
+ * Since: 2.10
+ **/
+gpointer
+gimp_color_profile_get_lcms_profile (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ return profile->priv->lcms_profile;
+}
+
+static gchar *
+gimp_color_profile_get_info (GimpColorProfile *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 = gimp_any_to_utf8 (data, -1, NULL);
+
+ g_free (data);
+ }
+
+ return text;
+}
+
+/**
+ * gimp_color_profile_get_description:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's description. The
+ * returned value belongs to @profile and must not be
+ * modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_description (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->description)
+ profile->priv->description =
+ gimp_color_profile_get_info (profile, cmsInfoDescription);
+
+ return profile->priv->description;
+}
+
+/**
+ * gimp_color_profile_get_manufacturer:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's manufacturer. The
+ * returned value belongs to @profile and must not be
+ * modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_manufacturer (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->manufacturer)
+ profile->priv->manufacturer =
+ gimp_color_profile_get_info (profile, cmsInfoManufacturer);
+
+ return profile->priv->manufacturer;
+}
+
+/**
+ * gimp_color_profile_get_model:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's model. The returned
+ * value belongs to @profile and must not be modified or
+ * freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_model (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->model)
+ profile->priv->model =
+ gimp_color_profile_get_info (profile, cmsInfoModel);
+
+ return profile->priv->model;
+}
+
+/**
+ * gimp_color_profile_get_copyright:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's copyright. The
+ * returned value belongs to @profile and must not be
+ * modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_copyright (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->copyright)
+ profile->priv->copyright =
+ gimp_color_profile_get_info (profile, cmsInfoCopyright);
+
+ return profile->priv->copyright;
+}
+
+/**
+ * gimp_color_profile_get_label:
+ * @profile: a #GimpColorProfile
+ *
+ * This function returns a string containing @profile's "title", a
+ * string that can be used to label the profile in a user interface.
+ *
+ * Unlike gimp_color_profile_get_description(), this function always
+ * returns a string (as a fallback, it returns "(unnamed profile)").
+ *
+ * Return value: the @profile's label. The returned value belongs to
+ * @profile and must not be modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_label (GimpColorProfile *profile)
+{
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->label)
+ {
+ const gchar *label = gimp_color_profile_get_description (profile);
+
+ if (! label || ! strlen (label))
+ label = _("(unnamed profile)");
+
+ profile->priv->label = g_strdup (label);
+ }
+
+ return profile->priv->label;
+}
+
+/**
+ * gimp_color_profile_get_summary:
+ * @profile: a #GimpColorProfile
+ *
+ * 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.
+ *
+ * Return value: the @profile's summary. The returned value belongs to
+ * @profile and must not be modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_summary (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->summary)
+ {
+ GString *string = g_string_new (NULL);
+ const gchar *text;
+
+ text = gimp_color_profile_get_description (profile);
+ if (text)
+ g_string_append (string, text);
+
+ text = gimp_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 = gimp_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 = gimp_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;
+}
+
+/**
+ * gimp_color_profile_is_equal:
+ * @profile1: a #GimpColorProfile
+ * @profile2: a #GimpColorProfile
+ *
+ * Compares two profiles.
+ *
+ * Return value: %TRUE if the profiles are equal, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_equal (GimpColorProfile *profile1,
+ GimpColorProfile *profile2)
+{
+ const gsize header_len = sizeof (cmsICCHeader);
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile1), FALSE);
+ g_return_val_if_fail (GIMP_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);
+}
+
+/**
+ * gimp_color_profile_is_rgb:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: %TRUE if the profile's color space is RGB, %FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_rgb (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigRgbData);
+}
+
+/**
+ * gimp_color_profile_is_gray:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: %TRUE if the profile's color space is grayscale, %FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_gray (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigGrayData);
+}
+
+/**
+ * gimp_color_profile_is_cmyk:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: %TRUE if the profile's color space is CMYK, %FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_cmyk (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigCmykData);
+}
+
+
+/**
+ * gimp_color_profile_is_linear:
+ * @profile: a #GimpColorProfile
+ *
+ * This function determines is the ICC profile represented by a GimpColorProfile
+ * is a linear RGB profile or not, some profiles that are LUTs though linear
+ * will also return FALSE;
+ *
+ * Return value: %TRUE if the profile is a matrix shaping profile with linear
+ * TRCs, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_linear (GimpColorProfile *profile)
+{
+ cmsHPROFILE prof;
+ cmsToneCurve *curve;
+
+ g_return_val_if_fail (GIMP_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 (gimp_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 (gimp_color_profile_is_gray (profile))
+ {
+ curve = cmsReadTag(prof, cmsSigGrayTRCTag);
+ if (curve == NULL || ! cmsIsToneCurveLinear (curve))
+ return FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_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
+gimp_color_profile_get_rgb_matrix_colorants (GimpColorProfile *profile,
+ GimpMatrix3 *matrix)
+{
+ cmsHPROFILE lcms_profile;
+ cmsCIEXYZ *red;
+ cmsCIEXYZ *green;
+ cmsCIEXYZ *blue;
+
+ g_return_val_if_fail (GIMP_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
+gimp_color_profile_make_tag (cmsHPROFILE profile,
+ cmsTagSignature sig,
+ const gchar *gimp_tag,
+ const gchar *gimp_prefix,
+ const gchar *gimp_prefix_alt,
+ const gchar *original_tag)
+{
+ if (! original_tag || ! strlen (original_tag) ||
+ ! strcmp (original_tag, gimp_tag))
+ {
+ /* if there is no original tag (or it is the same as the new
+ * tag), just use the new tag
+ */
+
+ gimp_color_profile_set_tag (profile, sig, gimp_tag);
+ }
+ else
+ {
+ /* otherwise prefix the existing tag with a gimp prefix
+ * indicating that the profile has been generated
+ */
+
+ if (g_str_has_prefix (original_tag, gimp_prefix))
+ {
+ /* don't add multiple GIMP prefixes */
+ gimp_color_profile_set_tag (profile, sig, original_tag);
+ }
+ else if (gimp_prefix_alt &&
+ g_str_has_prefix (original_tag, gimp_prefix_alt))
+ {
+ /* replace GIMP prefix_alt by prefix */
+ gchar *new_tag = g_strconcat (gimp_prefix,
+ original_tag + strlen (gimp_prefix_alt),
+ NULL);
+
+ gimp_color_profile_set_tag (profile, sig, new_tag);
+ g_free (new_tag);
+ }
+ else
+ {
+ gchar *new_tag = g_strconcat (gimp_prefix,
+ original_tag,
+ NULL);
+
+ gimp_color_profile_set_tag (profile, sig, new_tag);
+ g_free (new_tag);
+ }
+ }
+}
+
+static GimpColorProfile *
+gimp_color_profile_new_from_color_profile (GimpColorProfile *profile,
+ gboolean linear)
+{
+ GimpColorProfile *new_profile;
+ cmsHPROFILE target_profile;
+ GimpMatrix3 matrix = { { { 0, } } };
+ cmsCIEXYZ *whitepoint;
+ cmsToneCurve *curve;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (gimp_color_profile_is_rgb (profile))
+ {
+ if (! gimp_color_profile_get_rgb_matrix_colorants (profile, &matrix))
+ return NULL;
+ }
+ else if (! gimp_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);
+
+ gimp_color_profile_make_tag (target_profile, cmsSigProfileDescriptionTag,
+ "linear TRC from unnamed profile",
+ "linear TRC from ",
+ "sRGB TRC from ",
+ gimp_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);
+
+ gimp_color_profile_make_tag (target_profile, cmsSigProfileDescriptionTag,
+ "sRGB TRC from unnamed profile",
+ "sRGB TRC from ",
+ "linear TRC from ",
+ gimp_color_profile_get_description (profile));
+ }
+
+ if (gimp_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);
+
+ gimp_color_profile_make_tag (target_profile, cmsSigDeviceMfgDescTag,
+ "GIMP",
+ "GIMP from ", NULL,
+ gimp_color_profile_get_manufacturer (profile));
+ gimp_color_profile_make_tag (target_profile, cmsSigDeviceModelDescTag,
+ "Generated by GIMP",
+ "GIMP from ", NULL,
+ gimp_color_profile_get_model (profile));
+ gimp_color_profile_make_tag (target_profile, cmsSigCopyrightTag,
+ "Public Domain",
+ "GIMP from ", NULL,
+ gimp_color_profile_get_copyright (profile));
+
+ new_profile = gimp_color_profile_new_from_lcms_profile (target_profile, NULL);
+
+ cmsCloseProfile (target_profile);
+
+ return new_profile;
+}
+
+/**
+ * gimp_color_profile_new_srgb_trc_from_color_profile:
+ * @profile: a #GimpColorProfile
+ *
+ * This function creates a new RGB #GimpColorProfile with a sRGB gamma
+ * TRC and @profile's RGB chromacities and whitepoint.
+ *
+ * Return value: the new #GimpColorProfile, or %NULL if @profile is not
+ * an RGB profile or not matrix-based.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_srgb_trc_from_color_profile (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ return gimp_color_profile_new_from_color_profile (profile, FALSE);
+}
+
+/**
+ * gimp_color_profile_new_linear_from_color_profile:
+ * @profile: a #GimpColorProfile
+ *
+ * This function creates a new RGB #GimpColorProfile with a linear TRC
+ * and @profile's RGB chromacities and whitepoint.
+ *
+ * Return value: the new #GimpColorProfile, or %NULL if @profile is not
+ * an RGB profile or not matrix-based.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_linear_from_color_profile (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ return gimp_color_profile_new_from_color_profile (profile, TRUE);
+}
+
+static cmsHPROFILE *
+gimp_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 ITU‐R BT.709‐5 (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]);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "sRGB");
+ gimp_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;
+}
+
+/**
+ * gimp_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.
+ *
+ * Return value: the sRGB #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_rgb_srgb (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_rgb_srgb_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE
+gimp_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 ITU‐R BT.709‐5 (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]);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in Linear sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "Linear sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_rgb_srgb_linear:
+ *
+ * This function creates a profile for babl_model("RGB"). Please
+ * somebody write something smarter here.
+ *
+ * Return value: the linear RGB #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_rgb_srgb_linear (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_rgb_srgb_linear_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE *
+gimp_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]);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "Compatible with Adobe RGB (1998)");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "Compatible with Adobe RGB (1998)");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_rgb_adobe:
+ *
+ * This function creates a profile compatible with AbobeRGB (1998).
+ *
+ * Return value: the AdobeRGB-compatible #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_rgb_adobe (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_rgb_adobe_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE *
+gimp_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);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in D65 Grayscale with sRGB TRC");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "D65 Grayscale with sRGB TRC");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_d65_gray_srgb_trc
+ *
+ * This function creates a grayscale #GimpColorProfile with an
+ * sRGB TRC. See gimp_color_profile_new_rgb_srgb().
+ *
+ * Return value: the sRGB-gamma grayscale #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_d65_gray_srgb_trc (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_d65_gray_srgb_trc_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE
+gimp_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);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in D65 Linear Grayscale");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "D65 Linear Grayscale");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_d65_gray_srgb_gray:
+ *
+ * This function creates a profile for babl_model("Y"). Please
+ * somebody write something smarter here.
+ *
+ * Return value: the linear grayscale #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_d65_gray_linear (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_d65_gray_linear_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE *
+gimp_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);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in D50 Grayscale with LAB L TRC");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "D50 Grayscale with LAB L TRC");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+
+/**
+ * gimp_color_profile_new_d50_gray_lab_trc
+ *
+ * This function creates a grayscale #GimpColorProfile with the
+ * D50 ICC profile illuminant as the profile white point and the
+ * LAB companding curve as the TRC.
+ *
+ * Return value: 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
+ **/
+GimpColorProfile *
+gimp_color_profile_new_d50_gray_lab_trc (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_d50_gray_lab_trc_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+/**
+ * gimp_color_profile_get_space:
+ * @profile: a #GimpColorProfile
+ * @intent: a #GimpColorRenderingIntent
+ * @error: return location for #GError
+ *
+ * This function returns the #Babl space of @profile, for the
+ * specified @intent.
+ *
+ * Return value: the new #Babl space.
+ *
+ * Since: 2.10.6
+ **/
+const Babl *
+gimp_color_profile_get_space (GimpColorProfile *profile,
+ GimpColorRenderingIntent intent,
+ GError **error)
+{
+ const Babl *space;
+ const gchar *babl_error = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ space = babl_icc_make_space ((const gchar *) profile->priv->data,
+ profile->priv->length,
+ (BablIccIntent) intent,
+ &babl_error);
+
+ if (! space)
+ g_set_error (error, GIMP_COLOR_PROFILE_ERROR, 0,
+ "%s: %s",
+ gimp_color_profile_get_label (profile), babl_error);
+
+ return space;
+}
+
+/**
+ * gimp_color_profile_get_format:
+ * @profile: a #GimpColorProfile
+ * @format: a #Babl format
+ * @intent: a #GimpColorRenderingIntent
+ * @error: return location for #GError
+ *
+ * This function takes a #GimpColorProfile and a #Babl format and
+ * returns a new #Babl format with @profile's RGB primaries and TRC,
+ * and @format's pixel layout.
+ *
+ * Return value: the new #Babl format.
+ *
+ * Since: 2.10
+ **/
+const Babl *
+gimp_color_profile_get_format (GimpColorProfile *profile,
+ const Babl *format,
+ GimpColorRenderingIntent intent,
+ GError **error)
+{
+ const Babl *space;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+ g_return_val_if_fail (format != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ space = gimp_color_profile_get_space (profile, intent, error);
+
+ if (! space)
+ return NULL;
+
+ return babl_format_with_space (babl_get_name (format), space);
+}
+
+/**
+ * gimp_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.
+ *
+ * Return value: 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 *
+gimp_color_profile_get_lcms_format (const Babl *format,
+ guint32 *lcms_format)
+{
+ const Babl *output_format = NULL;
+ const Babl *type;
+ const Babl *model;
+ gboolean has_alpha;
+ gboolean rgb = FALSE;
+ gboolean gray = FALSE;
+ gboolean cmyk = FALSE;
+ gboolean linear = 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);
+
+ 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;
+ }
+ 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;
+ }
+ 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 ("RGBA float");
+ }
+ else
+ {
+ *lcms_format = TYPE_RGB_FLT;
+
+ return babl_format ("RGB float");
+ }
+ }
+ else if (babl_format_is_palette (format))
+ {
+ if (has_alpha)
+ {
+ *lcms_format = TYPE_RGBA_8;
+
+ return babl_format ("R'G'B'A u8");
+ }
+ else
+ {
+ *lcms_format = TYPE_RGB_8;
+
+ return babl_format ("R'G'B' u8");
+ }
+ }
+ 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 ("RGBA " babl_t); \
+ else \
+ output_format = babl_format ("R'G'B'A " babl_t); \
+ } \
+ else if (gray) \
+ { \
+ *lcms_format = TYPE_GRAYA_##lcms_t; \
+ \
+ if (linear) \
+ output_format = babl_format ("YA " babl_t); \
+ else \
+ output_format = babl_format ("Y'A " babl_t); \
+ } \
+ 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 ("RGB " babl_t); \
+ else \
+ output_format = babl_format ("R'G'B' " babl_t); \
+ } \
+ else if (gray) \
+ { \
+ *lcms_format = TYPE_GRAY_##lcms_t; \
+ \
+ if (linear) \
+ output_format = babl_format ("Y " babl_t); \
+ else \
+ output_format = babl_format ("Y' " babl_t); \
+ } \
+ 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;
+}