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 /app/core/gimpimage-color-profile.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream.tar.xz gimp-upstream.zip |
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/core/gimpimage-color-profile.c')
-rw-r--r-- | app/core/gimpimage-color-profile.c | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/app/core/gimpimage-color-profile.c b/app/core/gimpimage-color-profile.c new file mode 100644 index 0000000..a7030f3 --- /dev/null +++ b/app/core/gimpimage-color-profile.c @@ -0,0 +1,822 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpimage-color-profile.c + * Copyright (C) 2015 Michael Natterer <mitch@gimp.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> + +#include <cairo.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gegl.h> + +#include "libgimpconfig/gimpconfig.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpbase/gimpbase.h" + +#include "core-types.h" + +#include "config/gimpdialogconfig.h" + +#include "gegl/gimp-babl.h" +#include "gegl/gimp-gegl-loops.h" + +#include "gimp.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimperror.h" +#include "gimpimage.h" +#include "gimpimage-color-profile.h" +#include "gimpimage-colormap.h" +#include "gimpimage-private.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimpobjectqueue.h" +#include "gimpprogress.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_image_convert_profile_layers (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress); +static void gimp_image_convert_profile_colormap (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress); + +static void gimp_image_create_color_transforms (GimpImage *image); + + +/* public functions */ + +gboolean +gimp_image_get_is_color_managed (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + return GIMP_IMAGE_GET_PRIVATE (image)->is_color_managed; +} + +void +gimp_image_set_is_color_managed (GimpImage *image, + gboolean is_color_managed, + gboolean push_undo) +{ + GimpImagePrivate *private; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + is_color_managed = is_color_managed ? TRUE : FALSE; + + if (is_color_managed != private->is_color_managed) + { + if (push_undo) + gimp_image_undo_push_image_color_managed (image, NULL); + + private->is_color_managed = is_color_managed; + + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); + } +} + +gboolean +gimp_image_validate_icc_parasite (GimpImage *image, + const GimpParasite *icc_parasite, + gboolean *is_builtin, + GError **error) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (icc_parasite != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strcmp (gimp_parasite_name (icc_parasite), + GIMP_ICC_PROFILE_PARASITE_NAME) != 0) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Parasite's name is not 'icc-profile'")); + return FALSE; + } + + if (gimp_parasite_flags (icc_parasite) != (GIMP_PARASITE_PERSISTENT | + GIMP_PARASITE_UNDOABLE)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Parasite's flags are not (PERSISTENT | UNDOABLE)")); + return FALSE; + } + + return gimp_image_validate_icc_profile (image, + gimp_parasite_data (icc_parasite), + gimp_parasite_data_size (icc_parasite), + is_builtin, + error); +} + +const GimpParasite * +gimp_image_get_icc_parasite (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return gimp_image_parasite_find (image, GIMP_ICC_PROFILE_PARASITE_NAME); +} + +void +gimp_image_set_icc_parasite (GimpImage *image, + const GimpParasite *icc_parasite) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + + if (icc_parasite) + { + g_return_if_fail (gimp_image_validate_icc_parasite (image, icc_parasite, + NULL, NULL) == TRUE); + + gimp_image_parasite_attach (image, icc_parasite, TRUE); + } + else + { + gimp_image_parasite_detach (image, GIMP_ICC_PROFILE_PARASITE_NAME, TRUE); + } +} + +gboolean +gimp_image_validate_icc_profile (GimpImage *image, + const guint8 *data, + gsize length, + gboolean *is_builtin, + GError **error) +{ + GimpColorProfile *profile; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (data != NULL || length == 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + profile = gimp_color_profile_new_from_icc_profile (data, length, error); + + if (! profile) + { + g_prefix_error (error, _("ICC profile validation failed: ")); + return FALSE; + } + + if (! gimp_image_validate_color_profile (image, profile, is_builtin, error)) + { + g_object_unref (profile); + return FALSE; + } + + g_object_unref (profile); + + return TRUE; +} + +const guint8 * +gimp_image_get_icc_profile (GimpImage *image, + gsize *length) +{ + const GimpParasite *parasite; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + + parasite = gimp_image_parasite_find (image, GIMP_ICC_PROFILE_PARASITE_NAME); + + if (parasite) + { + if (length) + *length = gimp_parasite_data_size (parasite); + + return gimp_parasite_data (parasite); + } + + if (length) + *length = 0; + + return NULL; +} + +gboolean +gimp_image_set_icc_profile (GimpImage *image, + const guint8 *data, + gsize length, + GError **error) +{ + GimpParasite *parasite = NULL; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (data == NULL || length != 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (data) + { + gboolean is_builtin; + + parasite = gimp_parasite_new (GIMP_ICC_PROFILE_PARASITE_NAME, + GIMP_PARASITE_PERSISTENT | + GIMP_PARASITE_UNDOABLE, + length, data); + + if (! gimp_image_validate_icc_parasite (image, parasite, &is_builtin, + error)) + { + gimp_parasite_free (parasite); + return FALSE; + } + + /* don't tag the image with the built-in profile */ + if (is_builtin) + { + gimp_parasite_free (parasite); + parasite = NULL; + } + } + + gimp_image_set_icc_parasite (image, parasite); + + if (parasite) + gimp_parasite_free (parasite); + + return TRUE; +} + +gboolean +gimp_image_validate_color_profile (GimpImage *image, + GimpColorProfile *profile, + gboolean *is_builtin, + GError **error) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + format = gimp_image_get_layer_format (image, TRUE); + + return gimp_image_validate_color_profile_by_format (format, + profile, is_builtin, + error); +} + +GimpColorProfile * +gimp_image_get_color_profile (GimpImage *image) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + return GIMP_IMAGE_GET_PRIVATE (image)->color_profile; +} + +gboolean +gimp_image_set_color_profile (GimpImage *image, + GimpColorProfile *profile, + GError **error) +{ + const guint8 *data = NULL; + gsize length = 0; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile), + FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (profile) + data = gimp_color_profile_get_icc_profile (profile, &length); + + return gimp_image_set_icc_profile (image, data, length, error); +} + +gboolean +gimp_image_validate_color_profile_by_format (const Babl *format, + GimpColorProfile *profile, + gboolean *is_builtin, + GError **error) +{ + g_return_val_if_fail (format != NULL, FALSE); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (gimp_babl_format_get_base_type (format) == GIMP_GRAY) + { + if (! gimp_color_profile_is_gray (profile)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Color profile is not for grayscale color space")); + return FALSE; + } + } + else + { + if (! gimp_color_profile_is_rgb (profile)) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("ICC profile validation failed: " + "Color profile is not for RGB color space")); + return FALSE; + } + } + + if (is_builtin) + { + GimpColorProfile *builtin; + + builtin = gimp_babl_format_get_color_profile (format); + + *is_builtin = gimp_color_profile_is_equal (profile, builtin); + } + + return TRUE; +} + +GimpColorProfile * +gimp_image_get_builtin_color_profile (GimpImage *image) +{ + const Babl *format; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + format = gimp_image_get_layer_format (image, FALSE); + + return gimp_babl_format_get_color_profile (format); +} + +gboolean +gimp_image_convert_color_profile (GimpImage *image, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress, + GError **error) +{ + GimpColorProfile *src_profile; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); + g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), FALSE); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (! gimp_image_validate_color_profile (image, dest_profile, NULL, error)) + return FALSE; + + src_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); + + if (! src_profile || gimp_color_profile_is_equal (src_profile, dest_profile)) + return TRUE; + + if (progress) + gimp_progress_start (progress, FALSE, + _("Converting from '%s' to '%s'"), + gimp_color_profile_get_label (src_profile), + gimp_color_profile_get_label (dest_profile)); + + gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT, + _("Color profile conversion")); + + switch (gimp_image_get_base_type (image)) + { + case GIMP_RGB: + case GIMP_GRAY: + gimp_image_convert_profile_layers (image, + src_profile, dest_profile, + intent, bpc, + progress); + break; + + case GIMP_INDEXED: + gimp_image_convert_profile_colormap (image, + src_profile, dest_profile, + intent, bpc, + progress); + break; + } + + gimp_image_set_is_color_managed (image, TRUE, TRUE); + gimp_image_set_color_profile (image, dest_profile, NULL); + /* omg... */ + gimp_image_parasite_detach (image, "icc-profile-name", TRUE); + + gimp_image_undo_group_end (image); + + if (progress) + gimp_progress_end (progress); + + return TRUE; +} + +void +gimp_image_import_color_profile (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + gboolean interactive) +{ + g_return_if_fail (GIMP_IS_IMAGE (image)); + g_return_if_fail (GIMP_IS_CONTEXT (context)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + + if (gimp_image_get_is_color_managed (image) && + gimp_image_get_color_profile (image)) + { + GimpColorProfilePolicy policy; + GimpColorProfile *dest_profile = NULL; + GimpColorRenderingIntent intent; + gboolean bpc; + + policy = GIMP_DIALOG_CONFIG (image->gimp->config)->color_profile_policy; + intent = GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC; + bpc = TRUE; + + if (policy == GIMP_COLOR_PROFILE_POLICY_ASK) + { + if (interactive) + { + gboolean dont_ask = FALSE; + + policy = gimp_query_profile_policy (image->gimp, image, context, + &dest_profile, + &intent, &bpc, + &dont_ask); + + if (dont_ask) + { + g_object_set (G_OBJECT (image->gimp->config), + "color-profile-policy", policy, + NULL); + } + } + else + { + policy = GIMP_COLOR_PROFILE_POLICY_KEEP; + } + } + + if (policy == GIMP_COLOR_PROFILE_POLICY_CONVERT) + { + if (! dest_profile) + { + dest_profile = gimp_image_get_builtin_color_profile (image); + g_object_ref (dest_profile); + } + + gimp_image_convert_color_profile (image, dest_profile, + intent, bpc, + progress, NULL); + + g_object_unref (dest_profile); + } + } +} + +GimpColorTransform * +gimp_image_get_color_transform_to_srgb_u8 (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_to_srgb_u8; + + return NULL; +} + +GimpColorTransform * +gimp_image_get_color_transform_from_srgb_u8 (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_from_srgb_u8; + + return NULL; +} + +GimpColorTransform * +gimp_image_get_color_transform_to_srgb_double (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_to_srgb_double; + + return NULL; +} + +GimpColorTransform * +gimp_image_get_color_transform_from_srgb_double (GimpImage *image) +{ + GimpImagePrivate *private; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + + private = GIMP_IMAGE_GET_PRIVATE (image); + + gimp_image_create_color_transforms (image); + + if (private->is_color_managed) + return private->transform_from_srgb_double; + + return NULL; +} + +void +gimp_image_color_profile_pixel_to_srgb (GimpImage *image, + const Babl *pixel_format, + gpointer pixel, + GimpRGB *color) +{ + GimpColorTransform *transform; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + transform = gimp_image_get_color_transform_to_srgb_double (image); + + if (transform) + { + gimp_color_transform_process_pixels (transform, + pixel_format, + pixel, + babl_format ("R'G'B'A double"), + color, + 1); + } + else + { + gimp_rgba_set_pixel (color, pixel_format, pixel); + } +} + +void +gimp_image_color_profile_srgb_to_pixel (GimpImage *image, + const GimpRGB *color, + const Babl *pixel_format, + gpointer pixel) +{ + GimpColorTransform *transform; + + g_return_if_fail (GIMP_IS_IMAGE (image)); + + transform = gimp_image_get_color_transform_from_srgb_double (image); + + if (transform) + { + gimp_color_transform_process_pixels (transform, + babl_format ("R'G'B'A double"), + color, + pixel_format, + pixel, + 1); + } + else + { + gimp_rgba_get_pixel (color, pixel_format, pixel); + } +} + + +/* internal API */ + +void +_gimp_image_free_color_profile (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + g_clear_object (&private->color_profile); + + _gimp_image_free_color_transforms (image); +} + +void +_gimp_image_free_color_transforms (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + g_clear_object (&private->transform_to_srgb_u8); + g_clear_object (&private->transform_from_srgb_u8); + g_clear_object (&private->transform_to_srgb_double); + g_clear_object (&private->transform_from_srgb_double); + + private->color_transforms_created = FALSE; +} + +void +_gimp_image_update_color_profile (GimpImage *image, + const GimpParasite *icc_parasite) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + _gimp_image_free_color_profile (image); + + if (icc_parasite) + { + private->color_profile = + gimp_color_profile_new_from_icc_profile (gimp_parasite_data (icc_parasite), + gimp_parasite_data_size (icc_parasite), + NULL); + } + + gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image)); +} + + +/* private functions */ + +static void +gimp_image_convert_profile_layers (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress) +{ + GimpObjectQueue *queue; + GList *layers; + GList *list; + GimpDrawable *drawable; + + queue = gimp_object_queue_new (progress); + progress = GIMP_PROGRESS (queue); + + layers = gimp_image_get_layer_list (image); + + for (list = layers; list; list = g_list_next (list)) + { + if (! gimp_viewable_get_children (list->data)) + gimp_object_queue_push (queue, list->data); + } + + g_list_free (layers); + + while ((drawable = gimp_object_queue_pop (queue))) + { + GimpItem *item = GIMP_ITEM (drawable); + GeglBuffer *buffer; + gboolean alpha; + + alpha = gimp_drawable_has_alpha (drawable); + + buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + gimp_item_get_width (item), + gimp_item_get_height (item)), + gimp_image_get_layer_format (image, alpha)); + + gimp_drawable_push_undo (drawable, NULL, NULL, + 0, 0, + gimp_item_get_width (GIMP_ITEM (drawable)), + gimp_item_get_height (GIMP_ITEM (drawable))); + + gimp_gegl_convert_color_profile (gimp_drawable_get_buffer (drawable), + NULL, + src_profile, + buffer, + NULL, + dest_profile, + intent, bpc, + progress); + + gimp_drawable_set_buffer (drawable, TRUE, NULL, buffer); + g_object_unref (buffer); + } + + g_object_unref (queue); +} + +static void +gimp_image_convert_profile_colormap (GimpImage *image, + GimpColorProfile *src_profile, + GimpColorProfile *dest_profile, + GimpColorRenderingIntent intent, + gboolean bpc, + GimpProgress *progress) +{ + GimpColorTransform *transform; + GimpColorTransformFlags flags = 0; + guchar *cmap; + gint n_colors; + + n_colors = gimp_image_get_colormap_size (image); + cmap = g_memdup (gimp_image_get_colormap (image), n_colors * 3); + + if (bpc) + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + transform = gimp_color_transform_new (src_profile, + babl_format ("R'G'B' u8"), + dest_profile, + babl_format ("R'G'B' u8"), + intent, flags); + + if (transform) + { + gimp_color_transform_process_pixels (transform, + babl_format ("R'G'B' u8"), cmap, + babl_format ("R'G'B' u8"), cmap, + n_colors); + g_object_unref (transform); + + gimp_image_set_colormap (image, cmap, n_colors, TRUE); + } + else + { + g_warning ("cmsCreateTransform() failed!"); + } + + g_free (cmap); +} + +static void +gimp_image_create_color_transforms (GimpImage *image) +{ + GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); + + if (private->color_profile && + ! private->color_transforms_created) + { + GimpColorProfile *srgb_profile; + GimpColorTransformFlags flags = 0; + + srgb_profile = gimp_color_profile_new_rgb_srgb (); + + flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE; + flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; + + private->transform_to_srgb_u8 = + gimp_color_transform_new (private->color_profile, + gimp_image_get_layer_format (image, TRUE), + srgb_profile, + babl_format ("R'G'B'A u8"), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + private->transform_from_srgb_u8 = + gimp_color_transform_new (srgb_profile, + babl_format ("R'G'B'A u8"), + private->color_profile, + gimp_image_get_layer_format (image, TRUE), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + private->transform_to_srgb_double = + gimp_color_transform_new (private->color_profile, + gimp_image_get_layer_format (image, TRUE), + srgb_profile, + babl_format ("R'G'B'A double"), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + private->transform_from_srgb_double = + gimp_color_transform_new (srgb_profile, + babl_format ("R'G'B'A double"), + private->color_profile, + gimp_image_get_layer_format (image, TRUE), + GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, + flags); + + g_object_unref (srgb_profile); + + private->color_transforms_created = TRUE; + } +} |