[gimp] app: add gimp_image_transform()



commit c45f1b4148d1797a85d4785135444a7ced88da15
Author: Ell <ell_se yahoo com>
Date:   Sat Aug 10 18:42:51 2019 +0300

    app: add gimp_image_transform()
    
    Add a new gimp_image_transform() function, which transforms the
    entire image, including all layers, channels (including selection
    mask), vectors, guides, and sample points, according to a
    transformation matrix.  The canvas is resized according to the
    clip_result parameter, the same way drawables are resized during
    transformation; the layers are resized using ADJUST mode
    regardless.

 app/core/Makefile.am           |   2 +
 app/core/core-enums.c          |   2 +
 app/core/core-enums.h          |   1 +
 app/core/gimpimage-transform.c | 338 +++++++++++++++++++++++++++++++++++++++++
 app/core/gimpimage-transform.h |  34 +++++
 app/core/gimpimage-undo.c      |   1 +
 6 files changed, 378 insertions(+)
---
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index a06b289b39..be90e742ed 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -325,6 +325,8 @@ libappcore_a_sources = \
        gimpimage-snap.h                        \
        gimpimage-symmetry.c                    \
        gimpimage-symmetry.h                    \
+       gimpimage-transform.c                   \
+       gimpimage-transform.h                   \
        gimpimage-undo.c                        \
        gimpimage-undo.h                        \
        gimpimage-undo-push.c                   \
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index 90580abdf5..3baf9ee169 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -1096,6 +1096,7 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_GROUP_IMAGE_RESIZE, "GIMP_UNDO_GROUP_IMAGE_RESIZE", "group-image-resize" },
     { GIMP_UNDO_GROUP_IMAGE_FLIP, "GIMP_UNDO_GROUP_IMAGE_FLIP", "group-image-flip" },
     { GIMP_UNDO_GROUP_IMAGE_ROTATE, "GIMP_UNDO_GROUP_IMAGE_ROTATE", "group-image-rotate" },
+    { GIMP_UNDO_GROUP_IMAGE_TRANSFORM, "GIMP_UNDO_GROUP_IMAGE_TRANSFORM", "group-image-transform" },
     { GIMP_UNDO_GROUP_IMAGE_CROP, "GIMP_UNDO_GROUP_IMAGE_CROP", "group-image-crop" },
     { GIMP_UNDO_GROUP_IMAGE_CONVERT, "GIMP_UNDO_GROUP_IMAGE_CONVERT", "group-image-convert" },
     { GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, "GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE", "group-image-item-remove" },
@@ -1195,6 +1196,7 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_GROUP_IMAGE_RESIZE, NC_("undo-type", "Resize image"), NULL },
     { GIMP_UNDO_GROUP_IMAGE_FLIP, NC_("undo-type", "Flip image"), NULL },
     { GIMP_UNDO_GROUP_IMAGE_ROTATE, NC_("undo-type", "Rotate image"), NULL },
+    { GIMP_UNDO_GROUP_IMAGE_TRANSFORM, NC_("undo-type", "Transform image"), NULL },
     { GIMP_UNDO_GROUP_IMAGE_CROP, NC_("undo-type", "Crop image"), NULL },
     { GIMP_UNDO_GROUP_IMAGE_CONVERT, NC_("undo-type", "Convert image"), NULL },
     { GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, NC_("undo-type", "Remove item"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 4b6ea7c0ac..7b569c9ffd 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -502,6 +502,7 @@ typedef enum /*< pdb-skip >*/
   GIMP_UNDO_GROUP_IMAGE_RESIZE,          /*< desc="Resize image"                   >*/
   GIMP_UNDO_GROUP_IMAGE_FLIP,            /*< desc="Flip image"                     >*/
   GIMP_UNDO_GROUP_IMAGE_ROTATE,          /*< desc="Rotate image"                   >*/
+  GIMP_UNDO_GROUP_IMAGE_TRANSFORM,       /*< desc="Transform image"                >*/
   GIMP_UNDO_GROUP_IMAGE_CROP,            /*< desc="Crop image"                     >*/
   GIMP_UNDO_GROUP_IMAGE_CONVERT,         /*< desc="Convert image"                  >*/
   GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE,     /*< desc="Remove item"                    >*/
diff --git a/app/core/gimpimage-transform.c b/app/core/gimpimage-transform.c
new file mode 100644
index 0000000000..afe4cfc72d
--- /dev/null
+++ b/app/core/gimpimage-transform.c
@@ -0,0 +1,338 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpimage-transform.c
+ * Copyright (C) 2019 Ell
+ *
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "core-types.h"
+
+#include "vectors/gimpvectors.h"
+
+#include "gimp.h"
+#include "gimp-transform-resize.h"
+#include "gimp-transform-utils.h"
+#include "gimpchannel.h"
+#include "gimpcontext.h"
+#include "gimpguide.h"
+#include "gimpimage.h"
+#include "gimpimage-guides.h"
+#include "gimpimage-sample-points.h"
+#include "gimpimage-transform.h"
+#include "gimpimage-undo.h"
+#include "gimpimage-undo-push.h"
+#include "gimpitem.h"
+#include "gimpobjectqueue.h"
+#include "gimpprogress.h"
+#include "gimpsamplepoint.h"
+
+
+#define EPSILON 1e-6
+
+
+/*  local function prototypes  */
+
+static void    gimp_image_transform_guides        (GimpImage           *image,
+                                                   const GimpMatrix3   *matrix,
+                                                   const GeglRectangle *old_bounds);
+static void    gimp_image_transform_sample_points (GimpImage           *image,
+                                                   const GimpMatrix3   *matrix,
+                                                   const GeglRectangle *old_bounds);
+
+
+/*  private functions  */
+
+static void
+gimp_image_transform_guides (GimpImage           *image,
+                             const GimpMatrix3   *matrix,
+                             const GeglRectangle *old_bounds)
+{
+  GList *iter;
+
+  for (iter = gimp_image_get_guides (image); iter;)
+    {
+      GimpGuide           *guide           = iter->data;
+      GimpOrientationType  old_orientation = gimp_guide_get_orientation (guide);
+      gint                 old_position    = gimp_guide_get_position (guide);
+      GimpOrientationType  new_orientation;
+      gint                 new_position;
+      GimpVector2          vertices[2];
+      gint                 n_vertices;
+      GimpVector2          diff;
+
+      iter = g_list_next (iter);
+
+      switch (old_orientation)
+        {
+        case GIMP_ORIENTATION_HORIZONTAL:
+          vertices[0].x = old_bounds->x;
+          vertices[0].y = old_bounds->y + old_position;
+
+          vertices[1].x = old_bounds->x + old_bounds->width / 2.0;
+          vertices[1].y = old_bounds->y + old_position;
+          break;
+
+        case GIMP_ORIENTATION_VERTICAL:
+          vertices[0].x = old_bounds->x + old_position;
+          vertices[0].y = old_bounds->y;
+
+          vertices[1].x = old_bounds->x + old_position;
+          vertices[1].y = old_bounds->y + old_bounds->height / 2.0;
+          break;
+
+        case GIMP_ORIENTATION_UNKNOWN:
+          g_return_if_reached ();
+        }
+
+      gimp_transform_polygon (matrix,
+                              vertices, 2, FALSE,
+                              vertices, &n_vertices);
+
+      if (n_vertices < 2)
+        {
+          gimp_image_remove_guide (image, guide, TRUE);
+
+          continue;
+        }
+
+      gimp_vector2_sub (&diff, &vertices[1], &vertices[0]);
+
+      if (gimp_vector2_length (&diff) <= EPSILON)
+        {
+          gimp_image_remove_guide (image, guide, TRUE);
+
+          continue;
+        }
+
+      if (fabs (diff.x) >= fabs (diff.y))
+        {
+          new_orientation = GIMP_ORIENTATION_HORIZONTAL;
+          new_position    = SIGNED_ROUND (vertices[1].y);
+
+          if (new_position < 0 || new_position > gimp_image_get_height (image))
+            {
+              gimp_image_remove_guide (image, guide, TRUE);
+
+              continue;
+            }
+        }
+      else
+        {
+          new_orientation = GIMP_ORIENTATION_VERTICAL;
+          new_position    = SIGNED_ROUND (vertices[1].x);
+
+          if (new_position < 0 || new_position > gimp_image_get_width (image))
+            {
+              gimp_image_remove_guide (image, guide, TRUE);
+
+              continue;
+            }
+        }
+
+      if (new_orientation != old_orientation ||
+          new_position    != old_position)
+        {
+          gimp_image_undo_push_guide (image, NULL, guide);
+
+          gimp_guide_set_orientation (guide, new_orientation);
+          gimp_guide_set_position    (guide, new_position);
+
+          gimp_image_guide_moved (image, guide);
+        }
+    }
+}
+
+static void
+gimp_image_transform_sample_points (GimpImage           *image,
+                                    const GimpMatrix3   *matrix,
+                                    const GeglRectangle *old_bounds)
+{
+  GList *iter;
+
+  for (iter = gimp_image_get_sample_points (image); iter;)
+    {
+      GimpSamplePoint     *sample_point = iter->data;
+      gint                 old_x;
+      gint                 old_y;
+      gint                 new_x;
+      gint                 new_y;
+      GimpVector2          vertices[1];
+      gint                 n_vertices;
+
+      iter = g_list_next (iter);
+
+      gimp_sample_point_get_position (sample_point, &old_x, &old_y);
+
+      vertices[0].x = old_x;
+      vertices[0].y = old_y;
+
+      gimp_transform_polygon (matrix,
+                              vertices, 1, FALSE,
+                              vertices, &n_vertices);
+
+      if (n_vertices < 1)
+        {
+          gimp_image_remove_sample_point (image, sample_point, TRUE);
+
+          continue;
+        }
+
+      new_x = SIGNED_ROUND (vertices[0].x);
+      new_y = SIGNED_ROUND (vertices[0].y);
+
+      if (new_x < 0 || new_x >= gimp_image_get_width  (image) ||
+          new_y < 0 || new_y >= gimp_image_get_height (image))
+        {
+          gimp_image_remove_sample_point (image, sample_point, TRUE);
+
+          continue;
+        }
+
+      if (new_x != old_x || new_y != old_y)
+        gimp_image_move_sample_point (image, sample_point, new_x, new_y, TRUE);
+    }
+}
+
+
+/*  public functions  */
+
+void
+gimp_image_transform (GimpImage              *image,
+                      GimpContext            *context,
+                      const GimpMatrix3      *matrix,
+                      GimpTransformDirection  direction,
+                      GimpInterpolationType   interpolation_type,
+                      GimpTransformResize     clip_result,
+                      GimpProgress           *progress)
+{
+  GimpObjectQueue *queue;
+  GimpItem        *item;
+  GimpMatrix3      transform;
+  GeglRectangle    old_bounds;
+  GeglRectangle    new_bounds;
+
+  g_return_if_fail (GIMP_IS_IMAGE (image));
+  g_return_if_fail (GIMP_IS_CONTEXT (context));
+  g_return_if_fail (matrix != NULL);
+  g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
+
+  gimp_set_busy (image->gimp);
+
+  old_bounds.x      = 0;
+  old_bounds.y      = 0;
+  old_bounds.width  = gimp_image_get_width  (image);
+  old_bounds.height = gimp_image_get_height (image);
+
+  transform = *matrix;
+
+  if (direction == GIMP_TRANSFORM_BACKWARD)
+    gimp_matrix3_invert (&transform);
+
+  gimp_transform_resize_boundary (&transform, clip_result,
+
+                                  old_bounds.x,
+                                  old_bounds.y,
+                                  old_bounds.x + old_bounds.width,
+                                  old_bounds.y + old_bounds.height,
+
+                                  &new_bounds.x,
+                                  &new_bounds.y,
+                                  &new_bounds.width,
+                                  &new_bounds.height);
+
+  new_bounds.width  -= new_bounds.x;
+  new_bounds.height -= new_bounds.y;
+
+  gimp_matrix3_translate (&transform,
+                          old_bounds.x - new_bounds.x,
+                          old_bounds.y - new_bounds.y);
+
+  queue    = gimp_object_queue_new (progress);
+  progress = GIMP_PROGRESS (queue);
+
+  gimp_object_queue_push_container (queue, gimp_image_get_layers (image));
+  gimp_object_queue_push (queue, gimp_image_get_mask (image));
+  gimp_object_queue_push_container (queue, gimp_image_get_channels (image));
+  gimp_object_queue_push_container (queue, gimp_image_get_vectors (image));
+
+  g_object_freeze_notify (G_OBJECT (image));
+
+  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_TRANSFORM, NULL);
+
+  /*  Transform all layers, channels (including selection mask), and vectors  */
+  while ((item = gimp_object_queue_pop (queue)))
+    {
+      GimpTransformResize clip = GIMP_TRANSFORM_RESIZE_ADJUST;
+
+      if (GIMP_IS_CHANNEL (item))
+        clip = clip_result;
+
+      gimp_item_transform (item,
+                           context,
+                           &transform, direction,
+                           interpolation_type, clip,
+                           progress);
+
+      if (GIMP_IS_VECTORS (item))
+        gimp_item_set_size (item, new_bounds.width, new_bounds.height);
+    }
+
+  /*  Resize the image (if needed)  */
+  if (! gegl_rectangle_equal (&new_bounds, &old_bounds))
+    {
+      gimp_image_undo_push_image_size (image,
+                                       NULL,
+                                       new_bounds.x,
+                                       new_bounds.y,
+                                       new_bounds.width,
+                                       new_bounds.height);
+
+      g_object_set (image,
+                    "width",  new_bounds.width,
+                    "height", new_bounds.height,
+                    NULL);
+    }
+
+  /*  Transform all Guides  */
+  gimp_image_transform_guides (image, &transform, &old_bounds);
+
+  /*  Transform all sample points  */
+  gimp_image_transform_sample_points (image, &transform, &old_bounds);
+
+  gimp_image_undo_group_end (image);
+
+  g_object_unref (queue);
+
+  if (! gegl_rectangle_equal (&new_bounds, &old_bounds))
+    {
+      gimp_image_size_changed_detailed (image,
+                                        old_bounds.x - new_bounds.x,
+                                        old_bounds.y - new_bounds.y,
+                                        old_bounds.width,
+                                        old_bounds.height);
+    }
+
+  g_object_thaw_notify (G_OBJECT (image));
+
+  gimp_unset_busy (image->gimp);
+}
diff --git a/app/core/gimpimage-transform.h b/app/core/gimpimage-transform.h
new file mode 100644
index 0000000000..63a851dff9
--- /dev/null
+++ b/app/core/gimpimage-transform.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattisbvf
+ *
+ * gimpimage-transform.h
+ * Copyright (C) 2019 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_IMAGE_TRANSFORM_H__
+#define __GIMP_IMAGE_TRANSFORM_H__
+
+
+void   gimp_image_transform (GimpImage              *image,
+                             GimpContext            *context,
+                             const GimpMatrix3      *matrix,
+                             GimpTransformDirection  direction,
+                             GimpInterpolationType   interpolation_type,
+                             GimpTransformResize     clip_result,
+                             GimpProgress           *progress);
+
+
+#endif /* __GIMP_IMAGE_TRANSFORM_H__ */
diff --git a/app/core/gimpimage-undo.c b/app/core/gimpimage-undo.c
index 8f3ea4b906..fbf808b650 100644
--- a/app/core/gimpimage-undo.c
+++ b/app/core/gimpimage-undo.c
@@ -614,6 +614,7 @@ gimp_image_undo_dirty_from_type (GimpUndoType undo_type)
     case GIMP_UNDO_GROUP_IMAGE_RESIZE:
     case GIMP_UNDO_GROUP_IMAGE_FLIP:
     case GIMP_UNDO_GROUP_IMAGE_ROTATE:
+    case GIMP_UNDO_GROUP_IMAGE_TRANSFORM:
     case GIMP_UNDO_GROUP_IMAGE_CROP:
       return GIMP_DIRTY_IMAGE | GIMP_DIRTY_IMAGE_SIZE;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]