/* 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-convert-precision.c * Copyright (C) 2012 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 "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "core-types.h" #include "gegl/pika-babl.h" #include "gegl/pika-gegl-loops.h" #include "pikachannel.h" #include "pikadrawable.h" #include "pikadrawable-operation.h" #include "pikaimage.h" #include "pikaimage-color-profile.h" #include "pikaimage-convert-precision.h" #include "pikaimage-undo.h" #include "pikaimage-undo-push.h" #include "pikaobjectqueue.h" #include "pikaprogress.h" #include "text/pikatextlayer.h" #include "pika-intl.h" void pika_image_convert_precision (PikaImage *image, PikaPrecision precision, GeglDitherMethod layer_dither_type, GeglDitherMethod text_layer_dither_type, GeglDitherMethod mask_dither_type, PikaProgress *progress) { PikaColorProfile *old_profile; PikaColorProfile *new_profile = NULL; const Babl *old_format; const Babl *new_format; PikaObjectQueue *queue; PikaProgress *sub_progress; GList *layers; PikaDrawable *drawable; const gchar *enum_desc; gchar *undo_desc = NULL; g_return_if_fail (PIKA_IS_IMAGE (image)); g_return_if_fail (precision != pika_image_get_precision (image)); g_return_if_fail (pika_babl_is_valid (pika_image_get_base_type (image), precision)); g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress)); pika_enum_get_value (PIKA_TYPE_PRECISION, precision, NULL, NULL, &enum_desc, NULL); undo_desc = g_strdup_printf (C_("undo-type", "Convert Image to %s"), enum_desc); if (progress) pika_progress_start (progress, FALSE, "%s", undo_desc); queue = pika_object_queue_new (progress); sub_progress = PIKA_PROGRESS (queue); layers = pika_image_get_layer_list (image); pika_object_queue_push_list (queue, layers); g_list_free (layers); pika_object_queue_push (queue, pika_image_get_mask (image)); pika_object_queue_push_container (queue, pika_image_get_channels (image)); g_object_freeze_notify (G_OBJECT (image)); pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_CONVERT, undo_desc); g_free (undo_desc); /* Push the image precision to the stack */ pika_image_undo_push_image_precision (image, NULL); old_profile = pika_color_managed_get_color_profile (PIKA_COLOR_MANAGED (image)); old_format = pika_image_get_layer_format (image, FALSE); pika_image_set_converting (image, TRUE); /* Set the new precision */ g_object_set (image, "precision", precision, NULL); new_format = pika_image_get_layer_format (image, FALSE); /* we use old_format and new_format just for looking at their * TRCs, new_format's space might be incorrect, don't use it * for anything else. */ if (pika_babl_format_get_trc (old_format) != pika_babl_format_get_trc (new_format)) { PikaImageBaseType base_type = pika_image_get_base_type (image); PikaTRCType new_trc = pika_babl_trc (precision); /* if the image doesn't use the builtin profile, create a new * one, using the original profile's chromacities and * whitepoint, but a linear/sRGB-gamma TRC. */ if (pika_image_get_color_profile (image)) { if (new_trc == PIKA_TRC_LINEAR) { new_profile = pika_color_profile_new_linear_from_color_profile (old_profile); } else { new_profile = pika_color_profile_new_srgb_trc_from_color_profile (old_profile); } } /* we always need a profile for convert_type with changing TRC * on the same image, use the new precision's builtin profile if * the profile couldn't be converted or the image used the old * TRC's builtin profile. */ if (! new_profile) { new_profile = pika_babl_get_builtin_color_profile (base_type, new_trc); g_object_ref (new_profile); } } while ((drawable = pika_object_queue_pop (queue))) { if (drawable == PIKA_DRAWABLE (pika_image_get_mask (image))) { GeglBuffer *buffer; pika_image_undo_push_mask_precision (image, NULL, PIKA_CHANNEL (drawable)); buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, pika_image_get_width (image), pika_image_get_height (image)), pika_image_get_mask_format (image)); pika_gegl_buffer_copy (pika_drawable_get_buffer (drawable), NULL, GEGL_ABYSS_NONE, buffer, NULL); pika_drawable_set_buffer (drawable, FALSE, NULL, buffer); g_object_unref (buffer); pika_progress_set_value (sub_progress, 1.0); } else { GeglDitherMethod dither_type; if (pika_item_is_text_layer (PIKA_ITEM (drawable))) dither_type = text_layer_dither_type; else dither_type = layer_dither_type; pika_drawable_convert_type (drawable, image, pika_drawable_get_base_type (drawable), precision, pika_drawable_has_alpha (drawable), old_profile, new_profile, dither_type, mask_dither_type, TRUE, sub_progress); } } if (new_profile) { pika_image_set_color_profile (image, new_profile, NULL); g_object_unref (new_profile); } else { pika_color_managed_profile_changed (PIKA_COLOR_MANAGED (image)); } pika_image_set_converting (image, FALSE); pika_image_undo_group_end (image); pika_image_precision_changed (image); g_object_thaw_notify (G_OBJECT (image)); g_object_unref (queue); if (progress) pika_progress_end (progress); } void pika_image_convert_dither_u8 (PikaImage *image, PikaProgress *progress) { GeglNode *dither; g_return_if_fail (PIKA_IS_IMAGE (image)); g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress)); dither = gegl_node_new_child (NULL, "operation", "gegl:noise-rgb", "red", 1.0 / 256.0, "green", 1.0 / 256.0, "blue", 1.0 / 256.0, "linear", FALSE, "gaussian", FALSE, NULL); if (dither) { PikaObjectQueue *queue; PikaProgress *sub_progress; GList *layers; GList *list; PikaDrawable *drawable; if (progress) pika_progress_start (progress, FALSE, "%s", _("Dithering")); queue = pika_object_queue_new (progress); sub_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_item_is_text_layer (list->data)) { pika_object_queue_push (queue, list->data); } } g_list_free (layers); while ((drawable = pika_object_queue_pop (queue))) { pika_drawable_apply_operation (drawable, sub_progress, _("Dithering"), dither); } g_object_unref (queue); if (progress) pika_progress_end (progress); g_object_unref (dither); } }