/* 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-1999 Spencer Kimball and Peter Mattis * * 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 "libpikaconfig/pikaconfig.h" #include "core-types.h" #include "config/pikacoreconfig.h" #include "gegl/pika-babl.h" #include "gegl/pika-gegl-utils.h" #include "pika.h" #include "pikabuffer.h" #include "pikachannel.h" #include "pikacontext.h" #include "pikadrawable-fill.h" #include "pikagrouplayer.h" #include "pikaimage.h" #include "pikaimage-color-profile.h" #include "pikaimage-colormap.h" #include "pikaimage-new.h" #include "pikaimage-undo.h" #include "pikalayer.h" #include "pikalayer-new.h" #include "pikalist.h" #include "pikatemplate.h" #include "pika-intl.h" PikaTemplate * pika_image_new_get_last_template (Pika *pika, PikaImage *image) { PikaTemplate *template; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (image == NULL || PIKA_IS_IMAGE (image), NULL); template = pika_template_new ("image new values"); if (image) { pika_config_sync (G_OBJECT (pika->config->default_image), G_OBJECT (template), 0); pika_template_set_from_image (template, image); } else { pika_config_sync (G_OBJECT (pika->image_new_last_template), G_OBJECT (template), 0); } return template; } void pika_image_new_set_last_template (Pika *pika, PikaTemplate *template) { g_return_if_fail (PIKA_IS_PIKA (pika)); g_return_if_fail (PIKA_IS_TEMPLATE (template)); pika_config_sync (G_OBJECT (template), G_OBJECT (pika->image_new_last_template), 0); } PikaImage * pika_image_new_from_template (Pika *pika, PikaTemplate *template, PikaContext *context) { PikaImage *image; PikaLayer *layer; PikaColorProfile *profile; PikaColorRenderingIntent intent; gboolean bpc; gint width, height; gboolean has_alpha; const gchar *comment; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (PIKA_IS_TEMPLATE (template), NULL); g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); image = pika_create_image (pika, pika_template_get_width (template), pika_template_get_height (template), pika_template_get_base_type (template), pika_template_get_precision (template), FALSE); pika_context_set_image (context, image); pika_image_undo_disable (image); comment = pika_template_get_comment (template); if (comment) { PikaParasite *parasite; parasite = pika_parasite_new ("pika-comment", PIKA_PARASITE_PERSISTENT, strlen (comment) + 1, comment); pika_image_parasite_attach (image, parasite, FALSE); pika_parasite_free (parasite); } pika_image_set_resolution (image, pika_template_get_resolution_x (template), pika_template_get_resolution_y (template)); pika_image_set_unit (image, pika_template_get_resolution_unit (template)); profile = pika_template_get_color_profile (template); pika_image_set_color_profile (image, profile, NULL); if (profile) g_object_unref (profile); profile = pika_template_get_simulation_profile (template); pika_image_set_simulation_profile (image, profile); if (profile) g_object_unref (profile); intent = pika_template_get_simulation_intent (template); pika_image_set_simulation_intent (image, intent); bpc = pika_template_get_simulation_bpc (template); pika_image_set_simulation_bpc (image, bpc); width = pika_image_get_width (image); height = pika_image_get_height (image); if (pika_template_get_fill_type (template) == PIKA_FILL_TRANSPARENT) has_alpha = TRUE; else has_alpha = FALSE; layer = pika_layer_new (image, width, height, pika_image_get_layer_format (image, has_alpha), _("Background"), PIKA_OPACITY_OPAQUE, pika_image_get_default_new_layer_mode (image)); pika_drawable_fill (PIKA_DRAWABLE (layer), context, pika_template_get_fill_type (template)); pika_image_add_layer (image, layer, NULL, 0, FALSE); pika_image_undo_enable (image); pika_image_clean_all (image); return image; } PikaImage * pika_image_new_from_drawable (Pika *pika, PikaDrawable *drawable) { PikaItem *item; PikaImage *image; PikaImage *new_image; PikaLayer *new_layer; GType new_type; gint off_x, off_y; PikaImageBaseType type; gdouble xres; gdouble yres; PikaColorProfile *profile; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (PIKA_IS_DRAWABLE (drawable), NULL); item = PIKA_ITEM (drawable); image = pika_item_get_image (item); type = pika_drawable_get_base_type (drawable); new_image = pika_create_image (pika, pika_item_get_width (item), pika_item_get_height (item), type, pika_drawable_get_precision (drawable), TRUE); pika_image_undo_disable (new_image); if (type == PIKA_INDEXED) pika_image_set_colormap_palette (new_image, pika_image_get_colormap_palette (image), FALSE); pika_image_get_resolution (image, &xres, &yres); pika_image_set_resolution (new_image, xres, yres); pika_image_set_unit (new_image, pika_image_get_unit (image)); profile = pika_color_managed_get_color_profile (PIKA_COLOR_MANAGED (drawable)); pika_image_set_color_profile (new_image, profile, NULL); if (PIKA_IS_LAYER (drawable)) new_type = G_TYPE_FROM_INSTANCE (drawable); else new_type = PIKA_TYPE_LAYER; new_layer = PIKA_LAYER (pika_item_convert (PIKA_ITEM (drawable), new_image, new_type)); pika_object_set_name (PIKA_OBJECT (new_layer), pika_object_get_name (drawable)); pika_item_get_offset (PIKA_ITEM (new_layer), &off_x, &off_y); pika_item_translate (PIKA_ITEM (new_layer), -off_x, -off_y, FALSE); pika_item_set_visible (PIKA_ITEM (new_layer), TRUE, FALSE); pika_layer_set_mode (new_layer, pika_image_get_default_new_layer_mode (new_image), FALSE); pika_layer_set_opacity (new_layer, PIKA_OPACITY_OPAQUE, FALSE); if (pika_layer_can_lock_alpha (new_layer)) pika_layer_set_lock_alpha (new_layer, FALSE, FALSE); pika_image_add_layer (new_image, new_layer, NULL, 0, TRUE); pika_image_undo_enable (new_image); return new_image; } /** * pika_image_new_copy_drawables: * @image: the image where @drawables belong to. * @drawables: the drawables to copy into @new_image. * @new_image: the image to insert to. * @tag_copies: tag copies of @drawable with "pika-image-copied-layer". * @copied_drawables: * @tagged_drawables: * @parent: * @new_parent: * * This recursive function will create copies of all @drawables * belonging to the same @image, and will insert them into @new_image * with the same layer order and hierarchy (adding layer groups when * needed). * If a single drawable is selected, it will be copied visible, with * full opacity and default layer mode. Otherwise, visibility, opacity * and layer mode will be copied as-is, allowing proper compositing. * * The @copied_drawables, @tagged_drawables, @parent and @new_parent arguments * are only used internally for recursive calls and must be set to NULL for the * initial call. */ static void pika_image_new_copy_drawables (PikaImage *image, GList *drawables, PikaImage *new_image, gboolean tag_copies, GList *copied_drawables, GList *tagged_drawables, PikaLayer *parent, PikaLayer *new_parent) { GList *layers; GList *iter; gint n_drawables; gint index; n_drawables = g_list_length (drawables); if (parent == NULL) { /* Root layers. */ layers = pika_image_get_layer_iter (image); copied_drawables = g_list_copy (drawables); for (iter = copied_drawables; iter; iter = iter->next) { /* Tagged drawables are the explicitly copied drawables which have no * explicitly copied descendant items. */ GList *iter2; for (iter2 = iter; iter2; iter2 = iter2->next) if (pika_viewable_is_ancestor (iter->data, iter2->data)) break; if (iter2 == NULL) tagged_drawables = g_list_prepend (tagged_drawables, iter->data); } /* Add any item parent. */ for (iter = copied_drawables; iter; iter = iter->next) { PikaItem *item = iter->data; while ((item = pika_item_get_parent (item))) if (! g_list_find (copied_drawables, item)) copied_drawables = g_list_prepend (copied_drawables, item); } } else { PikaContainer *container; container = pika_viewable_get_children (PIKA_VIEWABLE (parent)); layers = PIKA_LIST (container)->queue->head; } index = 0; for (iter = layers; iter; iter = iter->next) { if (g_list_find (copied_drawables, iter->data)) { PikaLayer *new_layer; GType new_type; gboolean is_group; gboolean is_tagged; if (PIKA_IS_LAYER (iter->data)) new_type = G_TYPE_FROM_INSTANCE (iter->data); else new_type = PIKA_TYPE_LAYER; is_group = (pika_viewable_get_children (iter->data) != NULL); is_tagged = (g_list_find (tagged_drawables, iter->data) != NULL); if (is_group && ! is_tagged) new_layer = pika_group_layer_new (new_image); else new_layer = PIKA_LAYER (pika_item_convert (PIKA_ITEM (iter->data), new_image, new_type)); if (tag_copies && is_tagged) g_object_set_data (G_OBJECT (new_layer), "pika-image-copied-layer", GINT_TO_POINTER (TRUE)); pika_object_set_name (PIKA_OBJECT (new_layer), pika_object_get_name (iter->data)); /* Visibility, mode and opacity mimic the source image if * multiple items are copied. Otherwise we just set them to * defaults. */ pika_item_set_visible (PIKA_ITEM (new_layer), n_drawables > 1 ? pika_item_get_visible (iter->data) : TRUE, FALSE); pika_layer_set_mode (new_layer, n_drawables > 1 && PIKA_IS_LAYER (iter->data) ? pika_layer_get_mode (iter->data) : pika_image_get_default_new_layer_mode (new_image), FALSE); pika_layer_set_opacity (new_layer, n_drawables > 1 && PIKA_IS_LAYER (iter->data) ? pika_layer_get_opacity (iter->data) : PIKA_OPACITY_OPAQUE, FALSE); if (pika_layer_can_lock_alpha (new_layer)) pika_layer_set_lock_alpha (new_layer, FALSE, FALSE); pika_image_add_layer (new_image, new_layer, new_parent, index++, TRUE); /* If a group, loop through children. */ if (is_group && ! is_tagged) pika_image_new_copy_drawables (image, drawables, new_image, tag_copies, copied_drawables, tagged_drawables, iter->data, new_layer); } } if (parent == NULL) { g_list_free (copied_drawables); g_list_free (tagged_drawables); } } PikaImage * pika_image_new_from_drawables (Pika *pika, GList *drawables, gboolean copy_selection, gboolean tag_copies) { PikaImage *image = NULL; PikaImage *new_image; GList *iter; PikaImageBaseType type; PikaPrecision precision; gdouble xres; gdouble yres; PikaColorProfile *profile = NULL; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (drawables != NULL, NULL); for (iter = drawables; iter; iter = iter->next) { g_return_val_if_fail (PIKA_IS_DRAWABLE (iter->data), NULL); if (iter == drawables) image = pika_item_get_image (iter->data); else /* We only accept list of drawables for a same origin image. */ g_return_val_if_fail (pika_item_get_image (iter->data) == image, NULL); } type = pika_drawable_get_base_type (drawables->data); precision = pika_drawable_get_precision (drawables->data); profile = pika_color_managed_get_color_profile (PIKA_COLOR_MANAGED (drawables->data)); new_image = pika_create_image (pika, pika_image_get_width (image), pika_image_get_height (image), type, precision, TRUE); pika_image_undo_disable (new_image); if (type == PIKA_INDEXED) pika_image_set_colormap_palette (new_image, pika_image_get_colormap_palette (image), FALSE); pika_image_get_resolution (image, &xres, &yres); pika_image_set_resolution (new_image, xres, yres); pika_image_set_unit (new_image, pika_image_get_unit (image)); if (profile) pika_image_set_color_profile (new_image, profile, NULL); if (copy_selection) { PikaChannel *selection; selection = pika_image_get_mask (image); if (! pika_channel_is_empty (selection)) { PikaChannel *new_selection; GeglBuffer *buffer; new_selection = pika_image_get_mask (new_image); buffer = pika_gegl_buffer_dup (pika_drawable_get_buffer (PIKA_DRAWABLE (selection))); pika_drawable_set_buffer (PIKA_DRAWABLE (new_selection), FALSE, NULL, buffer); g_object_unref (buffer); } } pika_image_new_copy_drawables (image, drawables, new_image, tag_copies, NULL, NULL, NULL, NULL); pika_image_undo_enable (new_image); return new_image; } PikaImage * pika_image_new_from_component (Pika *pika, PikaImage *image, PikaChannelType component) { PikaImage *new_image; PikaChannel *channel; PikaLayer *layer; const gchar *desc; gdouble xres; gdouble yres; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); new_image = pika_create_image (pika, pika_image_get_width (image), pika_image_get_height (image), PIKA_GRAY, pika_image_get_precision (image), TRUE); pika_image_undo_disable (new_image); pika_image_get_resolution (image, &xres, &yres); pika_image_set_resolution (new_image, xres, yres); pika_image_set_unit (new_image, pika_image_get_unit (image)); channel = pika_channel_new_from_component (image, component, NULL, NULL); layer = PIKA_LAYER (pika_item_convert (PIKA_ITEM (channel), new_image, PIKA_TYPE_LAYER)); g_object_unref (channel); pika_enum_get_value (PIKA_TYPE_CHANNEL_TYPE, component, NULL, NULL, &desc, NULL); pika_object_take_name (PIKA_OBJECT (layer), g_strdup_printf (_("%s Channel Copy"), desc)); pika_image_add_layer (new_image, layer, NULL, 0, TRUE); pika_image_undo_enable (new_image); return new_image; } PikaImage * pika_image_new_from_buffer (Pika *pika, PikaBuffer *buffer) { PikaImage *image; PikaLayer *layer; const Babl *format; gboolean has_alpha; gdouble res_x; gdouble res_y; PikaColorProfile *profile; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (PIKA_IS_BUFFER (buffer), NULL); format = pika_buffer_get_format (buffer); has_alpha = babl_format_has_alpha (format); image = pika_create_image (pika, pika_buffer_get_width (buffer), pika_buffer_get_height (buffer), pika_babl_format_get_base_type (format), pika_babl_format_get_precision (format), TRUE); pika_image_undo_disable (image); if (pika_buffer_get_resolution (buffer, &res_x, &res_y)) { pika_image_set_resolution (image, res_x, res_y); pika_image_set_unit (image, pika_buffer_get_unit (buffer)); } profile = pika_buffer_get_color_profile (buffer); pika_image_set_color_profile (image, profile, NULL); layer = pika_layer_new_from_buffer (buffer, image, pika_image_get_layer_format (image, has_alpha), _("Pasted Layer"), PIKA_OPACITY_OPAQUE, pika_image_get_default_new_layer_mode (image)); pika_image_add_layer (image, layer, NULL, 0, TRUE); pika_image_undo_enable (image); return image; } PikaImage * pika_image_new_from_pixbuf (Pika *pika, GdkPixbuf *pixbuf, const gchar *layer_name) { PikaImage *new_image; PikaLayer *layer; PikaImageBaseType base_type; gboolean has_alpha = FALSE; guint8 *icc_data; gsize icc_len; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); switch (gdk_pixbuf_get_n_channels (pixbuf)) { case 2: has_alpha = TRUE; case 1: base_type = PIKA_GRAY; break; case 4: has_alpha = TRUE; case 3: base_type = PIKA_RGB; break; default: g_return_val_if_reached (NULL); } new_image = pika_create_image (pika, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), base_type, PIKA_PRECISION_U8_NON_LINEAR, FALSE); pika_image_undo_disable (new_image); icc_data = pika_pixbuf_get_icc_profile (pixbuf, &icc_len); if (icc_data) { pika_image_set_icc_profile (new_image, icc_data, icc_len, PIKA_ICC_PROFILE_PARASITE_NAME, NULL); g_free (icc_data); } layer = pika_layer_new_from_pixbuf (pixbuf, new_image, pika_image_get_layer_format (new_image, has_alpha), layer_name, PIKA_OPACITY_OPAQUE, pika_image_get_default_new_layer_mode (new_image)); pika_image_add_layer (new_image, layer, NULL, 0, TRUE); pika_image_undo_enable (new_image); return new_image; }