/* 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 * * 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 "libpikabase/pikabase.h" #include "core-types.h" #include "gegl/pika-gegl-loops.h" #include "vectors/pikavectors.h" #include "pika.h" #include "pikachannel.h" #include "pikaguide.h" #include "pikaimage.h" #include "pikaimage-color-profile.h" #include "pikaimage-colormap.h" #include "pikaimage-duplicate.h" #include "pikaimage-grid.h" #include "pikaimage-guides.h" #include "pikaimage-metadata.h" #include "pikaimage-private.h" #include "pikaimage-undo.h" #include "pikaimage-sample-points.h" #include "pikaitemstack.h" #include "pikalayer.h" #include "pikalayermask.h" #include "pikalayer-floating-selection.h" #include "pikaparasitelist.h" #include "pikasamplepoint.h" static void pika_image_duplicate_resolution (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_save_source_file (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_colormap (PikaImage *image, PikaImage *new_image); static PikaItem * pika_image_duplicate_item (PikaItem *item, PikaImage *new_image); static GList * pika_image_duplicate_layers (PikaImage *image, PikaImage *new_image); static GList * pika_image_duplicate_channels (PikaImage *image, PikaImage *new_image); static GList * pika_image_duplicate_vectors (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_floating_sel (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_mask (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_components (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_guides (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_sample_points (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_grid (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_metadata (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_quick_mask (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_parasites (PikaImage *image, PikaImage *new_image); static void pika_image_duplicate_color_profile (PikaImage *image, PikaImage *new_image); PikaImage * pika_image_duplicate (PikaImage *image) { PikaImage *new_image; GList *active_layers; GList *active_channels; GList *active_vectors; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); pika_set_busy_until_idle (image->pika); /* Create a new image */ new_image = pika_create_image (image->pika, pika_image_get_width (image), pika_image_get_height (image), pika_image_get_base_type (image), pika_image_get_precision (image), FALSE); pika_image_undo_disable (new_image); /* Store the source uri to be used by the save dialog */ pika_image_duplicate_save_source_file (image, new_image); /* Copy resolution information */ pika_image_duplicate_resolution (image, new_image); /* Copy parasites first so we have a color profile */ pika_image_duplicate_parasites (image, new_image); pika_image_duplicate_color_profile (image, new_image); /* Copy the colormap if necessary */ pika_image_duplicate_colormap (image, new_image); /* Copy the layers */ active_layers = pika_image_duplicate_layers (image, new_image); /* Copy the channels */ active_channels = pika_image_duplicate_channels (image, new_image); /* Copy any vectors */ active_vectors = pika_image_duplicate_vectors (image, new_image); /* Copy floating layer */ pika_image_duplicate_floating_sel (image, new_image); /* Copy the selection mask */ pika_image_duplicate_mask (image, new_image); /* Set active layer, active channel, active vectors */ if (active_layers) pika_image_set_selected_layers (new_image, active_layers); if (active_channels) pika_image_set_selected_channels (new_image, active_channels); if (active_vectors) pika_image_set_selected_vectors (new_image, active_vectors); /* Copy state of all color components */ pika_image_duplicate_components (image, new_image); /* Copy any guides */ pika_image_duplicate_guides (image, new_image); /* Copy any sample points */ pika_image_duplicate_sample_points (image, new_image); /* Copy the grid */ pika_image_duplicate_grid (image, new_image); /* Copy the metadata */ pika_image_duplicate_metadata (image, new_image); /* Copy the quick mask info */ pika_image_duplicate_quick_mask (image, new_image); pika_image_undo_enable (new_image); /* Explicitly mark image as dirty, so that its dirty time is set */ pika_image_dirty (new_image, PIKA_DIRTY_ALL); return new_image; } static void pika_image_duplicate_resolution (PikaImage *image, PikaImage *new_image) { gdouble xres; gdouble yres; 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)); } static void pika_image_duplicate_save_source_file (PikaImage *image, PikaImage *new_image) { GFile *file = pika_image_get_file (image); if (file) g_object_set_data_full (G_OBJECT (new_image), "pika-image-source-file", g_object_ref (file), (GDestroyNotify) g_object_unref); } static void pika_image_duplicate_colormap (PikaImage *image, PikaImage *new_image) { if (pika_image_get_base_type (new_image) == PIKA_INDEXED) pika_image_set_colormap_palette (new_image, pika_image_get_colormap_palette (image), FALSE); } static PikaItem * pika_image_duplicate_item (PikaItem *item, PikaImage *new_image) { PikaItem *new_item; new_item = pika_item_convert (item, new_image, G_TYPE_FROM_INSTANCE (item)); /* Make sure the copied item doesn't say: " copy" */ pika_object_set_name (PIKA_OBJECT (new_item), pika_object_get_name (item)); return new_item; } static GList * pika_image_duplicate_layers (PikaImage *image, PikaImage *new_image) { GList *new_selected_layers = NULL; GList *selected_paths = NULL; GList *selected_layers; PikaItemStack *new_item_stack; GList *list; gint count; selected_layers = pika_image_get_selected_layers (image); for (list = selected_layers; list; list = list->next) selected_paths = g_list_prepend (selected_paths, pika_item_get_path (list->data)); for (list = pika_image_get_layer_iter (image), count = 0; list; list = g_list_next (list)) { PikaLayer *layer = list->data; PikaLayer *new_layer; if (pika_layer_is_floating_sel (layer)) continue; new_layer = PIKA_LAYER (pika_image_duplicate_item (PIKA_ITEM (layer), new_image)); /* Make sure that if the layer has a layer mask, * its name isn't screwed up */ if (new_layer->mask) pika_object_set_name (PIKA_OBJECT (new_layer->mask), pika_object_get_name (layer->mask)); pika_image_add_layer (new_image, new_layer, NULL, count++, FALSE); } new_item_stack = PIKA_ITEM_STACK (pika_image_get_layers (new_image)); for (list = selected_paths; list; list = list->next) new_selected_layers = g_list_prepend (new_selected_layers, pika_item_stack_get_item_by_path (new_item_stack, list->data)); g_list_free_full (selected_paths, (GDestroyNotify) g_list_free); return new_selected_layers; } static GList * pika_image_duplicate_channels (PikaImage *image, PikaImage *new_image) { GList *new_selected_channels = NULL; GList *selected_channels; GList *list; gint count; selected_channels = pika_image_get_selected_channels (image); for (list = pika_image_get_channel_iter (image), count = 0; list; list = g_list_next (list)) { PikaChannel *channel = list->data; PikaChannel *new_channel; new_channel = PIKA_CHANNEL (pika_image_duplicate_item (PIKA_ITEM (channel), new_image)); if (g_list_find (selected_channels, channel)) new_selected_channels = g_list_prepend (new_selected_channels, new_channel); pika_image_add_channel (new_image, new_channel, NULL, count++, FALSE); } return new_selected_channels; } static GList * pika_image_duplicate_vectors (PikaImage *image, PikaImage *new_image) { GList *new_selected_vectors = NULL; GList *selected_vectors; GList *list; gint count; selected_vectors = pika_image_get_selected_vectors (image); for (list = pika_image_get_vectors_iter (image), count = 0; list; list = g_list_next (list)) { PikaVectors *vectors = list->data; PikaVectors *new_vectors; new_vectors = PIKA_VECTORS (pika_image_duplicate_item (PIKA_ITEM (vectors), new_image)); if (g_list_find (selected_vectors, vectors)) new_selected_vectors = g_list_prepend (new_selected_vectors, new_vectors); pika_image_add_vectors (new_image, new_vectors, NULL, count++, FALSE); } return new_selected_vectors; } static void pika_image_duplicate_floating_sel (PikaImage *image, PikaImage *new_image) { PikaLayer *floating_sel; PikaDrawable *floating_sel_drawable; GList *floating_sel_path; PikaItemStack *new_item_stack; PikaLayer *new_floating_sel; PikaDrawable *new_floating_sel_drawable; floating_sel = pika_image_get_floating_selection (image); if (! floating_sel) return; floating_sel_drawable = pika_layer_get_floating_sel_drawable (floating_sel); if (PIKA_IS_LAYER_MASK (floating_sel_drawable)) { PikaLayer *layer; layer = pika_layer_mask_get_layer (PIKA_LAYER_MASK (floating_sel_drawable)); floating_sel_path = pika_item_get_path (PIKA_ITEM (layer)); new_item_stack = PIKA_ITEM_STACK (pika_image_get_layers (new_image)); } else { floating_sel_path = pika_item_get_path (PIKA_ITEM (floating_sel_drawable)); if (PIKA_IS_LAYER (floating_sel_drawable)) new_item_stack = PIKA_ITEM_STACK (pika_image_get_layers (new_image)); else new_item_stack = PIKA_ITEM_STACK (pika_image_get_channels (new_image)); } /* adjust path[0] for the floating layer missing in new_image */ floating_sel_path->data = GUINT_TO_POINTER (GPOINTER_TO_UINT (floating_sel_path->data) - 1); if (PIKA_IS_LAYER (floating_sel_drawable)) { new_floating_sel = PIKA_LAYER (pika_image_duplicate_item (PIKA_ITEM (floating_sel), new_image)); } else { /* can't use pika_item_convert() for floating selections of channels * or layer masks because they maybe don't have a normal layer's type */ new_floating_sel = PIKA_LAYER (pika_item_duplicate (PIKA_ITEM (floating_sel), G_TYPE_FROM_INSTANCE (floating_sel))); pika_item_set_image (PIKA_ITEM (new_floating_sel), new_image); pika_object_set_name (PIKA_OBJECT (new_floating_sel), pika_object_get_name (floating_sel)); } /* Make sure the copied layer doesn't say: " copy" */ pika_object_set_name (PIKA_OBJECT (new_floating_sel), pika_object_get_name (floating_sel)); new_floating_sel_drawable = PIKA_DRAWABLE (pika_item_stack_get_item_by_path (new_item_stack, floating_sel_path)); if (PIKA_IS_LAYER_MASK (floating_sel_drawable)) new_floating_sel_drawable = PIKA_DRAWABLE (pika_layer_get_mask (PIKA_LAYER (new_floating_sel_drawable))); floating_sel_attach (new_floating_sel, new_floating_sel_drawable); g_list_free (floating_sel_path); } static void pika_image_duplicate_mask (PikaImage *image, PikaImage *new_image) { PikaDrawable *mask; PikaDrawable *new_mask; mask = PIKA_DRAWABLE (pika_image_get_mask (image)); new_mask = PIKA_DRAWABLE (pika_image_get_mask (new_image)); pika_gegl_buffer_copy (pika_drawable_get_buffer (mask), NULL, GEGL_ABYSS_NONE, pika_drawable_get_buffer (new_mask), NULL); PIKA_CHANNEL (new_mask)->bounds_known = FALSE; PIKA_CHANNEL (new_mask)->boundary_known = FALSE; } static void pika_image_duplicate_components (PikaImage *image, PikaImage *new_image) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); PikaImagePrivate *new_private = PIKA_IMAGE_GET_PRIVATE (new_image); gint count; for (count = 0; count < MAX_CHANNELS; count++) { new_private->visible[count] = private->visible[count]; new_private->active[count] = private->active[count]; } } static void pika_image_duplicate_guides (PikaImage *image, PikaImage *new_image) { GList *list; for (list = pika_image_get_guides (image); list; list = g_list_next (list)) { PikaGuide *guide = list->data; gint position = pika_guide_get_position (guide); switch (pika_guide_get_orientation (guide)) { case PIKA_ORIENTATION_HORIZONTAL: pika_image_add_hguide (new_image, position, FALSE); break; case PIKA_ORIENTATION_VERTICAL: pika_image_add_vguide (new_image, position, FALSE); break; default: g_error ("Unknown guide orientation.\n"); } } } static void pika_image_duplicate_sample_points (PikaImage *image, PikaImage *new_image) { GList *list; for (list = pika_image_get_sample_points (image); list; list = g_list_next (list)) { PikaSamplePoint *sample_point = list->data; gint x; gint y; pika_sample_point_get_position (sample_point, &x, &y); pika_image_add_sample_point_at_pos (new_image, x, y, FALSE); } } static void pika_image_duplicate_grid (PikaImage *image, PikaImage *new_image) { if (pika_image_get_grid (image)) pika_image_set_grid (new_image, pika_image_get_grid (image), FALSE); } static void pika_image_duplicate_metadata (PikaImage *image, PikaImage *new_image) { PikaMetadata *metadata = pika_image_get_metadata (image); if (metadata) { metadata = pika_metadata_duplicate (metadata); pika_image_set_metadata (new_image, metadata, FALSE); g_object_unref (metadata); } } static void pika_image_duplicate_quick_mask (PikaImage *image, PikaImage *new_image) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); PikaImagePrivate *new_private = PIKA_IMAGE_GET_PRIVATE (new_image); new_private->quick_mask_state = private->quick_mask_state; new_private->quick_mask_inverted = private->quick_mask_inverted; new_private->quick_mask_color = private->quick_mask_color; } static void pika_image_duplicate_parasites (PikaImage *image, PikaImage *new_image) { PikaImagePrivate *private = PIKA_IMAGE_GET_PRIVATE (image); PikaImagePrivate *new_private = PIKA_IMAGE_GET_PRIVATE (new_image); if (private->parasites) { g_object_unref (new_private->parasites); new_private->parasites = pika_parasite_list_copy (private->parasites); } } static void pika_image_duplicate_color_profile (PikaImage *image, PikaImage *new_image) { PikaColorProfile *profile = pika_image_get_color_profile (image); PikaColorProfile *hidden = _pika_image_get_hidden_profile (image); pika_image_set_color_profile (new_image, profile, NULL); _pika_image_set_hidden_profile (new_image, hidden, FALSE); }