mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 09:23:24 +00:00

All babl formats now have a space equivalent to a color profile, determining the format's primaries and TRCs. This commit makes GIMP aware of this. libgimp: - enum GimpPrecision: rename GAMMA values to NON_LINEAR and keep GAMMA as deprecated aliases, add PERCEPTUAL values so we now have LINEAR, NON_LINEAR and PERCPTUAL for each encoding, matching the babl encoding variants RGB, R'G'B' and R~G~B~. - gimp_color_transform_can_gegl_copy() now returns TRUE if both profiles can return a babl space, increasing the amount of fast babl color conversions significantly. - TODO: no solution yet for getting libgimp drawable proxy buffers in the right format with space. plug-ins: - follow the GimpPrecision change. - TODO: everything else unchanged and partly broken or sub-optimal, like setting a new image's color profile too late. app: - add enum GimpTRCType { LINEAR, NON_LINEAR, PERCEPTUAL } as replacement for all "linear" booleans. - change gimp-babl functions to take babl spaces and GimpTRCType parameters and support all sorts of new perceptual ~ formats. - a lot of places changed in the early days of goat invasion didn't take advantage of gimp-babl utility functions and constructed formats manually. They all needed revisiting and many now use much simpler code calling gimp-babl API. - change gimp_babl_format_get_color_profile() to really extract a newly allocated color profile from the format, and add gimp_babl_get_builtin_color_profile() which does the same as gimp_babl_format_get_color_profile() did before. Visited all callers to decide whether they are looking for the format's actual profile, or for one of the builtin profiles, simplifying code that only needs builtin profiles. - drawables have a new get_space_api(), get_linear() is now get_trc(). - images now have a "layer space" and an API to get it, gimp_image_get_layer_format() returns formats in that space. - an image's layer space is created from the image's color profile, change gimpimage-color-profile to deal with that correctly - change many babl_format() calls to babl_format_with_space() and take the space from passed formats or drawables - add function gimp_layer_fix_format_space() which replaces the layer's buffer with one that has the image's layer format, but doesn't change pixel values - use gimp_layer_fix_format_space() to make sure layers loaded from XCF and created by plug-ins have the right space when added to the image, because it's impossible to always assign the right space upon layer creation - "assign color profile" and "discard color profile" now require use of gimp_layer_fix_format_space() too because the profile is now embedded in all formats via the space. Add gimp_image_assign_color_profile() which does all that and call it instead of a simple gimp_image_set_color_profile(), also from the PDB set-color-profile functions, which are essentially "assign" and "discard" calls. - generally, make sure a new image's color profile is set before adding layers to it, gimp_image_set_color_profile() is more than before considered know-what-you-are-doing API. - take special precaution in all places that call gimp_drawable_convert_type(), we now must pass a new_profile from all callers that convert layers within the same image (such as image_convert_type, image_convert_precision), because the layer's new space can't be determined from the image's layer format during the call. - change all "linear" properties to "trc", in all config objects like for levels and curves, in the histogram, in the widgets. This results in some GUI that now has three choices instead of two. TODO: we might want to reduce that back to two later. - keep "linear" boolean properties around as compat if needed for file pasring, but always convert the parsed parsed boolean to GimpTRCType. - TODO: the image's "enable color management" switch is currently broken, will fix that in another commit.
1748 lines
51 KiB
C
1748 lines
51 KiB
C
/* 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 (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;
|
||
|
||
g_type_class_add_private (klass, sizeof (GimpColorProfilePrivate));
|
||
}
|
||
|
||
static void
|
||
gimp_color_profile_init (GimpColorProfile *profile)
|
||
{
|
||
profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile,
|
||
GIMP_TYPE_COLOR_PROFILE,
|
||
GimpColorProfilePrivate);
|
||
}
|
||
|
||
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 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;
|
||
const gchar *model;
|
||
gchar *new_model;
|
||
|
||
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_set_tag (target_profile, cmsSigProfileDescriptionTag,
|
||
"linear TRC variant generated by GIMP");
|
||
}
|
||
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_set_tag (target_profile, cmsSigProfileDescriptionTag,
|
||
"sRGB TRC variant generated by GIMP");
|
||
}
|
||
|
||
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);
|
||
|
||
model = gimp_color_profile_get_model (profile);
|
||
|
||
if (model && g_str_has_prefix (model, "Generated from '"))
|
||
{
|
||
/* don't add multiple "Generated from 'foo'" */
|
||
new_model = g_strdup (model);
|
||
}
|
||
else
|
||
{
|
||
new_model = g_strdup_printf ("Generated from '%s'",
|
||
gimp_color_profile_get_description (profile));
|
||
}
|
||
|
||
gimp_color_profile_set_tag (target_profile, cmsSigDeviceMfgDescTag,
|
||
"GIMP");
|
||
gimp_color_profile_set_tag (target_profile, cmsSigDeviceModelDescTag,
|
||
new_model);
|
||
gimp_color_profile_set_tag (target_profile, cmsSigCopyrightTag,
|
||
"Public Domain");
|
||
|
||
g_free (new_model);
|
||
|
||
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_space_from_icc ((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_format_get_encoding (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: %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;
|
||
}
|