[gimp] app: add "Composited preview" option to transform tools



commit a33582789651e038b816df24d289825e2e246b5e
Author: Ell <ell_se yahoo com>
Date:   Thu Jan 16 00:17:56 2020 +0200

    app: add "Composited preview" option to transform tools
    
    Add a "Composited preview" option to all transform-grid tools,
    which displays the transform preview as part of the image
    composition (i.e., as it would actually look when applying the
    transformation, keeping the layer at the right position in the
    stack, and with the right layer mode), instead of using an overlay.
    
    This option is off by default, since it's generally slower to
    render than an overlay, due to the lack of mipmap rendering.  We're
    also still using an overlay when transfoming a selection, and not a
    whole layer.

 app/tools/gimptransformgridoptions.c |  47 +++-
 app/tools/gimptransformgridoptions.h |   1 +
 app/tools/gimptransformgridtool.c    | 431 ++++++++++++++++++++++++++++++++---
 app/tools/gimptransformgridtool.h    |  45 ++--
 4 files changed, 455 insertions(+), 69 deletions(-)
---
diff --git a/app/tools/gimptransformgridoptions.c b/app/tools/gimptransformgridoptions.c
index 09e00699a4..bb4ce94c86 100644
--- a/app/tools/gimptransformgridoptions.c
+++ b/app/tools/gimptransformgridoptions.c
@@ -50,6 +50,7 @@ enum
   PROP_DIRECTION,
   PROP_DIRECTION_LINKED,
   PROP_SHOW_PREVIEW,
+  PROP_COMPOSITED_PREVIEW,
   PROP_PREVIEW_OPACITY,
   PROP_GRID_TYPE,
   PROP_GRID_SIZE,
@@ -111,6 +112,13 @@ gimp_transform_grid_options_class_init (GimpTransformGridOptionsClass *klass)
                             TRUE,
                             GIMP_PARAM_STATIC_STRINGS);
 
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_COMPOSITED_PREVIEW,
+                            "composited-preview",
+                            _("Composited preview"),
+                            _("Show preview as part of the image composition"),
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PREVIEW_OPACITY,
                            "preview-opacity",
                            _("Image opacity"),
@@ -228,6 +236,9 @@ gimp_transform_grid_options_set_property (GObject      *object,
     case PROP_SHOW_PREVIEW:
       options->show_preview = g_value_get_boolean (value);
       break;
+    case PROP_COMPOSITED_PREVIEW:
+      options->composited_preview = g_value_get_boolean (value);
+      break;
     case PROP_PREVIEW_OPACITY:
       options->preview_opacity = g_value_get_double (value);
       break;
@@ -293,6 +304,9 @@ gimp_transform_grid_options_get_property (GObject    *object,
     case PROP_SHOW_PREVIEW:
       g_value_set_boolean (value, options->show_preview);
       break;
+    case PROP_COMPOSITED_PREVIEW:
+      g_value_set_boolean (value, options->composited_preview);
+      break;
     case PROP_PREVIEW_OPACITY:
       g_value_set_double (value, options->preview_opacity);
       break;
@@ -352,6 +366,8 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
   GObject                    *config = G_OBJECT (tool_options);
   GimpTransformGridToolClass *tg_class;
   GtkWidget                  *vbox;
+  GtkWidget                  *vbox2;
+  GtkWidget                  *button;
   GtkWidget                  *frame;
   GtkWidget                  *combo;
   GtkWidget                  *scale;
@@ -367,9 +383,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
   if (tg_class->matrix_to_info)
     {
       GimpTransformOptions *tr_options = GIMP_TRANSFORM_OPTIONS (tool_options);
-      GtkWidget            *vbox2;
       GtkWidget            *hbox;
-      GtkWidget            *button;
 
       vbox2 = gtk_bin_get_child (GTK_BIN (tr_options->direction_frame));
       g_object_ref (vbox2);
@@ -397,12 +411,25 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
   g_type_class_unref (tg_class);
 
   /*  the preview frame  */
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+  button = gimp_prop_check_button_new (config, "composited-preview", NULL);
+  gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
   scale = gimp_prop_spin_scale_new (config, "preview-opacity", NULL,
                                     0.01, 0.1, 0);
   gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
+  gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
+  gtk_widget_show (scale);
+
+  g_object_bind_property (config, "composited-preview",
+                          scale,  "sensitive",
+                          G_BINDING_SYNC_CREATE |
+                          G_BINDING_INVERT_BOOLEAN);
 
   frame = gimp_prop_expanding_frame_new (config, "show-preview", NULL,
-                                         scale, NULL);
+                                         vbox2, NULL);
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
 
   /*  the guides frame  */
@@ -431,8 +458,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
 
   if (tool_options->tool_info->tool_type == GIMP_TYPE_ROTATE_TOOL)
     {
-      GtkWidget *button;
-      gchar     *label;
+      gchar *label;
 
       label = g_strdup_printf (_("15 degrees (%s)"),
                                gimp_get_mod_string (extend_mask));
@@ -447,8 +473,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
     }
   else if (tool_options->tool_info->tool_type == GIMP_TYPE_SCALE_TOOL)
     {
-      GtkWidget *button;
-      gchar     *label;
+      gchar *label;
 
       label = g_strdup_printf (_("Keep aspect (%s)"),
                                gimp_get_mod_string (extend_mask));
@@ -474,8 +499,7 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
     }
   else if (tool_options->tool_info->tool_type == GIMP_TYPE_PERSPECTIVE_TOOL)
     {
-      GtkWidget *button;
-      gchar     *label;
+      gchar *label;
 
       label = g_strdup_printf (_("Constrain handles (%s)"),
                                gimp_get_mod_string (extend_mask));
@@ -541,9 +565,8 @@ gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
           N_("Lock pivot position to canvas") },
       };
 
-      GtkWidget *button;
-      gchar     *label;
-      gint       i;
+      gchar *label;
+      gint   i;
 
       frame = NULL;
 
diff --git a/app/tools/gimptransformgridoptions.h b/app/tools/gimptransformgridoptions.h
index 91d46bde09..d7c2df79ee 100644
--- a/app/tools/gimptransformgridoptions.h
+++ b/app/tools/gimptransformgridoptions.h
@@ -39,6 +39,7 @@ struct _GimpTransformGridOptions
 
   gboolean              direction_linked;
   gboolean              show_preview;
+  gboolean              composited_preview;
   gdouble               preview_opacity;
   GimpGuidesType        grid_type;
   gint                  grid_size;
diff --git a/app/tools/gimptransformgridtool.c b/app/tools/gimptransformgridtool.c
index 78369e5ae7..0c283a99f0 100644
--- a/app/tools/gimptransformgridtool.c
+++ b/app/tools/gimptransformgridtool.c
@@ -26,9 +26,19 @@
 
 #include "tools-types.h"
 
+#include "gegl/gimpapplicator.h"
+#include "gegl/gimp-gegl-nodes.h"
+#include "gegl/gimp-gegl-utils.h"
+
 #include "core/gimp.h"
+#include "core/gimp-transform-resize.h"
+#include "core/gimp-transform-utils.h"
 #include "core/gimpboundary.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpdrawablefilter.h"
 #include "core/gimperror.h"
+#include "core/gimpfilter.h"
+#include "core/gimpgrouplayer.h"
 #include "core/gimpimage.h"
 #include "core/gimpimage-undo.h"
 #include "core/gimpimage-undo-push.h"
@@ -68,6 +78,22 @@
 #define UNDO_COMPRESS_TIME (0.5 * G_TIME_SPAN_SECOND)
 
 
+typedef struct
+{
+  GimpTransformGridTool *tg_tool;
+
+  GimpDrawable          *drawable;
+  GimpDrawableFilter    *filter;
+
+  GimpDrawable          *clip_drawable;
+
+  GeglNode              *transform_node;
+  GeglNode              *crop_node;
+
+  GimpMatrix3            transform;
+  GeglRectangle          bounds;
+} Filter;
+
 typedef struct
 {
   gint64                 time;
@@ -155,6 +181,9 @@ static void      gimp_transform_grid_tool_widget_response    (GimpToolWidget
                                                               gint                    response_id,
                                                               GimpTransformGridTool  *tg_tool);
 
+static void      gimp_transform_grid_tool_filter_flush       (GimpDrawableFilter     *filter,
+                                                              GimpTransformGridTool  *tg_tool);
+
 static void      gimp_transform_grid_tool_halt               (GimpTransformGridTool  *tg_tool);
 static void      gimp_transform_grid_tool_commit             (GimpTransformGridTool  *tg_tool);
 
@@ -169,12 +198,27 @@ static void      gimp_transform_grid_tool_response           (GimpToolGui
                                                               gint                    response_id,
                                                               GimpTransformGridTool  *tg_tool);
 
+static gboolean  gimp_transform_grid_tool_composited_preview (GimpTransformGridTool  *tg_tool);
 static void      gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool  *tg_tool);
 static void      gimp_transform_grid_tool_update_preview     (GimpTransformGridTool  *tg_tool);
 static void      gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool  *tg_tool,
                                                               GimpObject             *object);
 static void      gimp_transform_grid_tool_show_active_object (GimpTransformGridTool  *tg_tool);
 
+static void      gimp_transform_grid_tool_add_filter         (GimpDrawable           *drawable,
+                                                              GimpTransformGridTool  *tg_tool);
+static void      gimp_transform_grid_tool_remove_filter      (GimpDrawable           *drawable,
+                                                              GimpTransformGridTool  *tg_tool);
+
+static void      gimp_transform_grid_tool_effective_mode_changed
+                                                             (GimpLayer              *layer,
+                                                              GimpTransformGridTool  *tg_tool);
+
+static Filter  * filter_new                                  (GimpTransformGridTool  *tg_tool,
+                                                              GimpDrawable           *drawable,
+                                                              gboolean                add_filter);
+static void      filter_free                                 (Filter                 *filter);
+
 static UndoInfo * undo_info_new  (void);
 static void       undo_info_free (UndoInfo *info);
 
@@ -242,9 +286,10 @@ gimp_transform_grid_tool_init (GimpTransformGridTool *tg_tool)
   gimp_tool_control_set_scroll_lock      (tool->control, TRUE);
   gimp_tool_control_set_preserve         (tool->control, FALSE);
   gimp_tool_control_set_dirty_mask       (tool->control,
-                                          GIMP_DIRTY_IMAGE_SIZE |
-                                          GIMP_DIRTY_DRAWABLE   |
-                                          GIMP_DIRTY_SELECTION  |
+                                          GIMP_DIRTY_IMAGE_SIZE      |
+                                          GIMP_DIRTY_IMAGE_STRUCTURE |
+                                          GIMP_DIRTY_DRAWABLE        |
+                                          GIMP_DIRTY_SELECTION       |
                                           GIMP_DIRTY_ACTIVE_DRAWABLE);
   gimp_tool_control_set_active_modifiers (tool->control,
                                           GIMP_TOOL_ACTIVE_MODIFIERS_SAME);
@@ -618,32 +663,35 @@ gimp_transform_grid_tool_options_notify (GimpTool         *tool,
       /*  recalculate the tool's transformation matrix  */
       gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
     }
-  else if (! strcmp (pspec->name, "show-preview"))
+  else if (! strcmp (pspec->name, "show-preview") ||
+           ! strcmp (pspec->name, "composited-preview"))
     {
       if (tg_tool->preview)
         {
           GimpDisplay *display;
           GimpObject  *object;
-          gboolean     show_preview;
-
-          show_preview = gimp_transform_grid_options_show_preview (tg_options) &&
-                         tr_tool->transform_valid;
-
-          gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
 
           display = tool->display;
           object  = gimp_transform_tool_get_active_object (tr_tool, display);
 
           if (object)
             {
-              if (gimp_transform_grid_options_show_preview (tg_options))
-                gimp_transform_grid_tool_hide_active_object (tg_tool, object);
+              if (tg_options->show_preview &&
+                  ! gimp_transform_grid_tool_composited_preview (tg_tool))
+                {
+                  gimp_transform_grid_tool_hide_active_object (tg_tool, object);
+                }
               else
-                gimp_transform_grid_tool_show_active_object (tg_tool);
+                {
+                  gimp_transform_grid_tool_show_active_object (tg_tool);
+                }
             }
+
+          gimp_transform_grid_tool_update_preview (tg_tool);
         }
     }
-  else if (! strcmp (pspec->name, "clip") ||
+  else if (! strcmp (pspec->name, "interpolation") ||
+           ! strcmp (pspec->name, "clip")          ||
            ! strcmp (pspec->name, "preview-opacity"))
     {
       gimp_transform_grid_tool_update_preview (tg_tool);
@@ -673,10 +721,10 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
   if (tr_options->direction == GIMP_TRANSFORM_BACKWARD)
     gimp_matrix3_invert (&matrix);
 
-  if (tg_tool->widget)
+  if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER ||
+      tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
     {
       GimpPickable *pickable;
-      gboolean      show_preview;
 
       if (tr_options->type == GIMP_TRANSFORM_TYPE_IMAGE)
         {
@@ -690,9 +738,6 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
           pickable = GIMP_PICKABLE (tool->drawable);
         }
 
-      show_preview = gimp_transform_grid_options_show_preview (options) &&
-                     tr_tool->transform_valid;
-
       tg_tool->preview =
         gimp_draw_tool_add_transform_preview (draw_tool,
                                               pickable,
@@ -703,10 +748,6 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
                                               tr_tool->y2);
       g_object_add_weak_pointer (G_OBJECT (tg_tool->preview),
                                  (gpointer) &tg_tool->preview);
-
-      gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
-
-      GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
     }
 
   if (tr_options->type == GIMP_TRANSFORM_TYPE_SELECTION)
@@ -786,6 +827,8 @@ gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
         }
     }
 
+  GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
+
   gimp_transform_grid_tool_update_preview (tg_tool);
 }
 
@@ -1058,6 +1101,16 @@ gimp_transform_grid_tool_widget_response (GimpToolWidget        *widget,
     }
 }
 
+static void
+gimp_transform_grid_tool_filter_flush (GimpDrawableFilter    *filter,
+                                       GimpTransformGridTool *tg_tool)
+{
+  GimpTool  *tool = GIMP_TOOL (tg_tool);
+  GimpImage *image = gimp_display_get_image (tool->display);
+
+  gimp_projection_flush (gimp_image_get_projection (image));
+}
+
 static void
 gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
 {
@@ -1070,6 +1123,8 @@ gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
   gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tg_tool), NULL);
   g_clear_object (&tg_tool->widget);
 
+  g_clear_pointer (&tg_tool->filters, g_hash_table_unref);
+
   if (tg_tool->gui)
     gimp_tool_gui_hide (tg_tool->gui);
 
@@ -1405,6 +1460,19 @@ gimp_transform_grid_tool_response (GimpToolGui           *gui,
     }
 }
 
+static gboolean
+gimp_transform_grid_tool_composited_preview (GimpTransformGridTool *tg_tool)
+{
+  GimpTool                 *tool       = GIMP_TOOL (tg_tool);
+  GimpTransformOptions     *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
+  GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  GimpImage                *image      = gimp_display_get_image (tool->display);
+
+  return tg_options->composited_preview                &&
+         tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
+         gimp_channel_is_empty (gimp_image_get_mask (image));
+}
+
 static void
 gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool)
 {
@@ -1432,26 +1500,127 @@ gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool)
 static void
 gimp_transform_grid_tool_update_preview (GimpTransformGridTool *tg_tool)
 {
+  GimpTool                 *tool       = GIMP_TOOL (tg_tool);
   GimpTransformTool        *tr_tool    = GIMP_TRANSFORM_TOOL (tg_tool);
   GimpTransformOptions     *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
   GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
   gint                      i;
 
-  if (tg_tool->preview)
+  if (tg_options->show_preview                              &&
+      gimp_transform_grid_tool_composited_preview (tg_tool) &&
+      tr_tool->transform_valid)
     {
-      gboolean show_preview;
+      GHashTableIter  iter;
+      GimpDrawable   *drawable;
+      Filter         *filter;
 
-      show_preview = gimp_transform_grid_options_show_preview (tg_options) &&
-                     tr_tool->transform_valid;
+      if (! tg_tool->filters)
+        {
+          tg_tool->filters = g_hash_table_new_full (
+            g_direct_hash, g_direct_equal,
+            NULL, (GDestroyNotify) filter_free);
 
-      gimp_canvas_item_begin_change (tg_tool->preview);
-      gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
-      g_object_set (tg_tool->preview,
-                    "transform", &tr_tool->transform,
-                    "clip",      tr_options->clip,
-                    "opacity",   tg_options->preview_opacity,
-                    NULL);
-      gimp_canvas_item_end_change (tg_tool->preview);
+          gimp_transform_grid_tool_add_filter (tool->drawable, tg_tool);
+        }
+
+      g_hash_table_iter_init (&iter, tg_tool->filters);
+
+      while (g_hash_table_iter_next (&iter,
+                                     (gpointer *) &drawable,
+                                     (gpointer *) &filter))
+        {
+          GimpMatrix3   transform;
+          GeglRectangle bounds;
+          gint          offset_x;
+          gint          offset_y;
+          gint          x1, y1;
+          gint          x2, y2;
+          gboolean      update = FALSE;
+
+          if (! filter->filter)
+            continue;
+
+          gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
+
+          gimp_matrix3_identity (&transform);
+          gimp_matrix3_translate (&transform, +offset_x, +offset_y);
+          gimp_matrix3_mult (&tr_tool->transform, &transform);
+          gimp_matrix3_translate (&transform, -offset_x, -offset_y);
+
+          gimp_transform_resize_boundary (&tr_tool->transform,
+                                          gimp_item_get_clip (
+                                            GIMP_ITEM (filter->clip_drawable),
+                                            tr_options->clip),
+                                          tr_tool->x1, tr_tool->y1,
+                                          tr_tool->x2, tr_tool->y2,
+                                          &x1,         &y1,
+                                          &x2,         &y2);
+
+          bounds.x      = x1 - offset_x;
+          bounds.y      = y1 - offset_y;
+          bounds.width  = x2 - x1;
+          bounds.height = y2 - y1;
+
+          if (! gimp_matrix3_equal (&transform, &filter->transform))
+            {
+              filter->transform = transform;
+
+              gimp_gegl_node_set_matrix (filter->transform_node, &transform);
+
+              update = TRUE;
+            }
+
+          if (! gegl_rectangle_equal (&bounds, &filter->bounds))
+            {
+              filter->bounds = bounds;
+
+              gegl_node_set (filter->crop_node,
+                             "x",      (gdouble) bounds.x,
+                             "y",      (gdouble) bounds.y,
+                             "width",  (gdouble) bounds.width,
+                             "height", (gdouble) bounds.height,
+                             NULL);
+
+              update = TRUE;
+            }
+
+          if (GIMP_IS_LAYER (drawable))
+            {
+              gimp_drawable_filter_set_add_alpha (
+                filter->filter,
+                tr_options->interpolation != GIMP_INTERPOLATION_NONE);
+            }
+
+          if (update)
+            gimp_drawable_filter_apply (filter->filter, NULL);
+        }
+    }
+  else
+    {
+      g_clear_pointer (&tg_tool->filters, g_hash_table_unref);
+    }
+
+  if (tg_tool->preview)
+    {
+      if (tg_options->show_preview                                &&
+          ! gimp_transform_grid_tool_composited_preview (tg_tool) &&
+          tr_tool->transform_valid)
+        {
+          gimp_canvas_item_begin_change (tg_tool->preview);
+          gimp_canvas_item_set_visible (tg_tool->preview, TRUE);
+          g_object_set (
+            tg_tool->preview,
+            "transform", &tr_tool->transform,
+            "clip",      gimp_item_get_clip (GIMP_ITEM (tool->drawable),
+                                             tr_options->clip),
+            "opacity",   tg_options->preview_opacity,
+            NULL);
+          gimp_canvas_item_end_change (tg_tool->preview);
+        }
+      else
+        {
+          gimp_canvas_item_set_visible (tg_tool->preview, FALSE);
+        }
     }
 
   if (tg_tool->boundary_in)
@@ -1502,6 +1671,7 @@ gimp_transform_grid_tool_hide_active_object (GimpTransformGridTool *tg_tool,
     {
       /*  hide only complete layers and channels, not layer masks  */
       if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
+          ! options->composited_preview                 &&
           GIMP_IS_DRAWABLE (object)                     &&
           ! GIMP_IS_LAYER_MASK (object)                 &&
           gimp_item_get_visible (GIMP_ITEM (object))    &&
@@ -1550,6 +1720,197 @@ gimp_transform_grid_tool_show_active_object (GimpTransformGridTool *tg_tool)
     }
 }
 
+static void
+gimp_transform_grid_tool_add_filter (GimpDrawable          *drawable,
+                                     GimpTransformGridTool *tg_tool)
+{
+  Filter        *filter;
+  GimpLayerMode  mode = GIMP_LAYER_MODE_NORMAL;
+
+  if (GIMP_IS_LAYER (drawable))
+    {
+      gimp_layer_get_effective_mode (GIMP_LAYER (drawable),
+                                     &mode, NULL, NULL, NULL);
+    }
+
+  if (mode != GIMP_LAYER_MODE_PASS_THROUGH)
+    {
+      filter = filter_new (tg_tool, drawable, TRUE);
+    }
+  else
+    {
+      GimpContainer *container;
+
+      filter = filter_new (tg_tool, drawable, FALSE);
+
+      container = gimp_viewable_get_children (GIMP_VIEWABLE (drawable));
+
+      gimp_container_foreach (container,
+                              (GFunc) gimp_transform_grid_tool_add_filter,
+                              tg_tool);
+    }
+
+  filter->clip_drawable = drawable;
+
+  g_hash_table_insert (tg_tool->filters, drawable, filter);
+
+  if (GIMP_IS_LAYER (drawable))
+    {
+      GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
+
+      if (mask)
+        {
+          Filter *mask_filter;
+
+          gimp_transform_grid_tool_add_filter (GIMP_DRAWABLE (mask), tg_tool);
+
+          mask_filter = g_hash_table_lookup (tg_tool->filters, mask);
+
+          mask_filter->clip_drawable = drawable;
+        }
+    }
+}
+
+static void
+gimp_transform_grid_tool_remove_filter (GimpDrawable          *drawable,
+                                        GimpTransformGridTool *tg_tool)
+{
+  Filter *filter = g_hash_table_lookup (tg_tool->filters, drawable);
+
+  if (GIMP_IS_LAYER (drawable))
+    {
+      GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
+
+      if (mask)
+        gimp_transform_grid_tool_remove_filter (GIMP_DRAWABLE (mask), tg_tool);
+    }
+
+  if (! filter->filter)
+    {
+      GimpContainer *container;
+
+      container = gimp_viewable_get_children (GIMP_VIEWABLE (drawable));
+
+      gimp_container_foreach (container,
+                              (GFunc) gimp_transform_grid_tool_remove_filter,
+                              tg_tool);
+    }
+
+  g_hash_table_remove (tg_tool->filters, drawable);
+}
+
+static void
+gimp_transform_grid_tool_effective_mode_changed (GimpLayer             *layer,
+                                                 GimpTransformGridTool *tg_tool)
+{
+  Filter        *filter = g_hash_table_lookup (tg_tool->filters, layer);
+  GimpLayerMode  mode;
+  gboolean       old_pass_through;
+  gboolean       new_pass_through;
+
+  gimp_layer_get_effective_mode (layer, &mode, NULL, NULL, NULL);
+
+  old_pass_through = ! filter->filter;
+  new_pass_through = mode == GIMP_LAYER_MODE_PASS_THROUGH;
+
+  if (old_pass_through != new_pass_through)
+    {
+      gimp_transform_grid_tool_remove_filter (GIMP_DRAWABLE (layer), tg_tool);
+      gimp_transform_grid_tool_add_filter    (GIMP_DRAWABLE (layer), tg_tool);
+
+      gimp_transform_grid_tool_update_preview (tg_tool);
+    }
+}
+
+static Filter *
+filter_new (GimpTransformGridTool *tg_tool,
+            GimpDrawable          *drawable,
+            gboolean               add_filter)
+{
+  Filter   *filter = g_slice_new0 (Filter);
+  GeglNode *node;
+  GeglNode *input_node;
+  GeglNode *output_node;
+
+  filter->tg_tool  = tg_tool;
+  filter->drawable = drawable;
+
+  if (add_filter)
+    {
+      node = gegl_node_new ();
+
+      input_node  = gegl_node_get_input_proxy (node, "input");
+      output_node = gegl_node_get_input_proxy (node, "output");
+
+      filter->transform_node = gegl_node_new_child (
+        node,
+        "operation", "gegl:transform",
+        "near-z",    GIMP_TRANSFORM_NEAR_Z,
+        "sampler",   GEGL_SAMPLER_NEAREST,
+        NULL);
+
+      filter->crop_node = gegl_node_new_child (
+        node,
+        "operation", "gegl:crop",
+        NULL);
+
+      gegl_node_link_many (input_node,
+                           filter->transform_node,
+                           filter->crop_node,
+                           output_node,
+                           NULL);
+
+      gimp_gegl_node_set_underlying_operation (node, filter->transform_node);
+
+      filter->filter = gimp_drawable_filter_new (
+        drawable,
+        GIMP_TRANSFORM_TOOL_GET_CLASS (tg_tool)->undo_desc,
+        node,
+        gimp_tool_get_icon_name (GIMP_TOOL (tg_tool)));
+
+      gimp_drawable_filter_set_clip (filter->filter, FALSE);
+      gimp_drawable_filter_set_override_constraints (filter->filter, TRUE);
+
+      g_signal_connect (
+        filter->filter, "flush",
+        G_CALLBACK (gimp_transform_grid_tool_filter_flush),
+        tg_tool);
+
+      g_object_unref (node);
+    }
+
+  if (GIMP_IS_GROUP_LAYER (drawable))
+    {
+      g_signal_connect (
+        drawable, "effective-mode-changed",
+        G_CALLBACK (gimp_transform_grid_tool_effective_mode_changed),
+        tg_tool);
+    }
+
+  return filter;
+}
+
+static void
+filter_free (Filter *filter)
+{
+  if (filter->filter)
+    {
+      gimp_drawable_filter_abort (filter->filter);
+
+      g_object_unref (filter->filter);
+    }
+
+  if (GIMP_IS_GROUP_LAYER (filter->drawable))
+    {
+      g_signal_handlers_disconnect_by_func (
+        filter->drawable,
+        gimp_transform_grid_tool_effective_mode_changed,
+        filter->tg_tool);
+    }
+
+  g_slice_free (Filter, filter);
+}
+
 static UndoInfo *
 undo_info_new (void)
 {
diff --git a/app/tools/gimptransformgridtool.h b/app/tools/gimptransformgridtool.h
index a91cef5a75..bc3ad1e69a 100644
--- a/app/tools/gimptransformgridtool.h
+++ b/app/tools/gimptransformgridtool.h
@@ -45,28 +45,29 @@ typedef struct _GimpTransformGridToolClass GimpTransformGridToolClass;
 
 struct _GimpTransformGridTool
 {
-  GimpTransformTool  parent_instance;
-
-  TransInfo          init_trans_info;  /*  initial transformation info           */
-  TransInfo          trans_infos[2];   /*  forward/backward transformation info  */
-  gdouble           *trans_info;       /*  current transformation info           */
-  GList             *undo_list;        /*  list of all states,
-                                           head is current == prev_trans_info,
-                                           tail is original == old_trans_info    */
-  GList             *redo_list;        /*  list of all undone states,
-                                           NULL when nothing undone */
-
-  GimpObject        *hidden_object;    /*  the object that was hidden during
-                                           the transform                         */
-
-  GimpToolWidget    *widget;
-  GimpToolWidget    *grab_widget;
-  GimpCanvasItem    *preview;
-  GimpCanvasItem    *boundary_in;
-  GimpCanvasItem    *boundary_out;
-  GPtrArray         *strokes;
-
-  GimpToolGui       *gui;
+  GimpTransformTool   parent_instance;
+
+  TransInfo           init_trans_info;  /*  initial transformation info           */
+  TransInfo           trans_infos[2];   /*  forward/backward transformation info  */
+  gdouble            *trans_info;       /*  current transformation info           */
+  GList              *undo_list;        /*  list of all states,
+                                            head is current == prev_trans_info,
+                                            tail is original == old_trans_info    */
+  GList              *redo_list;        /*  list of all undone states,
+                                            NULL when nothing undone */
+
+  GimpObject         *hidden_object;    /*  the object that was hidden during
+                                            the transform                         */
+
+  GimpToolWidget     *widget;
+  GimpToolWidget     *grab_widget;
+  GHashTable         *filters;
+  GimpCanvasItem     *preview;
+  GimpCanvasItem     *boundary_in;
+  GimpCanvasItem     *boundary_out;
+  GPtrArray          *strokes;
+
+  GimpToolGui        *gui;
 };
 
 struct _GimpTransformGridToolClass


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