/* 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 "core-types.h" #include "pika.h" #include "pikacontainer.h" #include "pikaguide.h" #include "pikagrouplayer.h" #include "pikaimage.h" #include "pikaimage-guides.h" #include "pikaimage-sample-points.h" #include "pikaimage-scale.h" #include "pikaimage-undo.h" #include "pikaimage-undo-push.h" #include "pikalayer.h" #include "pikaobjectqueue.h" #include "pikaprogress.h" #include "pikaprojection.h" #include "pikasamplepoint.h" #include "pika-log.h" #include "pika-intl.h" void pika_image_scale (PikaImage *image, gint new_width, gint new_height, PikaInterpolationType interpolation_type, PikaProgress *progress) { PikaObjectQueue *queue; PikaItem *item; GList *list; gint old_width; gint old_height; gint offset_x; gint offset_y; gdouble img_scale_w = 1.0; gdouble img_scale_h = 1.0; g_return_if_fail (PIKA_IS_IMAGE (image)); g_return_if_fail (new_width > 0 && new_height > 0); g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress)); pika_set_busy (image->pika); queue = pika_object_queue_new (progress); progress = PIKA_PROGRESS (queue); pika_object_queue_push_container (queue, pika_image_get_layers (image)); pika_object_queue_push (queue, pika_image_get_mask (image)); pika_object_queue_push_container (queue, pika_image_get_channels (image)); pika_object_queue_push_container (queue, pika_image_get_vectors (image)); g_object_freeze_notify (G_OBJECT (image)); pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_SCALE, C_("undo-type", "Scale Image")); old_width = pika_image_get_width (image); old_height = pika_image_get_height (image); img_scale_w = (gdouble) new_width / (gdouble) old_width; img_scale_h = (gdouble) new_height / (gdouble) old_height; offset_x = (old_width - new_width) / 2; offset_y = (old_height - new_height) / 2; /* Push the image size to the stack */ pika_image_undo_push_image_size (image, NULL, offset_x, offset_y, new_width, new_height); /* Set the new width and height early, so below image item setters * (esp. guides and sample points) don't choke about moving stuff * out of the image */ g_object_set (image, "width", new_width, "height", new_height, NULL); /* Scale all layers, channels (including selection mask), and vectors */ while ((item = pika_object_queue_pop (queue))) { if (! pika_item_scale_by_factors (item, img_scale_w, img_scale_h, interpolation_type, progress)) { /* Since 0 < img_scale_w, img_scale_h, failure due to one or more * vanishing scaled layer dimensions. Implicit delete implemented * here. Upstream warning implemented in resize_check_layer_scaling(), * which offers the user the chance to bail out. */ g_return_if_fail (PIKA_IS_LAYER (item)); pika_image_remove_layer (image, PIKA_LAYER (item), TRUE, NULL); } } /* Scale all Guides */ 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_move_guide (image, guide, (position * new_height) / old_height, TRUE); break; case PIKA_ORIENTATION_VERTICAL: pika_image_move_guide (image, guide, (position * new_width) / old_width, TRUE); break; default: break; } } /* Scale all sample points */ 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_move_sample_point (image, sample_point, x * new_width / old_width, y * new_height / old_height, TRUE); } pika_image_undo_group_end (image); g_object_unref (queue); pika_image_size_changed_detailed (image, -offset_x, -offset_y, old_width, old_height); g_object_thaw_notify (G_OBJECT (image)); pika_unset_busy (image->pika); } /** * pika_image_scale_check: * @image: A #PikaImage. * @new_width: The new width. * @new_height: The new height. * @max_memsize: The maximum new memory size. * @new_memsize: The new memory size. * * Inventory the layer list in image and check that it may be * scaled to @new_height and @new_width without problems. * * Returns: #PIKA_IMAGE_SCALE_OK if scaling the image will shrink none * of its layers completely away, and the new image size * is smaller than @max_memsize. * #PIKA_IMAGE_SCALE_TOO_SMALL if scaling would remove some * existing layers. * #PIKA_IMAGE_SCALE_TOO_BIG if the new image size would * exceed the maximum specified in the preferences. **/ PikaImageScaleCheckType pika_image_scale_check (PikaImage *image, gint new_width, gint new_height, gint64 max_memsize, gint64 *new_memsize) { GList *all_layers; GList *list; gint64 current_size; gint64 undo_size; gint64 redo_size; gint64 new_size; g_return_val_if_fail (PIKA_IS_IMAGE (image), PIKA_IMAGE_SCALE_TOO_SMALL); g_return_val_if_fail (new_memsize != NULL, PIKA_IMAGE_SCALE_TOO_SMALL); current_size = pika_object_get_memsize (PIKA_OBJECT (image), NULL); new_size = pika_image_estimate_memsize (image, pika_image_get_component_type (image), new_width, new_height); undo_size = pika_object_get_memsize (PIKA_OBJECT (pika_image_get_undo_stack (image)), NULL); redo_size = pika_object_get_memsize (PIKA_OBJECT (pika_image_get_redo_stack (image)), NULL); current_size -= undo_size + redo_size; new_size -= undo_size + redo_size; PIKA_LOG (IMAGE_SCALE, "old_size = %"G_GINT64_FORMAT" new_size = %"G_GINT64_FORMAT, current_size, new_size); *new_memsize = new_size; if (new_size > current_size && new_size > max_memsize) return PIKA_IMAGE_SCALE_TOO_BIG; all_layers = pika_image_get_layer_list (image); for (list = all_layers; list; list = g_list_next (list)) { PikaItem *item = list->data; /* group layers are updated automatically */ if (pika_viewable_get_children (PIKA_VIEWABLE (item))) continue; if (! pika_item_check_scaling (item, new_width, new_height)) { g_list_free (all_layers); return PIKA_IMAGE_SCALE_TOO_SMALL; } } g_list_free (all_layers); return PIKA_IMAGE_SCALE_OK; }