[gimp/gimp-2-10] app: confirm transforms that result in very large items



commit 6a8ece0fd5987df1483c84fff51ac9a005342e9f
Author: Ell <ell_se yahoo com>
Date:   Sun Sep 23 12:24:50 2018 -0400

    app: confirm transforms that result in very large items
    
    In GimpTransformTools, precalculate the resulting size of the
    transformed item(s), and request confirmation if the size grows to
    over 10 times the size of the image (in either dimension).  This
    protects against transformations that can result in suprprisingly
    large items, such as inverted transformations, and, specifically,
    perspective-correction transformations performed using the measure
    tool, which will be added in the following commits.
    
    (cherry picked from commit 20a6a3583b435b237cfaf36c178419db3ce9296d)

 app/tools/gimptransformtool.c | 181 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 181 insertions(+)
---
diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c
index cf8cb328b7..d89ea23f7c 100644
--- a/app/tools/gimptransformtool.c
+++ b/app/tools/gimptransformtool.c
@@ -21,6 +21,7 @@
 #include <gtk/gtk.h>
 
 #include "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
 
 #include "tools-types.h"
 
@@ -28,16 +29,21 @@
 #include "core/gimpdrawable-transform.h"
 #include "core/gimperror.h"
 #include "core/gimpimage.h"
+#include "core/gimpimage-item-list.h"
 #include "core/gimpimage-undo.h"
 #include "core/gimpitem-linked.h"
 #include "core/gimplayer.h"
 #include "core/gimplayermask.h"
 #include "core/gimpprogress.h"
+#include "core/gimp-transform-resize.h"
 
 #include "vectors/gimpvectors.h"
 
 #include "display/gimpdisplay.h"
 
+#include "widgets/gimpmessagedialog.h"
+#include "widgets/gimpmessagebox.h"
+
 #include "gimptoolcontrol.h"
 #include "gimptransformoptions.h"
 #include "gimptransformtool.h"
@@ -45,6 +51,14 @@
 #include "gimp-intl.h"
 
 
+/* the minimal ratio between the transformed item size and the image size,
+ * above which confirmation is required.
+ */
+#define MIN_CONFIRMATION_RATIO 10
+
+
+/*  local function prototypes  */
+
 static GeglBuffer * gimp_transform_tool_real_transform (GimpTransformTool  *tr_tool,
                                                         GimpItem           *item,
                                                         GeglBuffer         *orig_buffer,
@@ -54,12 +68,18 @@ static GeglBuffer * gimp_transform_tool_real_transform (GimpTransformTool  *tr_t
                                                         gint               *new_offset_x,
                                                         gint               *new_offset_y);
 
+static gboolean     gimp_transform_tool_confirm        (GimpTransformTool  *tr_tool,
+                                                        GimpDisplay        *display);
+
 
 G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
 
 #define parent_class gimp_transform_tool_parent_class
 
 
+/*  private functions  */
+
+
 static void
 gimp_transform_tool_class_init (GimpTransformToolClass *klass)
 {
@@ -160,6 +180,164 @@ gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
   return ret;
 }
 
+static gboolean
+gimp_transform_tool_confirm (GimpTransformTool *tr_tool,
+                             GimpDisplay       *display)
+{
+  GimpTransformOptions *options   = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+  GimpDisplayShell     *shell     = gimp_display_get_shell (display);
+  GimpImage            *image     = gimp_display_get_image (display);
+  GimpItem             *active_item;
+  gdouble               max_ratio = 0.0;
+
+  active_item = gimp_transform_tool_get_active_item (tr_tool, display);
+
+  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix)
+    {
+      GimpMatrix3    transform;
+      GeglRectangle  selection_bounds;
+      gboolean       selection_empty = TRUE;
+      GList         *items;
+      GList         *iter;
+
+      transform = tr_tool->transform;
+
+      if (options->direction == GIMP_TRANSFORM_BACKWARD)
+        gimp_matrix3_invert (&transform);
+
+      if (options->type == GIMP_TRANSFORM_TYPE_LAYER &&
+          ! gimp_viewable_get_children (GIMP_VIEWABLE (active_item)))
+        {
+          selection_empty = ! gimp_item_bounds (
+            GIMP_ITEM (gimp_image_get_mask (image)),
+            &selection_bounds.x,     &selection_bounds.y,
+            &selection_bounds.width, &selection_bounds.height);
+        }
+
+      if (selection_empty && gimp_item_get_linked (active_item))
+        {
+          items = gimp_image_item_list_get_list (image,
+                                                 GIMP_ITEM_TYPE_ALL,
+                                                 GIMP_ITEM_SET_LINKED);
+        }
+      else
+        {
+          items = g_list_append (NULL, active_item);
+        }
+
+      for (iter = items; iter; iter = g_list_next (iter))
+        {
+          GimpItem            *item = iter->data;
+          GimpTransformResize  clip = options->clip;
+          GeglRectangle        orig_bounds;
+          GeglRectangle        new_bounds;
+
+          if (GIMP_IS_DRAWABLE (item))
+            {
+              if (selection_empty)
+                {
+                  gimp_item_get_offset (item, &orig_bounds.x, &orig_bounds.y);
+
+                  orig_bounds.width  = gimp_item_get_width  (item);
+                  orig_bounds.height = gimp_item_get_height (item);
+                }
+              else
+                {
+                  orig_bounds = selection_bounds;
+                }
+
+              clip = gimp_drawable_transform_get_effective_clip (
+                GIMP_DRAWABLE (item), NULL, clip);
+            }
+          else
+            {
+              gimp_item_bounds (item,
+                                &orig_bounds.x,     &orig_bounds.y,
+                                &orig_bounds.width, &orig_bounds.height);
+
+              clip = GIMP_TRANSFORM_RESIZE_ADJUST;
+            }
+
+          gimp_transform_resize_boundary (&transform, clip,
+
+                                          orig_bounds.x,
+                                          orig_bounds.y,
+                                          orig_bounds.x + orig_bounds.width,
+                                          orig_bounds.y + orig_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;
+
+          if (new_bounds.width > orig_bounds.width)
+            {
+              max_ratio = MAX (max_ratio,
+                               (gdouble) new_bounds.width /
+                               (gdouble) gimp_image_get_width (image));
+            }
+
+          if (new_bounds.height > orig_bounds.height)
+            {
+              max_ratio = MAX (max_ratio,
+                               (gdouble) new_bounds.height /
+                               (gdouble) gimp_image_get_height (image));
+            }
+        }
+
+      g_list_free (items);
+    }
+
+  if (max_ratio > MIN_CONFIRMATION_RATIO)
+    {
+      GtkWidget *dialog;
+      gint       response;
+
+      dialog = gimp_message_dialog_new (_("Confirm Transformation"),
+                                        GIMP_ICON_DIALOG_WARNING,
+                                        GTK_WIDGET (shell),
+                                        GTK_DIALOG_MODAL |
+                                        GTK_DIALOG_DESTROY_WITH_PARENT,
+                                        gimp_standard_help_func, NULL,
+
+                                        _("_Cancel"),    GTK_RESPONSE_CANCEL,
+                                        _("_Transform"), GTK_RESPONSE_OK,
+
+                                        NULL);
+
+      gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+                                               GTK_RESPONSE_OK,
+                                               GTK_RESPONSE_CANCEL,
+                                               -1);
+
+      gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box,
+                                         _("Transformation creates "
+                                           "a very large item."));
+
+      gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box,
+                                 _("Applying the transformation will result "
+                                   "in an item that is over %g times larger "
+                                   "than the image."),
+                                   floor (max_ratio));
+
+      response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+      gtk_widget_destroy (dialog);
+
+      if (response != GTK_RESPONSE_OK)
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+/*  public functions  */
+
+
 gboolean
 gimp_transform_tool_bounds (GimpTransformTool *tr_tool,
                             GimpDisplay       *display)
@@ -447,6 +625,9 @@ gimp_transform_tool_transform (GimpTransformTool *tr_tool,
       return TRUE;
     }
 
+  if (! gimp_transform_tool_confirm (tr_tool, display))
+    return FALSE;
+
   gimp_set_busy (display->gimp);
 
   /*  We're going to dirty this image, but we want to keep the tool around  */


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