[gimp] app: implement creating an animation from a warp tool transform



commit dfcbc23caccab9e43718717f73115228f1ef2a0d
Author: Michael Natterer <mitch gimp org>
Date:   Sat Nov 8 00:47:39 2014 +0100

    app: implement creating an animation from a warp tool transform
    
    like in the iwarp plug-in. I consider the plug-in obsolete now.

 app/tools/gimpwarpoptions.c |   51 +++++++++++-
 app/tools/gimpwarpoptions.h |   15 +++-
 app/tools/gimpwarptool.c    |  185 ++++++++++++++++++++++++++++++++++++++----
 3 files changed, 223 insertions(+), 28 deletions(-)
---
diff --git a/app/tools/gimpwarpoptions.c b/app/tools/gimpwarpoptions.c
index df1d650..0dd278c 100644
--- a/app/tools/gimpwarpoptions.c
+++ b/app/tools/gimpwarpoptions.c
@@ -42,7 +42,8 @@ enum
   PROP_BEHAVIOR,
   PROP_EFFECT_STRENGTH,
   PROP_EFFECT_SIZE,
-  PROP_EFFECT_HARDNESS
+  PROP_EFFECT_HARDNESS,
+  PROP_N_ANIMATION_FRAMES
 };
 
 
@@ -94,6 +95,12 @@ gimp_warp_options_class_init (GimpWarpOptionsClass *klass)
                                    _("Effect Hardness"),
                                    0.0, 1.0, 0.5,
                                    GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_INSTALL_PROP_INT (object_class, PROP_N_ANIMATION_FRAMES,
+                                "n-animation-frames",
+                                _("Number of animation frames"),
+                                3, 1000, 10,
+                                GIMP_PARAM_STATIC_STRINGS);
 }
 
 static void
@@ -123,6 +130,9 @@ gimp_warp_options_set_property (GObject      *object,
     case PROP_EFFECT_HARDNESS:
       options->effect_hardness = g_value_get_double (value);
       break;
+    case PROP_N_ANIMATION_FRAMES:
+      options->n_animation_frames = g_value_get_int (value);
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -152,6 +162,9 @@ gimp_warp_options_get_property (GObject    *object,
     case PROP_EFFECT_HARDNESS:
       g_value_set_double (value, options->effect_hardness);
       break;
+    case PROP_N_ANIMATION_FRAMES:
+      g_value_set_int (value, options->n_animation_frames);
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -162,10 +175,13 @@ gimp_warp_options_get_property (GObject    *object,
 GtkWidget *
 gimp_warp_options_gui (GimpToolOptions *tool_options)
 {
-  GObject   *config = G_OBJECT (tool_options);
-  GtkWidget *vbox   = gimp_tool_options_gui (tool_options);
-  GtkWidget *combo;
-  GtkWidget *scale;
+  GimpWarpOptions *options = GIMP_WARP_OPTIONS (tool_options);
+  GObject         *config  = G_OBJECT (tool_options);
+  GtkWidget       *vbox    = gimp_tool_options_gui (tool_options);
+  GtkWidget       *frame;
+  GtkWidget       *anim_vbox;
+  GtkWidget       *combo;
+  GtkWidget       *scale;
 
   combo = gimp_prop_enum_combo_box_new (config, "behavior", 0, 0);
   g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
@@ -193,5 +209,30 @@ gimp_warp_options_gui (GimpToolOptions *tool_options)
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
+  /*  the animation frame  */
+  frame = gimp_frame_new (_("Animate"));
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  anim_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (frame), anim_vbox);
+  gtk_widget_show (anim_vbox);
+
+  scale = gimp_prop_spin_scale_new (config, "n-animation-frames",
+                                    _("Frames"),
+                                    0.01, 1.0, 2);
+  gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 3.0, 100.0);
+  gtk_box_pack_start (GTK_BOX (anim_vbox), scale, FALSE, FALSE, 0);
+  gtk_widget_show (scale);
+
+  options->animate_button = gtk_button_new_with_label (_("Create Animation"));
+  gtk_widget_set_sensitive (options->animate_button, FALSE);
+  gtk_box_pack_start (GTK_BOX (anim_vbox), options->animate_button,
+                      FALSE, FALSE, 0);
+  gtk_widget_show (options->animate_button);
+
+  g_object_add_weak_pointer (G_OBJECT (options->animate_button),
+                             (gpointer) &options->animate_button);
+
   return vbox;
 }
diff --git a/app/tools/gimpwarpoptions.h b/app/tools/gimpwarpoptions.h
index 19b9970..46b1fde 100644
--- a/app/tools/gimpwarpoptions.h
+++ b/app/tools/gimpwarpoptions.h
@@ -37,12 +37,17 @@ typedef struct _GimpWarpOptionsClass GimpWarpOptionsClass;
 
 struct _GimpWarpOptions
 {
-  GimpToolOptions  parent_instance;
+  GimpToolOptions   parent_instance;
 
-  GimpWarpBehavior behavior;
-  gdouble          effect_strength;
-  gdouble          effect_size;
-  gdouble          effect_hardness;
+  GimpWarpBehavior  behavior;
+  gdouble           effect_strength;
+  gdouble           effect_size;
+  gdouble           effect_hardness;
+
+  gint              n_animation_frames;
+
+  /*  options gui  */
+  GtkWidget        *animate_button;
 };
 
 struct _GimpWarpOptionsClass
diff --git a/app/tools/gimpwarptool.c b/app/tools/gimpwarptool.c
index 79efe15..8dc0c77 100644
--- a/app/tools/gimpwarptool.c
+++ b/app/tools/gimpwarptool.c
@@ -28,14 +28,19 @@
 
 #include "tools-types.h"
 
+#include "gegl/gimp-gegl-apply-operation.h"
+
 #include "core/gimp.h"
 #include "core/gimpchannel.h"
 #include "core/gimpimage.h"
 #include "core/gimpimagemap.h"
+#include "core/gimplayer.h"
 #include "core/gimpprogress.h"
 #include "core/gimpprojection.h"
+#include "core/gimpsubprogress.h"
 
 #include "widgets/gimphelp-ids.h"
+#include "widgets/gimpwidgets-utils.h"
 
 #include "display/gimpdisplay.h"
 
@@ -113,7 +118,11 @@ static void       gimp_warp_tool_stroke_changed     (GeglPath              *stro
 static void       gimp_warp_tool_image_map_flush    (GimpImageMap          *image_map,
                                                      GimpTool              *tool);
 static void       gimp_warp_tool_add_op             (GimpWarpTool          *wt,
-                                                     GeglNode              *new_op);
+                                                     GeglNode              *op);
+static void       gimp_warp_tool_remove_op          (GimpWarpTool          *wt,
+                                                     GeglNode              *op);
+
+static void       gimp_warp_tool_animate            (GimpWarpTool          *wt);
 
 
 G_DEFINE_TYPE (GimpWarpTool, gimp_warp_tool, GIMP_TYPE_DRAW_TOOL)
@@ -455,7 +464,6 @@ gimp_warp_tool_undo (GimpTool    *tool,
 {
   GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
   GeglNode     *to_delete;
-  GeglNode     *previous;
   const gchar  *type;
 
   if (! wt->render_node)
@@ -467,14 +475,10 @@ gimp_warp_tool_undo (GimpTool    *tool,
   if (strcmp (type, "gegl:warp"))
     return FALSE;
 
-  previous = gegl_node_get_producer (to_delete, "input", NULL);
-
-  gegl_node_disconnect (to_delete,       "input");
-  gegl_node_connect_to (previous,        "output",
-                        wt->render_node, "aux");
-
   wt->redo_stack = g_list_prepend (wt->redo_stack, g_object_ref (to_delete));
 
+  gimp_warp_tool_remove_op (wt, to_delete);
+
   gegl_node_remove_child (wt->graph, to_delete);
 
   gimp_warp_tool_update_stroke (wt, to_delete);
@@ -537,11 +541,12 @@ static void
 gimp_warp_tool_start (GimpWarpTool *wt,
                       GimpDisplay  *display)
 {
-  GimpTool      *tool     = GIMP_TOOL (wt);
-  GimpImage     *image    = gimp_display_get_image (display);
-  GimpDrawable  *drawable = gimp_image_get_active_drawable (image);
-  const Babl    *format;
-  GeglRectangle  bbox;
+  GimpTool        *tool     = GIMP_TOOL (wt);
+  GimpWarpOptions *options  = GIMP_WARP_TOOL_GET_OPTIONS (wt);
+  GimpImage       *image    = gimp_display_get_image (display);
+  GimpDrawable    *drawable = gimp_image_get_active_drawable (image);
+  const Babl      *format;
+  GeglRectangle    bbox;
 
   tool->display  = display;
   tool->drawable = drawable;
@@ -563,12 +568,22 @@ gimp_warp_tool_start (GimpWarpTool *wt,
 
   if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (wt)))
     gimp_draw_tool_start (GIMP_DRAW_TOOL (wt), display);
+
+  if (options->animate_button)
+    {
+      g_signal_connect_swapped (options->animate_button, "clicked",
+                                G_CALLBACK (gimp_warp_tool_animate),
+                                wt);
+
+      gtk_widget_set_sensitive (options->animate_button, TRUE);
+    }
 }
 
 static void
 gimp_warp_tool_halt (GimpWarpTool *wt)
 {
-  GimpTool *tool = GIMP_TOOL (wt);
+  GimpTool        *tool    = GIMP_TOOL (wt);
+  GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
 
   if (wt->coords_buffer)
     {
@@ -603,6 +618,15 @@ gimp_warp_tool_halt (GimpWarpTool *wt)
 
   if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (wt)))
     gimp_draw_tool_stop (GIMP_DRAW_TOOL (wt));
+
+  if (options->animate_button)
+    {
+      gtk_widget_set_sensitive (options->animate_button, FALSE);
+
+      g_signal_handlers_disconnect_by_func (options->animate_button,
+                                            gimp_warp_tool_animate,
+                                            wt);
+    }
 }
 
 static void
@@ -769,19 +793,144 @@ gimp_warp_tool_image_map_flush (GimpImageMap *image_map,
 
 static void
 gimp_warp_tool_add_op (GimpWarpTool *wt,
-                       GeglNode     *new_op)
+                       GeglNode     *op)
 {
   GeglNode *last_op;
 
   g_return_if_fail (GEGL_IS_NODE (wt->render_node));
 
-  gegl_node_add_child (wt->graph, new_op);
+  gegl_node_add_child (wt->graph, op);
 
   last_op = gegl_node_get_producer (wt->render_node, "aux", NULL);
 
   gegl_node_disconnect (wt->render_node, "aux");
   gegl_node_connect_to (last_op,         "output",
-                        new_op,          "input");
-  gegl_node_connect_to (new_op,          "output",
+                        op    ,          "input");
+  gegl_node_connect_to (op,              "output",
+                        wt->render_node, "aux");
+}
+
+static void
+gimp_warp_tool_remove_op (GimpWarpTool *wt,
+                          GeglNode     *op)
+{
+  GeglNode *previous;
+
+  g_return_if_fail (GEGL_IS_NODE (wt->render_node));
+
+  previous = gegl_node_get_producer (op, "input", NULL);
+
+  gegl_node_disconnect (op,              "input");
+  gegl_node_connect_to (previous,        "output",
                         wt->render_node, "aux");
+
+  gegl_node_remove_child (wt->graph, op);
+}
+
+static void
+gimp_warp_tool_animate (GimpWarpTool *wt)
+{
+  GimpTool        *tool    = GIMP_TOOL (wt);
+  GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
+  GimpImage       *orig_image;
+  GimpImage       *image;
+  GimpLayer       *layer;
+  GimpLayer       *first_layer;
+  GeglNode        *scale_node;
+  GimpProgress    *progress;
+  GtkWidget       *widget;
+  gint             i;
+
+  if (! gimp_warp_tool_get_undo_desc (tool, tool->display))
+    {
+      gimp_tool_message_literal (tool, tool->display,
+                                 _("Please add some warp strokes first."));
+      return;
+    }
+
+  /*  get rid of the image map so we can use wt->graph  */
+  if (wt->image_map)
+    {
+      gimp_image_map_abort (wt->image_map);
+      g_object_unref (wt->image_map);
+      wt->image_map = NULL;
+    }
+
+  gimp_progress_start (GIMP_PROGRESS (tool), FALSE,
+                       _("Rendering Frame %d"), 1);
+
+  orig_image = gimp_item_get_image (GIMP_ITEM (tool->drawable));
+
+  image = gimp_create_image (orig_image->gimp,
+                             gimp_item_get_width  (GIMP_ITEM (tool->drawable)),
+                             gimp_item_get_height (GIMP_ITEM (tool->drawable)),
+                             gimp_drawable_get_base_type (tool->drawable),
+                             gimp_drawable_get_precision (tool->drawable),
+                             TRUE);
+
+  /*  the first frame is always the unwarped image  */
+  layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (tool->drawable), image,
+                                         GIMP_TYPE_LAYER));
+  gimp_object_take_name (GIMP_OBJECT (layer),
+                         g_strdup_printf (_("Frame %d"), 1));
+
+  gimp_item_set_offset (GIMP_ITEM (layer), 0, 0);
+  gimp_item_set_visible (GIMP_ITEM (layer), TRUE, FALSE);
+  gimp_layer_set_mode (layer, GIMP_NORMAL_MODE, FALSE);
+  gimp_layer_set_opacity (layer, GIMP_OPACITY_OPAQUE, FALSE);
+  gimp_image_add_layer (image, layer, NULL, 0, FALSE);
+
+  first_layer = layer;
+
+  scale_node = gegl_node_new_child (NULL,
+                                    "operation",    "gimp:scalar-multiply",
+                                    "n-components", 2,
+                                    NULL);
+  gimp_warp_tool_add_op (wt, scale_node);
+
+  progress = gimp_sub_progress_new (GIMP_PROGRESS (tool));
+
+  for (i = 1; i < options->n_animation_frames; i++)
+    {
+      gimp_progress_set_text (GIMP_PROGRESS (tool),
+                              _("Rendering Frame %d"), i + 1);
+
+      gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (progress),
+                                  i, options->n_animation_frames);
+
+      layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (first_layer),
+                                               GIMP_TYPE_LAYER));
+      gimp_object_take_name (GIMP_OBJECT (layer),
+                             g_strdup_printf (_("Frame %d"), i + 1));
+
+      gegl_node_set (scale_node,
+                     "factor", (gdouble) i /
+                               (gdouble) (options->n_animation_frames - 1),
+                     NULL);
+
+      gimp_gegl_apply_operation (gimp_drawable_get_buffer (GIMP_DRAWABLE (first_layer)),
+                                 progress,
+                                 _("Frame"),
+                                 wt->graph,
+                                 gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+                                 NULL);
+
+      gimp_image_add_layer (image, layer, NULL, 0, FALSE);
+    }
+
+  g_object_unref (progress);
+
+  gimp_warp_tool_remove_op (wt, scale_node);
+
+  gimp_progress_end (GIMP_PROGRESS (tool));
+
+  /*  recreate the image map  */
+  gimp_warp_tool_create_image_map (wt, tool->drawable);
+  gimp_image_map_apply (wt->image_map, NULL);
+
+  widget = GTK_WIDGET (gimp_display_get_shell (tool->display));
+  gimp_create_display (orig_image->gimp, image, GIMP_UNIT_PIXEL, 1.0,
+                       G_OBJECT (gtk_widget_get_screen (widget)),
+                       gimp_widget_get_monitor (widget));
+  g_object_unref (image);
 }


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