/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikaimage-color-profile.c * Copyright (C) 2015-2018 Michael Natterer * * 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 . */ #include "config.h" #include #include #include #include #include "libpikaconfig/pikaconfig.h" #include "libpikacolor/pikacolor.h" #include "libpikabase/pikabase.h" #include "core-types.h" #include "config/pikadialogconfig.h" #include "gegl/pika-babl.h" #include "gegl/pika-gegl-loops.h" #include "pika.h" #include "pikacontext.h" #include "pikaerror.h" #include "pikalayer.h" #include "pikaimage.h" #include "pikaimage-color-profile.h" #include "pikaimage-colormap.h" #include "pikaimage-private.h" #include "pikaimage-undo.h" #include "pikaimage-undo-push.h" #include "pikaobjectqueue.h" #include "pikaprogress.h" #include "pika-intl.h" /* local function prototypes */ static void pika_image_convert_profile_layers (PikaImage *image, PikaColorProfile *src_profile, PikaColorProfile *dest_profile, PikaColorRenderingIntent intent, gboolean bpc, PikaProgress *progress); static void pika_image_convert_profile_colormap (PikaImage *image, PikaColorProfile *src_profile, PikaColorProfile *dest_profile, PikaColorRenderingIntent intent, gboolean bpc, PikaProgress *progress); static void pika_image_fix_layer_format_spaces (PikaImage *image, PikaProgress *progress); static void pika_image_create_color_transforms (PikaImage *image); /* public functions */ gboolean pika_image_get_use_srgb_profile (PikaImage *image, gboolean *hidden_profile) { PikaImagePrivate *private; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); private = PIKA_IMAGE_GET_PRIVATE (image); if (hidden_profile) *hidden_profile = (private->hidden_profile != NULL); return private->color_profile == NULL; } void pika_image_set_use_srgb_profile (PikaImage *image, gboolean use_srgb) { PikaImagePrivate *private; gboolean old_use_srgb; g_return_if_fail (PIKA_IS_IMAGE (image)); private = PIKA_IMAGE_GET_PRIVATE (image); old_use_srgb = (private->color_profile == NULL); use_srgb = use_srgb ? TRUE : FALSE; if (use_srgb == old_use_srgb) return; if (use_srgb) { PikaColorProfile *profile = pika_image_get_color_profile (image); if (profile) { pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_CONVERT, _("Enable 'Use sRGB Profile'")); g_object_ref (profile); pika_image_assign_color_profile (image, NULL, NULL, NULL); _pika_image_set_hidden_profile (image, profile, TRUE); g_object_unref (profile); pika_image_undo_group_end (image); } } else { PikaColorProfile *hidden = _pika_image_get_hidden_profile (image); if (hidden) { pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_CONVERT, _("Disable 'Use sRGB Profile'")); g_object_ref (hidden); pika_image_assign_color_profile (image, hidden, NULL, NULL); g_object_unref (hidden); pika_image_undo_group_end (image); } } } PikaColorProfile * _pika_image_get_hidden_profile (PikaImage *image) { PikaImagePrivate *private; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); private = PIKA_IMAGE_GET_PRIVATE (image); return private->hidden_profile; } void _pika_image_set_hidden_profile (PikaImage *image, PikaColorProfile *profile, gboolean push_undo) { PikaImagePrivate *private; g_return_if_fail (PIKA_IS_IMAGE (image)); g_return_if_fail (profile == NULL || PIKA_IS_COLOR_PROFILE (profile)); private = PIKA_IMAGE_GET_PRIVATE (image); if (profile != private->hidden_profile) { if (push_undo) pika_image_undo_push_image_hidden_profile (image, NULL); g_set_object (&private->hidden_profile, profile); } } gboolean pika_image_validate_icc_parasite (PikaImage *image, const PikaParasite *icc_parasite, const gchar *profile_type, gboolean *is_builtin, GError **error) { const guint8 *data; guint32 data_size; g_return_val_if_fail (PIKA_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 (pika_parasite_get_name (icc_parasite), profile_type) != 0) { gchar *invalid_parasite_name; invalid_parasite_name = g_strdup_printf (_("ICC profile validation failed: " "Parasite's name is not '%s'"), profile_type); g_set_error_literal (error, PIKA_ERROR, PIKA_FAILED, invalid_parasite_name); g_free (invalid_parasite_name); return FALSE; } if (pika_parasite_get_flags (icc_parasite) != (PIKA_PARASITE_PERSISTENT | PIKA_PARASITE_UNDOABLE)) { g_set_error_literal (error, PIKA_ERROR, PIKA_FAILED, _("ICC profile validation failed: " "Parasite's flags are not (PERSISTENT | UNDOABLE)")); return FALSE; } data = pika_parasite_get_data (icc_parasite, &data_size); return pika_image_validate_icc_profile (image, data, data_size, profile_type, is_builtin, error); } const PikaParasite * pika_image_get_icc_parasite (PikaImage *image) { g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); return pika_image_parasite_find (image, PIKA_ICC_PROFILE_PARASITE_NAME); } void pika_image_set_icc_parasite (PikaImage *image, const PikaParasite *icc_parasite, const gchar *profile_type) { g_return_if_fail (PIKA_IS_IMAGE (image)); if (icc_parasite) { g_return_if_fail (pika_image_validate_icc_parasite (image, icc_parasite, profile_type, NULL, NULL) == TRUE); pika_image_parasite_attach (image, icc_parasite, TRUE); } else { pika_image_parasite_detach (image, profile_type, TRUE); } } gboolean pika_image_validate_icc_profile (PikaImage *image, const guint8 *data, gsize length, const gchar *profile_type, gboolean *is_builtin, GError **error) { PikaColorProfile *profile; g_return_val_if_fail (PIKA_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 = pika_color_profile_new_from_icc_profile (data, length, error); if (! profile) { if (g_strcmp0 (profile_type, PIKA_ICC_PROFILE_PARASITE_NAME) == 0) g_prefix_error (error, _("ICC profile validation failed: ")); else if (g_strcmp0 (profile_type, PIKA_SIMULATION_ICC_PROFILE_PARASITE_NAME) == 0) g_prefix_error (error, _("Simulation ICC profile validation failed: ")); return FALSE; } if (g_strcmp0 (profile_type, PIKA_ICC_PROFILE_PARASITE_NAME) == 0) { if (! pika_image_validate_color_profile (image, profile, is_builtin, error)) { g_object_unref (profile); return FALSE; } } else { if (is_builtin) *is_builtin = FALSE; } g_object_unref (profile); return TRUE; } const guint8 * pika_image_get_icc_profile (PikaImage *image, gsize *length) { const PikaParasite *parasite; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); parasite = pika_image_parasite_find (image, PIKA_ICC_PROFILE_PARASITE_NAME); if (parasite) { const guint8 *data; guint32 data_size; data = pika_parasite_get_data (parasite, &data_size); if (length) *length = (gsize) data_size; return data; } if (length) *length = 0; return NULL; } gboolean pika_image_set_icc_profile (PikaImage *image, const guint8 *data, gsize length, const gchar *profile_type, GError **error) { PikaParasite *parasite = NULL; g_return_val_if_fail (PIKA_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 = pika_parasite_new (profile_type, PIKA_PARASITE_PERSISTENT | PIKA_PARASITE_UNDOABLE, length, data); if (! pika_image_validate_icc_parasite (image, parasite, profile_type, &is_builtin, error)) { pika_parasite_free (parasite); return FALSE; } /* don't tag the image with the built-in profile */ if (is_builtin) { pika_parasite_free (parasite); parasite = NULL; } } pika_image_set_icc_parasite (image, parasite, profile_type); if (parasite) pika_parasite_free (parasite); return TRUE; } gboolean pika_image_validate_color_profile (PikaImage *image, PikaColorProfile *profile, gboolean *is_builtin, GError **error) { const Babl *format; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); format = pika_image_get_layer_format (image, TRUE); return pika_image_validate_color_profile_by_format (format, profile, is_builtin, error); } PikaColorProfile * pika_image_get_color_profile (PikaImage *image) { g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); return PIKA_IMAGE_GET_PRIVATE (image)->color_profile; } gboolean pika_image_set_color_profile (PikaImage *image, PikaColorProfile *profile, GError **error) { const guint8 *data = NULL; gsize length = 0; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); g_return_val_if_fail (profile == NULL || PIKA_IS_COLOR_PROFILE (profile), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (profile) data = pika_color_profile_get_icc_profile (profile, &length); return pika_image_set_icc_profile (image, data, length, PIKA_ICC_PROFILE_PARASITE_NAME, error); } PikaColorProfile * pika_image_get_simulation_profile (PikaImage *image) { g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); return PIKA_IMAGE_GET_PRIVATE (image)->simulation_profile; } gboolean pika_image_set_simulation_profile (PikaImage *image, PikaColorProfile *profile) { PikaImagePrivate *private; const guint8 *data = NULL; gsize length = 0; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); g_return_val_if_fail (profile == NULL || PIKA_IS_COLOR_PROFILE (profile), FALSE); private = PIKA_IMAGE_GET_PRIVATE (image); if (profile != private->simulation_profile) { g_set_object (&private->simulation_profile, profile); pika_color_managed_simulation_profile_changed (PIKA_COLOR_MANAGED (image)); } if (profile) data = pika_color_profile_get_icc_profile (profile, &length); return pika_image_set_icc_profile (image, data, length, PIKA_SIMULATION_ICC_PROFILE_PARASITE_NAME, NULL); } PikaColorRenderingIntent pika_image_get_simulation_intent (PikaImage *image) { g_return_val_if_fail (PIKA_IS_IMAGE (image), PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC); return PIKA_IMAGE_GET_PRIVATE (image)->simulation_intent; } void pika_image_set_simulation_intent (PikaImage *image, PikaColorRenderingIntent intent) { PikaImagePrivate *private; g_return_if_fail (PIKA_IS_IMAGE (image)); private = PIKA_IMAGE_GET_PRIVATE (image); if (intent != private->simulation_intent) { PikaParasite *parasite; gchar i; private->simulation_intent = intent; pika_color_managed_simulation_intent_changed (PIKA_COLOR_MANAGED (image)); i = (gchar) intent; parasite = pika_parasite_new ("image-simulation-intent", PIKA_PARASITE_PERSISTENT, 1, (gpointer) &i); pika_image_parasite_attach (image, parasite, FALSE); pika_parasite_free (parasite); } } gboolean pika_image_get_simulation_bpc (PikaImage *image) { g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); return PIKA_IMAGE_GET_PRIVATE (image)->simulation_bpc; } void pika_image_set_simulation_bpc (PikaImage *image, gboolean bpc) { PikaImagePrivate *private; g_return_if_fail (PIKA_IS_IMAGE (image)); private = PIKA_IMAGE_GET_PRIVATE (image); if (bpc != private->simulation_bpc) { PikaParasite *parasite; gchar i; private->simulation_bpc = bpc; pika_color_managed_simulation_bpc_changed (PIKA_COLOR_MANAGED (image)); i = (gchar) bpc; parasite = pika_parasite_new ("image-simulation-bpc", PIKA_PARASITE_PERSISTENT, 1, (gpointer) &i); pika_image_parasite_attach (image, parasite, FALSE); pika_parasite_free (parasite); } } gboolean pika_image_validate_color_profile_by_format (const Babl *format, PikaColorProfile *profile, gboolean *is_builtin, GError **error) { g_return_val_if_fail (format != NULL, FALSE); g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (profile), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (pika_babl_format_get_base_type (format) == PIKA_GRAY) { if (! pika_color_profile_is_gray (profile)) { g_set_error_literal (error, PIKA_ERROR, PIKA_FAILED, _("ICC profile validation failed: " "Color profile is not for grayscale color space")); return FALSE; } } else { if (! pika_color_profile_is_rgb (profile)) { g_set_error_literal (error, PIKA_ERROR, PIKA_FAILED, _("ICC profile validation failed: " "Color profile is not for RGB color space")); return FALSE; } } if (is_builtin) { PikaColorProfile *builtin = pika_babl_get_builtin_color_profile (pika_babl_format_get_base_type (format), pika_babl_format_get_trc (format)); *is_builtin = pika_color_profile_is_equal (profile, builtin); } return TRUE; } PikaColorProfile * pika_image_get_builtin_color_profile (PikaImage *image) { const Babl *format; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); format = pika_image_get_layer_format (image, FALSE); return pika_babl_get_builtin_color_profile (pika_babl_format_get_base_type (format), pika_babl_format_get_trc (format)); } gboolean pika_image_assign_color_profile (PikaImage *image, PikaColorProfile *dest_profile, PikaProgress *progress, GError **error) { PikaColorProfile *src_profile; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); g_return_val_if_fail (dest_profile == NULL || PIKA_IS_COLOR_PROFILE (dest_profile), FALSE); g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (dest_profile && ! pika_image_validate_color_profile (image, dest_profile, NULL, error)) return FALSE; src_profile = pika_color_managed_get_color_profile (PIKA_COLOR_MANAGED (image)); if (src_profile == dest_profile || (src_profile && dest_profile && pika_color_profile_is_equal (src_profile, dest_profile))) return TRUE; if (progress) pika_progress_start (progress, FALSE, dest_profile ? _("Assigning color profile") : _("Discarding color profile")); pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_CONVERT, dest_profile ? _("Assign color profile") : _("Discard color profile")); _pika_image_set_hidden_profile (image, NULL, TRUE); pika_image_set_color_profile (image, dest_profile, NULL); /* omg... */ pika_image_parasite_detach (image, "icc-profile-name", TRUE); if (pika_image_get_base_type (image) == PIKA_INDEXED) pika_image_colormap_update_formats (image); pika_image_fix_layer_format_spaces (image, progress); pika_image_undo_group_end (image); return TRUE; } gboolean pika_image_convert_color_profile (PikaImage *image, PikaColorProfile *dest_profile, PikaColorRenderingIntent intent, gboolean bpc, PikaProgress *progress, GError **error) { PikaColorProfile *src_profile; g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); g_return_val_if_fail (PIKA_IS_COLOR_PROFILE (dest_profile), FALSE); g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (! pika_image_validate_color_profile (image, dest_profile, NULL, error)) return FALSE; src_profile = pika_color_managed_get_color_profile (PIKA_COLOR_MANAGED (image)); if (pika_color_profile_is_equal (src_profile, dest_profile)) return TRUE; if (progress) pika_progress_start (progress, FALSE, _("Converting from '%s' to '%s'"), pika_color_profile_get_label (src_profile), pika_color_profile_get_label (dest_profile)); pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_CONVERT, _("Color profile conversion")); /* retain src_profile across pika_image_set_color_profile() */ g_object_ref (src_profile); _pika_image_set_hidden_profile (image, NULL, TRUE); pika_image_set_color_profile (image, dest_profile, NULL); /* omg... */ pika_image_parasite_detach (image, "icc-profile-name", TRUE); switch (pika_image_get_base_type (image)) { case PIKA_RGB: case PIKA_GRAY: pika_image_convert_profile_layers (image, src_profile, dest_profile, intent, bpc, progress); break; case PIKA_INDEXED: pika_image_convert_profile_colormap (image, src_profile, dest_profile, intent, bpc, progress); pika_image_fix_layer_format_spaces (image, progress); break; } g_object_unref (src_profile); pika_image_undo_group_end (image); if (progress) pika_progress_end (progress); return TRUE; } void pika_image_import_color_profile (PikaImage *image, PikaContext *context, PikaProgress *progress, gboolean interactive) { PikaColorProfile *profile = NULL; g_return_if_fail (PIKA_IS_IMAGE (image)); g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress)); if ((profile = pika_image_get_color_profile (image))) { PikaColorProfilePolicy policy; PikaColorProfile *dest_profile = NULL; PikaColorProfile *pref_profile = NULL; PikaColorRenderingIntent intent; gboolean bpc; policy = PIKA_DIALOG_CONFIG (image->pika->config)->color_profile_policy; intent = PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC; bpc = TRUE; if (pika_image_get_base_type (image) == PIKA_GRAY) pref_profile = pika_color_config_get_gray_color_profile (image->pika->config->color_management, NULL); else pref_profile = pika_color_config_get_rgb_color_profile (image->pika->config->color_management, NULL); if (policy == PIKA_COLOR_PROFILE_POLICY_ASK) { if (pika_color_profile_is_equal (profile, pika_image_get_builtin_color_profile (image)) || (pref_profile && pika_color_profile_is_equal (pref_profile, profile))) { /* If already using the default profile or the preferred * profile for the image type, no need to ask. Just keep * the profile. */ policy = PIKA_COLOR_PROFILE_POLICY_KEEP; } else if (interactive) { gboolean dont_ask = FALSE; policy = pika_query_profile_policy (image->pika, image, context, &dest_profile, &intent, &bpc, &dont_ask); if (dont_ask) { g_object_set (G_OBJECT (image->pika->config), "color-profile-policy", policy, NULL); } } else { policy = PIKA_COLOR_PROFILE_POLICY_KEEP; } } if (policy == PIKA_COLOR_PROFILE_POLICY_CONVERT_PREFERRED || policy == PIKA_COLOR_PROFILE_POLICY_CONVERT_BUILTIN) { if (! dest_profile) { if (policy == PIKA_COLOR_PROFILE_POLICY_CONVERT_PREFERRED) { if (pika_image_get_base_type (image) == PIKA_GRAY) dest_profile = pika_color_config_get_gray_color_profile (image->pika->config->color_management, NULL); else dest_profile = pika_color_config_get_rgb_color_profile (image->pika->config->color_management, NULL); } if (! dest_profile) { /* Built-in policy or no preferred profile set. */ dest_profile = pika_image_get_builtin_color_profile (image); g_object_ref (dest_profile); } } pika_image_convert_color_profile (image, dest_profile, intent, bpc, progress, NULL); g_object_unref (dest_profile); } g_clear_object (&pref_profile); } } PikaColorTransform * pika_image_get_color_transform_to_srgb_u8 (PikaImage *image) { PikaImagePrivate *private; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); private = PIKA_IMAGE_GET_PRIVATE (image); pika_image_create_color_transforms (image); return private->transform_to_srgb_u8; } PikaColorTransform * pika_image_get_color_transform_from_srgb_u8 (PikaImage *image) { PikaImagePrivate *private; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); private = PIKA_IMAGE_GET_PRIVATE (image); pika_image_create_color_transforms (image); return private->transform_from_srgb_u8; } PikaColorTransform * pika_image_get_color_transform_to_srgb_double (PikaImage *image) { PikaImagePrivate *private; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); private = PIKA_IMAGE_GET_PRIVATE (image); pika_image_create_color_transforms (image); return private->transform_to_srgb_double; } PikaColorTransform * pika_image_get_color_transform_from_srgb_double (PikaImage *image) { PikaImagePrivate *private; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); private = PIKA_IMAGE_GET_PRIVATE (image); pika_image_create_color_transforms (image); return private->transform_from_srgb_double; } void pika_image_color_profile_pixel_to_rgb (PikaImage *image, const Babl *pixel_format, gpointer pixel, PikaRGB *color) { PikaColorProfile *profile = NULL; const Babl *space = NULL; g_return_if_fail (PIKA_IS_IMAGE (image)); profile = pika_image_get_color_profile (image); if (profile) space = pika_color_profile_get_space (profile, PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, NULL); if (profile) { babl_process (babl_fish (pixel_format, babl_format_with_space ("R'G'B'A double", space)), pixel, color, 1); } else { pika_rgba_set_pixel (color, pixel_format, pixel); } } void pika_image_color_profile_rgb_to_pixel (PikaImage *image, const PikaRGB *color, const Babl *pixel_format, gpointer pixel) { PikaColorProfile *profile = NULL; const Babl *space = NULL; g_return_if_fail (PIKA_IS_IMAGE (image)); profile = pika_image_get_color_profile (image); if (profile) space = pika_color_profile_get_space (profile, PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, NULL); if (profile) { babl_process (babl_fish (babl_format_with_space ("R'G'B'A double", space), pixel_format), color, pixel, 1); } else { pika_rgba_get_pixel (color, pixel_format, pixel); } } /* internal API */ void _pika_image_free_color_profile (PikaImage *image) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); g_clear_object (&private->color_profile); private->layer_space = NULL; g_clear_object (&private->hidden_profile); _pika_image_free_color_transforms (image); } void _pika_image_free_color_transforms (PikaImage *image) { PikaImagePrivate *private = PIKA_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 _pika_image_update_color_profile (PikaImage *image, const PikaParasite *icc_parasite) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); _pika_image_free_color_profile (image); if (icc_parasite) { GError *error = NULL; const guint8 *data; guint32 data_size; data = pika_parasite_get_data (icc_parasite, &data_size); private->color_profile = pika_color_profile_new_from_icc_profile (data, data_size, NULL); private->layer_space = pika_color_profile_get_space (private->color_profile, PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, &error); if (! private->layer_space) { g_printerr ("%s: failed to create Babl space from profile: %s\n", G_STRFUNC, error->message); g_clear_error (&error); } } pika_color_managed_profile_changed (PIKA_COLOR_MANAGED (image)); } void _pika_image_update_simulation_profile (PikaImage *image, const PikaParasite *icc_parasite) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); g_clear_object (&private->simulation_profile); if (icc_parasite) { const guint8 *data; guint32 data_size; data = pika_parasite_get_data (icc_parasite, &data_size); private->simulation_profile = pika_color_profile_new_from_icc_profile (data, data_size, NULL); } pika_color_managed_simulation_profile_changed (PIKA_COLOR_MANAGED (image)); } /* private functions */ static void pika_image_convert_profile_layers (PikaImage *image, PikaColorProfile *src_profile, PikaColorProfile *dest_profile, PikaColorRenderingIntent intent, gboolean bpc, PikaProgress *progress) { PikaObjectQueue *queue; GList *layers; GList *list; PikaDrawable *drawable; queue = pika_object_queue_new (progress); progress = PIKA_PROGRESS (queue); layers = pika_image_get_layer_list (image); for (list = layers; list; list = g_list_next (list)) { if (! pika_viewable_get_children (list->data)) pika_object_queue_push (queue, list->data); } g_list_free (layers); while ((drawable = pika_object_queue_pop (queue))) { PikaItem *item = PIKA_ITEM (drawable); GeglBuffer *buffer; gboolean alpha; alpha = pika_drawable_has_alpha (drawable); buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, pika_item_get_width (item), pika_item_get_height (item)), pika_image_get_layer_format (image, alpha)); pika_gegl_convert_color_profile (pika_drawable_get_buffer (drawable), NULL, src_profile, buffer, NULL, dest_profile, intent, bpc, progress); pika_drawable_set_buffer (drawable, TRUE, NULL, buffer); g_object_unref (buffer); } g_object_unref (queue); } static void pika_image_convert_profile_colormap (PikaImage *image, PikaColorProfile *src_profile, PikaColorProfile *dest_profile, PikaColorRenderingIntent intent, gboolean bpc, PikaProgress *progress) { PikaColorTransform *transform; const Babl *src_format; const Babl *dest_format; PikaColorTransformFlags flags = 0; guchar *cmap; gint n_colors; n_colors = pika_image_get_colormap_size (image); cmap = pika_image_get_colormap (image); if (bpc) flags |= PIKA_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; src_format = pika_color_profile_get_format (src_profile, babl_format ("R'G'B' u8"), PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, NULL); dest_format = pika_color_profile_get_format (dest_profile, babl_format ("R'G'B' u8"), PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, NULL); transform = pika_color_transform_new (src_profile, src_format, dest_profile, dest_format, intent, flags); if (transform) { pika_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); pika_image_set_colormap (image, cmap, n_colors, TRUE); } else { g_warning ("pika_color_transform_new() failed!"); } g_free (cmap); } static void pika_image_fix_layer_format_spaces (PikaImage *image, PikaProgress *progress) { PikaObjectQueue *queue; GList *layers; GList *list; PikaLayer *layer; queue = pika_object_queue_new (progress); layers = pika_image_get_layer_list (image); for (list = layers; list; list = g_list_next (list)) { if (! pika_viewable_get_children (list->data)) pika_object_queue_push (queue, list->data); } g_list_free (layers); while ((layer = pika_object_queue_pop (queue))) { pika_layer_fix_format_space (layer, TRUE, TRUE); } g_object_unref (queue); } static void pika_image_create_color_transforms (PikaImage *image) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); if (private->color_profile && ! private->color_transforms_created) { PikaColorProfile *srgb_profile; PikaColorTransformFlags flags = 0; srgb_profile = pika_color_profile_new_rgb_srgb (); flags |= PIKA_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE; flags |= PIKA_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION; private->transform_to_srgb_u8 = pika_color_transform_new (private->color_profile, pika_image_get_layer_format (image, TRUE), srgb_profile, babl_format ("R'G'B'A u8"), PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL, flags); private->transform_from_srgb_u8 = pika_color_transform_new (srgb_profile, babl_format ("R'G'B'A u8"), private->color_profile, pika_image_get_layer_format (image, TRUE), PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL, flags); private->transform_to_srgb_double = pika_color_transform_new (private->color_profile, pika_image_get_layer_format (image, TRUE), srgb_profile, babl_format ("R'G'B'A double"), PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL, flags); private->transform_from_srgb_double = pika_color_transform_new (srgb_profile, babl_format ("R'G'B'A double"), private->color_profile, pika_image_get_layer_format (image, TRUE), PIKA_COLOR_RENDERING_INTENT_PERCEPTUAL, flags); g_object_unref (srgb_profile); private->color_transforms_created = TRUE; } }