1
1
mirror of https://gitlab.gnome.org/GNOME/gimp.git synced 2025-10-06 09:32:41 +02:00
Files
gimp/app/core/gimplinklayer.c

1138 lines
40 KiB
C
Raw Permalink Normal View History

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpLinkLayer
* Copyright (C) 2019 Jehan
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
#include "libgimpmath/gimpmath.h"
#include "core-types.h"
#include "gegl/gimp-gegl-loops.h"
#include "gegl/gimp-gegl-utils.h"
#include "gimp.h"
#include "gimpcontext.h"
#include "gimpimage.h"
#include "gimpimage-color-profile.h"
#include "gimpimage-undo.h"
#include "gimpimage-undo-push.h"
#include "gimpitemtree.h"
#include "gimpobjectqueue.h"
#include "gimpprogress.h"
#include "gimplink.h"
#include "gimplinklayer.h"
#include "gimp-intl.h"
#define EPSILON 1e-10
enum
{
LINK_LAYER_XCF_NONE = 0,
LINK_LAYER_XCF_DONT_AUTO_RENAME = 1 << 0,
LINK_LAYER_XCF_MODIFIED = 1 << 1
};
enum
{
PROP_0,
PROP_LINK,
PROP_AUTO_RENAME,
PROP_SCALED_ONLY,
N_PROPS
};
struct _GimpLinkLayerPrivate
{
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
GimpLink *link;
gboolean scaled_only;
gboolean auto_rename;
GimpMatrix3 matrix;
gint offset_x;
gint offset_y;
GimpInterpolationType interpolation;
/* A transient value only useful to know when to drop monitoring after
* a buffer update.
*/
gboolean keep_monitoring;
};
static void gimp_link_layer_finalize (GObject *object);
static void gimp_link_layer_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_link_layer_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static gint64 gimp_link_layer_get_memsize (GimpObject *object,
gint64 *gui_size);
static GimpItem * gimp_link_layer_duplicate (GimpItem *item,
GType new_type);
static gboolean gimp_link_layer_rename (GimpItem *item,
const gchar *new_name,
const gchar *undo_desc,
GError **error);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static void gimp_link_layer_translate (GimpItem *item,
gdouble offset_x,
gdouble offset_y,
gboolean push_undo);
static void gimp_link_layer_scale (GimpItem *item,
gint new_width,
gint new_height,
gint new_offset_x,
gint new_offset_y,
GimpInterpolationType interpolation_type,
GimpProgress *progress);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static void gimp_link_layer_transform (GimpItem *item,
GimpContext *context,
const GimpMatrix3 *matrix,
GimpTransformDirection direction,
GimpInterpolationType interpolation_type,
GimpTransformResize clip_result,
GimpProgress *progress,
gboolean push_undo);
static void gimp_link_layer_set_buffer (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer,
const GeglRectangle *bounds);
static void gimp_link_layer_push_undo (GimpDrawable *drawable,
const gchar *undo_desc,
GeglBuffer *buffer,
gint x,
gint y,
gint width,
gint height);
static void gimp_link_layer_convert_type (GimpLayer *layer,
GimpImage *dest_image,
const Babl *new_format,
GimpColorProfile *src_profile,
GimpColorProfile *dest_profile,
GeglDitherMethod layer_dither_type,
GeglDitherMethod mask_dither_type,
gboolean push_undo,
GimpProgress *progress);
static void gimp_link_layer_render_full (GimpLinkLayer *layer);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static gboolean gimp_link_layer_render_link (GimpLinkLayer *layer);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static gboolean
gimp_link_layer_is_scaling_matrix (GimpLinkLayer *layer,
const GimpMatrix3 *matrix,
gint *new_width,
gint *new_height,
gint *new_offset_x,
gint *new_offset_y);
G_DEFINE_TYPE_WITH_PRIVATE (GimpLinkLayer, gimp_link_layer, GIMP_TYPE_LAYER)
#define parent_class gimp_link_layer_parent_class
static GParamSpec *link_layer_props[N_PROPS] = { NULL, };
static void
gimp_link_layer_class_init (GimpLinkLayerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
GimpItemClass *item_class = GIMP_ITEM_CLASS (klass);
GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass);
GimpLayerClass *layer_class = GIMP_LAYER_CLASS (klass);
object_class->finalize = gimp_link_layer_finalize;
object_class->get_property = gimp_link_layer_get_property;
object_class->set_property = gimp_link_layer_set_property;
gimp_object_class->get_memsize = gimp_link_layer_get_memsize;
viewable_class->default_icon_name = "emblem-symbolic-link";
viewable_class->default_name = _("Link Layer");
item_class->duplicate = gimp_link_layer_duplicate;
item_class->rename = gimp_link_layer_rename;
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
item_class->translate = gimp_link_layer_translate;
item_class->scale = gimp_link_layer_scale;
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
item_class->transform = gimp_link_layer_transform;
item_class->rename_desc = _("Rename Link Layer");
item_class->translate_desc = _("Move Link Layer");
item_class->scale_desc = _("Scale Link Layer");
item_class->resize_desc = _("Resize Link Layer");
item_class->flip_desc = _("Flip Link Layer");
item_class->rotate_desc = _("Rotate Link Layer");
item_class->transform_desc = _("Transform Link Layer");
drawable_class->set_buffer = gimp_link_layer_set_buffer;
drawable_class->push_undo = gimp_link_layer_push_undo;
layer_class->convert_type = gimp_link_layer_convert_type;
link_layer_props[PROP_LINK] = g_param_spec_object ("link",
NULL, NULL,
GIMP_TYPE_LINK,
GIMP_PARAM_READWRITE |
GIMP_PARAM_STATIC_STRINGS);
link_layer_props[PROP_AUTO_RENAME] = g_param_spec_boolean ("auto-rename",
NULL, NULL,
TRUE,
GIMP_PARAM_READWRITE |
GIMP_PARAM_STATIC_STRINGS);
link_layer_props[PROP_SCALED_ONLY] = g_param_spec_boolean ("scaled-only",
NULL, NULL,
FALSE,
GIMP_PARAM_READWRITE |
GIMP_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, link_layer_props);
}
static void
gimp_link_layer_init (GimpLinkLayer *layer)
{
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
layer->p = gimp_link_layer_get_instance_private (layer);
layer->p->link = NULL;
gimp_matrix3_identity (&layer->p->matrix);
layer->p->scaled_only = FALSE;
layer->p->auto_rename = FALSE;
layer->p->offset_x = 0;
layer->p->offset_y = 0;
layer->p->interpolation = GIMP_INTERPOLATION_NONE;
layer->p->keep_monitoring = FALSE;
}
static void
gimp_link_layer_finalize (GObject *object)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
g_clear_object (&layer->p->link);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_link_layer_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
switch (property_id)
{
case PROP_LINK:
g_value_set_object (value, layer->p->link);
break;
case PROP_AUTO_RENAME:
g_value_set_boolean (value, layer->p->auto_rename);
break;
case PROP_SCALED_ONLY:
g_value_set_boolean (value, layer->p->scaled_only);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_link_layer_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
switch (property_id)
{
case PROP_LINK:
gimp_link_layer_set_link (layer, g_value_get_object (value), FALSE);
break;
case PROP_AUTO_RENAME:
layer->p->auto_rename = g_value_get_boolean (value);
break;
case PROP_SCALED_ONLY:
layer->p->scaled_only = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gint64
gimp_link_layer_get_memsize (GimpObject *object,
gint64 *gui_size)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (object);
gint64 memsize = 0;
memsize += gimp_object_get_memsize (GIMP_OBJECT (layer->p->link),
gui_size);
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
static GimpItem *
gimp_link_layer_duplicate (GimpItem *item,
GType new_type)
{
GimpItem *new_item;
g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);
new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);
if (GIMP_IS_LINK_LAYER (new_item))
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (item);
GimpLinkLayer *new_layer = GIMP_LINK_LAYER (new_item);
GimpLink *link = NULL;
GeglBuffer *buffer;
gint width;
gint height;
if (layer->p->link)
{
link = gimp_link_duplicate (layer->p->link);
gimp_link_layer_set_link (new_layer, link, FALSE);
}
gimp_config_sync (G_OBJECT (layer), G_OBJECT (new_layer), 0);
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
if (width != gimp_item_get_width (GIMP_ITEM (new_layer)) ||
height != gimp_item_get_height (GIMP_ITEM (new_layer)))
{
GeglBuffer *new_buffer;
GeglRectangle bounds;
gimp_item_get_offset (GIMP_ITEM (new_layer), &bounds.x, &bounds.y);
bounds.width = 0;
bounds.height = 0;
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
gimp_drawable_get_format (GIMP_DRAWABLE (new_layer)));
GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (GIMP_DRAWABLE (new_layer),
FALSE, NULL,
new_buffer, &bounds);
g_object_unref (new_buffer);
}
gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
gimp_drawable_get_buffer (GIMP_DRAWABLE (new_layer)), NULL);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
new_layer->p->scaled_only = layer->p->scaled_only;
new_layer->p->auto_rename = layer->p->auto_rename;
new_layer->p->matrix = layer->p->matrix;
new_layer->p->offset_x = layer->p->offset_x;
new_layer->p->offset_y = layer->p->offset_y;
new_layer->p->interpolation = layer->p->interpolation;
new_layer->p->keep_monitoring = FALSE;
g_clear_object (&link);
}
return new_item;
}
static gboolean
gimp_link_layer_rename (GimpItem *item,
const gchar *new_name,
const gchar *undo_desc,
GError **error)
{
if (GIMP_ITEM_CLASS (parent_class)->rename (item, new_name, undo_desc, error))
{
g_object_set (item, "auto-rename", FALSE, NULL);
return TRUE;
}
return FALSE;
}
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static void
gimp_link_layer_translate (GimpItem *item,
gdouble offset_x,
gdouble offset_y,
gboolean push_undo)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (item);
if (! gimp_matrix3_is_identity (&layer->p->matrix))
gimp_matrix3_translate (&layer->p->matrix, offset_x, offset_y);
GIMP_ITEM_CLASS (parent_class)->translate (item, offset_x, offset_y, push_undo);
}
static void
gimp_link_layer_scale (GimpItem *item,
gint new_width,
gint new_height,
gint new_offset_x,
gint new_offset_y,
GimpInterpolationType interpolation_type,
GimpProgress *progress)
{
GimpLinkLayer *link_layer = GIMP_LINK_LAYER (item);
GimpLayer *layer = GIMP_LAYER (item);
GimpObjectQueue *queue = NULL;
if (progress && layer->mask)
{
GimpLayerMask *mask;
queue = gimp_object_queue_new (progress);
progress = GIMP_PROGRESS (queue);
/* temporarily set layer->mask to NULL, so that its size won't be counted
* when pushing the layer to the queue.
*/
mask = layer->mask;
layer->mask = NULL;
gimp_object_queue_push (queue, layer);
gimp_object_queue_push (queue, mask);
layer->mask = mask;
}
if (queue)
gimp_object_queue_pop (queue);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
if (gimp_link_is_vector (link_layer->p->link) &&
gimp_link_is_monitored (link_layer->p->link) &&
gimp_matrix3_is_identity (&link_layer->p->matrix))
{
/* Non-modified vector images are always recomputed from the
* source file and therefore are always sharp.
*/
gimp_link_set_size (link_layer->p->link, new_width, new_height, FALSE);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
gimp_item_set_offset (item, new_offset_x, new_offset_y);
gimp_link_layer_render_link (link_layer);
}
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
else if (! gimp_matrix3_is_identity (&link_layer->p->matrix))
{
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
GimpMatrix3 matrix = link_layer->p->matrix;
gdouble x_scale_factor;
gdouble y_scale_factor;
gint offset_x;
gint offset_y;
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
x_scale_factor = (gdouble) new_width / gimp_item_get_width (item);
y_scale_factor = (gdouble) new_height / gimp_item_get_height (item);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
gimp_matrix3_scale (&matrix, x_scale_factor, y_scale_factor);
gimp_link_layer_set_transform (link_layer, &matrix, interpolation_type, TRUE);
/* Unfortunately we can't know the proper translation offset to
* set before we replay the full matrix (the offsets may have been
* changed in previous transformations).
* To avoid re-loading the source image twice though, I don't call
* gimp_link_layer_set_transform() again but directly edit the
* matrix and set the offset.
*/
gimp_item_get_offset (item, &offset_x, &offset_y);
gimp_matrix3_translate (&link_layer->p->matrix,
new_offset_x - offset_x,
new_offset_y - offset_y);
gimp_item_set_offset (item, new_offset_x, new_offset_y);
gimp_drawable_update_all (GIMP_DRAWABLE (layer));
}
else if (gimp_link_is_monitored (link_layer->p->link))
{
GimpMatrix3 matrix = link_layer->p->matrix;
gdouble x_scale_factor;
gdouble y_scale_factor;
gint offset_x;
gint offset_y;
x_scale_factor = (gdouble) new_width / gimp_item_get_width (item);
y_scale_factor = (gdouble) new_height / gimp_item_get_height (item);
gimp_matrix3_scale (&matrix, x_scale_factor, y_scale_factor);
gimp_item_get_offset (item, &offset_x, &offset_y);
gimp_matrix3_translate (&matrix,
new_offset_x - offset_x,
new_offset_y - offset_y);
gimp_link_layer_set_transform (link_layer, &matrix, interpolation_type, TRUE);
}
else
{
GIMP_ITEM_CLASS (parent_class)->scale (GIMP_ITEM (layer),
new_width, new_height,
new_offset_x, new_offset_y,
interpolation_type, progress);
}
if (layer->mask)
{
if (queue)
gimp_object_queue_pop (queue);
gimp_item_scale (GIMP_ITEM (layer->mask),
new_width, new_height,
new_offset_x, new_offset_y,
interpolation_type, progress);
}
g_clear_object (&queue);
}
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static void
gimp_link_layer_transform (GimpItem *item,
GimpContext *context,
const GimpMatrix3 *matrix,
GimpTransformDirection direction,
GimpInterpolationType interpolation_type,
GimpTransformResize clip_result,
GimpProgress *progress,
gboolean push_undo)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (item);
gboolean keep_monitoring;
if (gimp_matrix3_is_identity (matrix))
return;
if (push_undo)
gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
_("Transform Link Layer"), layer);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
if (gimp_link_is_vector (layer->p->link) &&
gimp_link_is_monitored (layer->p->link) &&
gimp_matrix3_is_identity (&layer->p->matrix))
{
gint new_width;
gint new_height;
gint new_offset_x;
gint new_offset_y;
if (gimp_link_layer_is_scaling_matrix (layer, matrix,
&new_width, &new_height,
&new_offset_x, &new_offset_y))
{
/* Scaling when no other transformation has happened yet is
* special-case for vector links, because we can do even
* better by reloading from source.
*/
gimp_link_layer_scale (item, new_width, new_height,
new_offset_x, new_offset_y,
interpolation_type, progress);
return;
}
}
/* Clipping would produce different results when applied on a single
* step of multiple transformations. So only "adjust" transformations
* are stored non-destructively. Any clip/crop transformation triggers
* a destructive transform.
*
* Note: we could also store non-destructively a single clipped
* transformation, but that would be harder to document and explain.
*/
keep_monitoring = (gimp_link_is_monitored (layer->p->link) &&
clip_result == GIMP_TRANSFORM_RESIZE_ADJUST);
if (keep_monitoring)
{
GimpMatrix3 left = *matrix;
GimpMatrix3 right = layer->p->matrix;
if (direction == GIMP_TRANSFORM_BACKWARD)
gimp_matrix3_invert (&left);
gimp_matrix3_mult (&left, &right);
gimp_link_layer_set_transform (layer, &right, interpolation_type, TRUE);
}
else
{
/* Reset the transformation matrix. */
gimp_matrix3_identity (&layer->p->matrix);
GIMP_ITEM_CLASS (parent_class)->transform (item, context,
matrix, direction,
interpolation_type,
clip_result, progress,
push_undo);
}
}
static void
gimp_link_layer_set_buffer (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer,
const GeglRectangle *bounds)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (drawable);
GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
if (push_undo)
{
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD, undo_desc);
gimp_image_undo_push_link_layer (image, NULL, layer);
}
GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
FALSE, undo_desc,
buffer,
bounds);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
if (push_undo)
{
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
if (layer->p->link &&
gimp_link_is_monitored (layer->p->link) &&
! layer->p->keep_monitoring)
gimp_link_freeze (layer->p->link);
gimp_image_undo_group_end (image);
}
}
static void
gimp_link_layer_push_undo (GimpDrawable *drawable,
const gchar *undo_desc,
GeglBuffer *buffer,
gint x,
gint y,
gint width,
gint height)
{
GimpLinkLayer *layer = GIMP_LINK_LAYER (drawable);
GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
gboolean monitored;
monitored = gimp_link_is_monitored (layer->p->link);
if (monitored)
{
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE, undo_desc);
gimp_image_undo_push_link_layer (image, NULL, layer);
gimp_link_freeze (layer->p->link);
}
GIMP_DRAWABLE_CLASS (parent_class)->push_undo (drawable, undo_desc,
buffer,
x, y, width, height);
if (monitored)
{
gimp_image_undo_group_end (image);
}
}
static void
gimp_link_layer_convert_type (GimpLayer *layer,
GimpImage *dest_image,
const Babl *new_format,
GimpColorProfile *src_profile,
GimpColorProfile *dest_profile,
GeglDitherMethod layer_dither_type,
GeglDitherMethod mask_dither_type,
gboolean push_undo,
GimpProgress *progress)
{
GimpLinkLayer *link_layer = GIMP_LINK_LAYER (layer);
GimpImage *image = gimp_item_get_image (GIMP_ITEM (link_layer));
GIMP_LAYER_CLASS (parent_class)->convert_type (layer, dest_image,
new_format,
src_profile,
dest_profile,
layer_dither_type,
mask_dither_type,
push_undo,
progress);
if (link_layer->p->link && gimp_link_is_monitored (link_layer->p->link))
{
if (push_undo)
gimp_image_undo_push_link_layer (image, NULL, link_layer);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
gimp_link_layer_render_link (link_layer);
}
}
/* public functions */
/**
* gimp_link_layer_new:
* @image: the #GimpImage the layer should belong to
* @text: a #GimpText object
*
* Creates a new link layer.
*
* Return value: a new #GimpLinkLayer or %NULL in case of a problem
**/
GimpLayer *
gimp_link_layer_new (GimpImage *image,
GimpLink *link)
{
GimpLinkLayer *layer;
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
layer =
GIMP_LINK_LAYER (gimp_drawable_new (GIMP_TYPE_LINK_LAYER,
image, NULL,
0, 0, 1, 1,
gimp_image_get_layer_format (image,
TRUE)));
gimp_layer_set_mode (GIMP_LAYER (layer),
gimp_image_get_default_new_layer_mode (image),
FALSE);
if (! gimp_link_layer_set_link (layer, link, FALSE))
g_clear_object (&layer);
return GIMP_LAYER (layer);
}
GimpLink *
gimp_link_layer_get_link (GimpLinkLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), NULL);
return layer->p->link;
}
gboolean
gimp_link_layer_set_link (GimpLinkLayer *layer,
GimpLink *link,
gboolean push_undo)
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
{
return gimp_link_layer_set_link_with_matrix (layer, link, NULL,
GIMP_INTERPOLATION_NONE,
0, 0, push_undo);
}
gboolean
gimp_link_layer_set_link_with_matrix (GimpLinkLayer *layer,
GimpLink *link,
GimpMatrix3 *matrix,
GimpInterpolationType interpolation_type,
gint offset_x,
gint offset_y,
gboolean push_undo)
{
gboolean rendered = FALSE;
g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
g_return_val_if_fail (GIMP_IS_LINK (link), FALSE);
if (gimp_item_is_attached (GIMP_ITEM (layer)) && push_undo)
gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
_("Set layer link"), layer);
if (layer->p->link)
{
g_signal_handlers_disconnect_by_func (layer->p->link,
G_CALLBACK (gimp_link_layer_render_full),
layer);
}
g_set_object (&layer->p->link, link);
if (link)
{
g_signal_connect_object (link, "changed",
G_CALLBACK (gimp_link_layer_render_full),
layer, G_CONNECT_SWAPPED);
if (gimp_link_is_monitored (link))
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
{
if (matrix == NULL)
{
gimp_matrix3_identity (&layer->p->matrix);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
rendered = gimp_link_layer_render_link (layer);
}
else
{
if (! gimp_matrix3_is_identity (matrix))
{
layer->p->offset_x = offset_x;
layer->p->offset_y = offset_y;
}
rendered = gimp_link_layer_set_transform (layer, matrix, interpolation_type, push_undo);
}
}
}
g_object_notify (G_OBJECT (layer), "link");
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));
return rendered;
}
/**
* gimp_link_layer_discard:
* @layer: a #GimpLinkLayer
*
* Discards the link. This makes @layer behave like a
* normal layer.
*/
void
gimp_link_layer_discard (GimpLinkLayer *layer)
{
g_return_if_fail (GIMP_IS_LINK_LAYER (layer));
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
_("Discard Link"), layer);
gimp_link_freeze (layer->p->link);
/* Triggers thumbnail update. */
gimp_drawable_update_all (GIMP_DRAWABLE (layer));
/* Triggers contextual menu update. */
gimp_image_flush (gimp_item_get_image (GIMP_ITEM (layer)));
}
void
gimp_link_layer_monitor (GimpLinkLayer *layer)
{
g_return_if_fail (GIMP_IS_LINK_LAYER (layer));
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
gimp_image_undo_push_link_layer (gimp_item_get_image (GIMP_ITEM (layer)),
_("Monitor Link"), layer);
gimp_link_thaw (layer->p->link);
gimp_matrix3_identity (&layer->p->matrix);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
gimp_link_layer_render_link (layer);
}
gboolean
gimp_link_layer_is_monitored (GimpLinkLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
return (GIMP_LINK_LAYER (layer)->p->link &&
gimp_link_is_monitored (GIMP_LINK_LAYER (layer)->p->link));
}
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
gboolean
gimp_link_layer_get_transform (GimpLinkLayer *layer,
GimpMatrix3 *matrix,
gint *offset_x,
gint *offset_y,
GimpInterpolationType *interpolation)
{
g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
*matrix = layer->p->matrix;
*offset_x = layer->p->offset_x;
*offset_y = layer->p->offset_y;
*interpolation = layer->p->interpolation;
return ! gimp_matrix3_is_identity (matrix);
}
gboolean
gimp_link_layer_set_transform (GimpLinkLayer *layer,
GimpMatrix3 *matrix,
GimpInterpolationType interpolation_type,
gboolean push_undo)
{
Gimp *gimp;
gboolean rendered;
g_return_val_if_fail (GIMP_IS_LINK_LAYER (layer), FALSE);
g_return_val_if_fail (gimp_link_is_monitored (layer->p->link), FALSE);
gimp = (gimp_item_get_image (GIMP_ITEM (layer)))->gimp;
if (gimp_matrix3_is_identity (&layer->p->matrix))
/* First transformation: store the initial offset. */
gimp_item_get_offset (GIMP_ITEM (layer), &layer->p->offset_x, &layer->p->offset_y);
gimp_item_set_offset (GIMP_ITEM (layer), layer->p->offset_x, layer->p->offset_y);
rendered = gimp_link_layer_render_link (layer);
if (! gimp_matrix3_is_identity (matrix))
{
layer->p->keep_monitoring = TRUE;
GIMP_ITEM_CLASS (parent_class)->transform (GIMP_ITEM (layer),
gimp_get_user_context (gimp),
matrix,
GIMP_TRANSFORM_FORWARD,
interpolation_type,
GIMP_TRANSFORM_RESIZE_ADJUST,
NULL, push_undo);
layer->p->keep_monitoring = FALSE;
}
layer->p->matrix = *matrix;
/* Interpolations are used to obtain reasonable quality. Considering
* that doing a single transform will always be better than several
* transforms in a row, it's OK to just drop the interpolation
* algorithm of previous transforms. We just store the last one.
*/
layer->p->interpolation = interpolation_type;
return rendered;
}
gboolean
gimp_item_is_link_layer (GimpItem *item)
{
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
return (GIMP_IS_LINK_LAYER (item) &&
gimp_link_layer_is_monitored (GIMP_LINK_LAYER (item)));
}
void
gimp_link_layer_set_xcf_flags (GimpLinkLayer *layer,
guint32 flags)
{
g_return_if_fail (GIMP_IS_LINK_LAYER (layer));
g_object_set (layer,
"auto-rename", (flags & LINK_LAYER_XCF_DONT_AUTO_RENAME) == 0,
NULL);
if ((flags & LINK_LAYER_XCF_MODIFIED) != 0)
gimp_link_freeze (layer->p->link);
}
guint32
gimp_link_layer_get_xcf_flags (GimpLinkLayer *link_layer)
{
guint flags = 0;
g_return_val_if_fail (GIMP_IS_LINK_LAYER (link_layer), 0);
if (! link_layer->p->auto_rename)
flags |= LINK_LAYER_XCF_DONT_AUTO_RENAME;
if (! gimp_link_is_monitored (link_layer->p->link))
flags |= LINK_LAYER_XCF_MODIFIED;
return flags;
}
/* private functions */
static void
gimp_link_layer_render_full (GimpLinkLayer *layer)
{
gimp_link_layer_render_link (layer);
if (! gimp_matrix3_is_identity (&layer->p->matrix))
{
Gimp *gimp = (gimp_item_get_image (GIMP_ITEM (layer)))->gimp;
gimp_item_set_offset (GIMP_ITEM (layer), layer->p->offset_x, layer->p->offset_y);
layer->p->keep_monitoring = TRUE;
GIMP_ITEM_CLASS (parent_class)->transform (GIMP_ITEM (layer),
gimp_get_user_context (gimp),
&layer->p->matrix,
GIMP_TRANSFORM_FORWARD,
layer->p->interpolation,
GIMP_TRANSFORM_RESIZE_ADJUST,
NULL, FALSE);
layer->p->keep_monitoring = FALSE;
}
}
static gboolean
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
gimp_link_layer_render_link (GimpLinkLayer *layer)
{
GimpDrawable *drawable;
GimpItem *item;
GimpImage *image;
GeglBuffer *buffer;
gdouble xres;
gdouble yres;
gint width;
gint height;
if (! layer->p->link)
return FALSE;
drawable = GIMP_DRAWABLE (layer);
item = GIMP_ITEM (layer);
image = gimp_item_get_image (item);
gimp_image_get_resolution (image, &xres, &yres);
/* TODO: I could imagine a GimpBusyBox (to be made as GimpProgress) in
* the later list showing layer update.
*/
buffer = gimp_link_get_buffer (layer->p->link);
if (! buffer)
return FALSE;
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
g_object_freeze_notify (G_OBJECT (drawable));
if ((width != gimp_item_get_width (item) ||
height != gimp_item_get_height (item)))
{
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
GeglBuffer *new_buffer;
GeglRectangle bounds;
gimp_item_get_offset (GIMP_ITEM (drawable),
&bounds.x, &bounds.y);
bounds.width = 0;
bounds.height = 0;
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
gimp_drawable_get_format (drawable));
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
FALSE, NULL,
new_buffer, &bounds);
g_object_unref (new_buffer);
if (gimp_layer_get_mask (GIMP_LAYER (layer)))
{
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));
static GimpContext *unused_eek = NULL;
if (! unused_eek)
unused_eek = gimp_context_new (image->gimp, "eek", NULL);
gimp_item_resize (GIMP_ITEM (mask),
unused_eek, GIMP_FILL_TRANSPARENT,
width, height, 0, 0);
}
}
if (layer->p->auto_rename)
{
GimpItem *item = GIMP_ITEM (layer);
gchar *name = NULL;
if (layer->p->link)
{
name = g_strdup (gimp_object_get_name (layer->p->link));
}
if (! name || ! name[0])
{
g_free (name);
name = g_strdup (_("Link Layer"));
}
if (gimp_item_is_attached (item))
{
gimp_item_tree_rename_item (gimp_item_get_tree (item), item,
name, FALSE, NULL);
g_free (name);
}
else
{
gimp_object_take_name (GIMP_OBJECT (layer), name);
}
}
gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
gimp_drawable_get_buffer (drawable), NULL);
g_object_thaw_notify (G_OBJECT (drawable));
gimp_drawable_update (drawable, 0, 0, width, height);
gimp_image_flush (image);
return (width > 0 && height > 0);
}
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
static gboolean
gimp_link_layer_is_scaling_matrix (GimpLinkLayer *layer,
const GimpMatrix3 *matrix,
gint *new_width,
gint *new_height,
gint *new_offset_x,
gint *new_offset_y)
{
gboolean is_scaling;
/* Scaling 3x3 matrix on a 2D plane (with optional translation). */
is_scaling = (matrix->coeff[0][0] > 0.0 && matrix->coeff[0][1] == 0.0 &&
matrix->coeff[1][0] < EPSILON && matrix->coeff[1][1] > 0.0 &&
matrix->coeff[2][0] < EPSILON && matrix->coeff[2][1] < EPSILON && matrix->coeff[2][2] == 1.0);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
if (is_scaling)
{
gint width;
gint height;
gint offset_x;
gint offset_y;
width = gimp_item_get_width (GIMP_ITEM (layer));
height = gimp_item_get_height (GIMP_ITEM (layer));
gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);
*new_width = (gint) (width * matrix->coeff[0][0]);
*new_height = (gint) (height * matrix->coeff[1][1]);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
*new_offset_x = (gint) (offset_x * matrix->coeff[0][0] + matrix->coeff[0][2]);
*new_offset_y = (gint) (offset_y * matrix->coeff[1][1] + matrix->coeff[1][2]);
app: add generic support for less destructive transform tools. This commit will make all transform tools run on a link layer cumulate their transform matrix on top of the previous transform steps. It means that as long as you don't edit the pixels another way (e.g. with a paint tool), all your transformations will apply as a single transformation. For instance it means that applying several transform tool steps on a monitored link layer will be less destructive than applying the exact same transformations on the exact same "normal" layer contents. Even scaling an image to 1x1 then back to a big size will work very fine! Note nevertheless the following limitations in current implementation: * The link layer with transformations will still show as a standard link layer. Nothing says it has transformation applied on it right now. * To drop transformations applied on a link layer, you have to discard the link info, then monitor the link again. A specific action in the contextual menu could be worth it. * This should work with all transform tools (scale, rotation, unified, perspective, 3D, even Warp…) but it won't work for the Flip tool, nor will it work for the Transform actions. I will need to implement GimpItem's rotate(), flip() and resize() methods next. * The layer mask would still be destructively transformed (I have not made any tests with layer masks yet, but this should be done next too). * I think that the "scaled-only" property is now meaningless. It is now being replaced by the presence of the GimpMatrix3. Nevertheless I have still not removed this property. * The load/save code has not been redone yet to include all these changes. The kind of caveats we'd have to know about (and which are not planned for change, because it's just how it is): * Any intermediate interpolation methods are dropped when cumulating transform steps. Only the last interpolation is stored. This is because anyway the interpolation is only there as the best algorithm where we visually see the less quality loss. Applying several transformations as a single matrix will always be visually better than applying several matrices (whatever the intermediate interpolation methods chosen). * This only works with the "Adjust" clipping method (basically no clipping) because 2 transform steps with clipping won't produce the same result as the multiplied matrix with clipping. It means that applying a transform with clipping will downgrade your link layer to being a normal layer. The only issue I have with this is how to best convey that clipping is a major setting setting here, which disables our less-destructive abilities. Right now, people will just have to "know" it. * Vector link layers in particular will have 2 levels of non/less-destructivity transforms. In particular any scaling (both through "Scale Image", "Scale Layers" and transform tools — since I added code to detect a matrix doing only scaling and optionally translation) done **first** will be completely non-destructive since we will simply reload the original vector source at the right dimensions. Any other kind of transforms will be appended through the cumulative matrix, as raster link layers. This also includes scaling done **after** other transforms, since we cannot easily move the scaling first (matrix multiplication is not commutative). This second level of scaling will therefore be *less* destructive, but still destructive. It is possible eventually to improve the whole thing if we add some day the ability to request loading a vector image with a transform matrix (it will then be up to each vector format plug-in to support this feature or not). Note: it could be argued that this whole implementation could in fact be moved over to base layers, since it would allow also less-destructivity when applying multiple transformations in a row. We would only merge results once we edit pixels more directly. But I think that it's not a bad idea to experiment with this new code in the link layer. Eventually I may likely move this to the parent GimpLayer if no blocking issues are found.
2025-08-13 14:49:12 +02:00
}
return is_scaling;
}