diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
commit | 5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch) | |
tree | cbffb45144febf451e54061db2b21395faf94bfe /libgimpcolor/gimpcolorprofile.c | |
parent | Initial commit. (diff) | |
download | gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.tar.xz gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.zip |
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | libgimpcolor/gimpcolorprofile.c | 1789 |
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; +} |