[gimp] app: add option to stroke the warp tool during cursor motion ...



commit 40afffbebc87ef46abe66acb90b471ac013dd89c
Author: Ell <ell_se yahoo com>
Date:   Fri May 19 03:32:59 2017 -0400

    app: add option to stroke the warp tool during cursor motion ...
    
    ... and to disable/control the rate of the periodic stroke.
    
    The warp tool is now fast enough to enable stroking directly in
    the motion handler, which gives better-quality response to motion
    than stroking periodically.  It's not quite fast enough to enable
    exact motion, though :/
    
    Allow individually enabling/disabling stroking during motion and
    periodically, and allow controlling the rate of the periodical
    stroke.

 app/tools/gimpwarpoptions.c |   93 +++++++++++++++++++----
 app/tools/gimpwarpoptions.h |    4 +
 app/tools/gimpwarptool.c    |  178 ++++++++++++++++++++++++++++++-------------
 app/tools/gimpwarptool.h    |    1 -
 4 files changed, 207 insertions(+), 69 deletions(-)
---
diff --git a/app/tools/gimpwarpoptions.c b/app/tools/gimpwarpoptions.c
index 913560d..40385a1 100644
--- a/app/tools/gimpwarpoptions.c
+++ b/app/tools/gimpwarpoptions.c
@@ -44,18 +44,21 @@ enum
   PROP_EFFECT_SIZE,
   PROP_EFFECT_HARDNESS,
   PROP_STROKE_SPACING,
+  PROP_STROKE_DURING_MOTION,
+  PROP_STROKE_PERIODICALLY,
+  PROP_STROKE_PERIODICALLY_RATE,
   PROP_N_ANIMATION_FRAMES
 };
 
 
-static void   gimp_warp_options_set_property (GObject      *object,
-                                              guint         property_id,
-                                              const GValue *value,
-                                              GParamSpec   *pspec);
-static void   gimp_warp_options_get_property (GObject      *object,
-                                              guint         property_id,
-                                              GValue       *value,
-                                              GParamSpec   *pspec);
+static void       gimp_warp_options_set_property (GObject      *object,
+                                                  guint         property_id,
+                                                  const GValue *value,
+                                                  GParamSpec   *pspec);
+static void       gimp_warp_options_get_property (GObject      *object,
+                                                  guint         property_id,
+                                                  GValue       *value,
+                                                  GParamSpec   *pspec);
 
 
 G_DEFINE_TYPE (GimpWarpOptions, gimp_warp_options,
@@ -108,6 +111,27 @@ gimp_warp_options_class_init (GimpWarpOptionsClass *klass)
                            1.0, 100.0, 20.0,
                            GIMP_PARAM_STATIC_STRINGS);
 
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_STROKE_DURING_MOTION,
+                            "stroke-during-motion",
+                            _("During motion"),
+                            _("Apply effect during motion"),
+                            TRUE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_STROKE_PERIODICALLY,
+                            "stroke-periodically",
+                            _("Periodically"),
+                            _("Apply effect periodically"),
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_STROKE_PERIODICALLY_RATE,
+                           "stroke-periodically-rate",
+                           _("Rate"),
+                           _("Periodical stroke rate"),
+                           0.0, 100.0, 50.0,
+                           GIMP_PARAM_STATIC_STRINGS);
+
   GIMP_CONFIG_PROP_INT (object_class, PROP_N_ANIMATION_FRAMES,
                         "n-animation-frames",
                         _("Frames"),
@@ -146,6 +170,15 @@ gimp_warp_options_set_property (GObject      *object,
     case PROP_STROKE_SPACING:
       options->stroke_spacing = g_value_get_double (value);
       break;
+    case PROP_STROKE_DURING_MOTION:
+      options->stroke_during_motion = g_value_get_boolean (value);
+      break;
+    case PROP_STROKE_PERIODICALLY:
+      options->stroke_periodically = g_value_get_boolean (value);
+      break;
+    case PROP_STROKE_PERIODICALLY_RATE:
+      options->stroke_periodically_rate = g_value_get_double (value);
+      break;
     case PROP_N_ANIMATION_FRAMES:
       options->n_animation_frames = g_value_get_int (value);
       break;
@@ -181,6 +214,15 @@ gimp_warp_options_get_property (GObject    *object,
     case PROP_STROKE_SPACING:
       g_value_set_double (value, options->stroke_spacing);
       break;
+    case PROP_STROKE_DURING_MOTION:
+      g_value_set_boolean (value, options->stroke_during_motion);
+      break;
+    case PROP_STROKE_PERIODICALLY:
+      g_value_set_boolean (value, options->stroke_periodically);
+      break;
+    case PROP_STROKE_PERIODICALLY_RATE:
+      g_value_set_double (value, options->stroke_periodically_rate);
+      break;
     case PROP_N_ANIMATION_FRAMES:
       g_value_set_int (value, options->n_animation_frames);
       break;
@@ -198,7 +240,8 @@ gimp_warp_options_gui (GimpToolOptions *tool_options)
   GObject         *config  = G_OBJECT (tool_options);
   GtkWidget       *vbox    = gimp_tool_options_gui (tool_options);
   GtkWidget       *frame;
-  GtkWidget       *anim_vbox;
+  GtkWidget       *vbox2;
+  GtkWidget       *button;
   GtkWidget       *combo;
   GtkWidget       *scale;
 
@@ -231,24 +274,46 @@ gimp_warp_options_gui (GimpToolOptions *tool_options)
   gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
   gtk_widget_show (scale);
 
+  /*  the stroke frame  */
+  frame = gimp_frame_new (_("Stroke"));
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (frame), vbox2);
+  gtk_widget_show (vbox2);
+
+  button = gimp_prop_check_button_new (config, "stroke-during-motion", NULL);
+  gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  scale = gimp_prop_spin_scale_new (config, "stroke-periodically-rate", NULL,
+                                    1, 10, 1);
+  gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 0.0, 100.0);
+
+  frame = gimp_prop_expanding_frame_new (config, "stroke-periodically", NULL,
+                                         scale, NULL);
+  gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
   /*  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);
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (frame), vbox2);
+  gtk_widget_show (vbox2);
 
   scale = gimp_prop_spin_scale_new (config, "n-animation-frames", NULL,
                                     1.0, 10.0, 0);
   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_box_pack_start (GTK_BOX (vbox2), 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,
+  gtk_box_pack_start (GTK_BOX (vbox2), options->animate_button,
                       FALSE, FALSE, 0);
   gtk_widget_show (options->animate_button);
 
diff --git a/app/tools/gimpwarpoptions.h b/app/tools/gimpwarpoptions.h
index 231c66f..f66beda 100644
--- a/app/tools/gimpwarpoptions.h
+++ b/app/tools/gimpwarpoptions.h
@@ -45,6 +45,10 @@ struct _GimpWarpOptions
   gdouble           effect_hardness;
   gdouble           stroke_spacing;
 
+  gboolean          stroke_during_motion;
+  gboolean          stroke_periodically;
+  gdouble           stroke_periodically_rate;
+
   gint              n_animation_frames;
 
   /*  options gui  */
diff --git a/app/tools/gimpwarptool.c b/app/tools/gimpwarptool.c
index b8a1744..bed1676 100644
--- a/app/tools/gimpwarptool.c
+++ b/app/tools/gimpwarptool.c
@@ -52,9 +52,9 @@
 #include "gimp-intl.h"
 
 
-#define STROKE_PERIOD   100
-#define PREVIEW_SAMPLER GEGL_SAMPLER_NEAREST
-#define COMMIT_SAMPLER  GEGL_SAMPLER_CUBIC
+#define STROKE_TIMER_MAX_FPS 20
+#define PREVIEW_SAMPLER      GEGL_SAMPLER_NEAREST
+#define COMMIT_SAMPLER       GEGL_SAMPLER_CUBIC
 
 
 static void       gimp_warp_tool_control            (GimpTool              *tool,
@@ -103,11 +103,17 @@ static void       gimp_warp_tool_options_notify     (GimpTool              *tool
 
 static void       gimp_warp_tool_draw               (GimpDrawTool          *draw_tool);
 
+static gboolean   gimp_warp_tool_can_stroke         (GimpWarpTool          *wt,
+                                                     GimpDisplay           *display,
+                                                     gboolean               show_message);
+
 static gboolean   gimp_warp_tool_start              (GimpWarpTool          *wt,
                                                      GimpDisplay           *display);
 static void       gimp_warp_tool_halt               (GimpWarpTool          *wt);
 static void       gimp_warp_tool_commit             (GimpWarpTool          *wt);
 
+static void       gimp_warp_tool_start_stroke_timer (GimpWarpTool          *wt);
+static void       gimp_warp_tool_stop_stroke_timer  (GimpWarpTool          *wt);
 static gboolean   gimp_warp_tool_stroke_timer       (GimpWarpTool          *wt);
 
 static void       gimp_warp_tool_create_graph       (GimpWarpTool          *wt);
@@ -243,6 +249,9 @@ gimp_warp_tool_button_press (GimpTool            *tool,
         return;
     }
 
+  if (! gimp_warp_tool_can_stroke (wt, display, TRUE))
+    return;
+
   wt->current_stroke = gegl_path_new ();
 
   new_op = gegl_node_new_child (NULL,
@@ -267,11 +276,7 @@ gimp_warp_tool_button_press (GimpTool            *tool,
   gegl_path_append (wt->current_stroke,
                     'M', coords->x - off_x, coords->y - off_y);
 
-  wt->cursor_moved = FALSE;
-
-  wt->stroke_timer = g_timeout_add (STROKE_PERIOD,
-                                    (GSourceFunc) gimp_warp_tool_stroke_timer,
-                                    wt);
+  gimp_warp_tool_start_stroke_timer (wt);
 
   gimp_tool_control_activate (tool->control);
 }
@@ -290,8 +295,7 @@ gimp_warp_tool_button_release (GimpTool              *tool,
 
   gimp_tool_control_halt (tool->control);
 
-  g_source_remove (wt->stroke_timer);
-  wt->stroke_timer = 0;
+  gimp_warp_tool_stop_stroke_timer (wt);
 
   g_signal_handlers_disconnect_by_func (wt->current_stroke,
                                         gimp_warp_tool_stroke_changed,
@@ -339,13 +343,25 @@ gimp_warp_tool_motion (GimpTool         *tool,
                        GdkModifierType   state,
                        GimpDisplay      *display)
 {
-  GimpWarpTool *wt = GIMP_WARP_TOOL (tool);
+  GimpWarpTool    *wt = GIMP_WARP_TOOL (tool);
+  GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
 
   gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
 
-  wt->cursor_x     = coords->x;
-  wt->cursor_y     = coords->y;
-  wt->cursor_moved = TRUE;
+  wt->cursor_x = coords->x;
+  wt->cursor_y = coords->y;
+
+  if (options->stroke_during_motion)
+    {
+      gint off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
+
+      gegl_path_append (wt->current_stroke,
+                        'L', wt->cursor_x - off_x, wt->cursor_y - off_y);
+
+      gimp_warp_tool_start_stroke_timer (wt);
+    }
 
   gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
 }
@@ -414,10 +430,15 @@ gimp_warp_tool_cursor_update (GimpTool         *tool,
                               GdkModifierType   state,
                               GimpDisplay      *display)
 {
+  GimpWarpTool       *wt       = GIMP_WARP_TOOL (tool);
   GimpWarpOptions    *options  = GIMP_WARP_TOOL_GET_OPTIONS (tool);
   GimpCursorModifier  modifier = GIMP_CURSOR_MODIFIER_PLUS;
 
-  if (display == tool->display)
+  if (! gimp_warp_tool_can_stroke (wt, display, FALSE))
+    {
+      modifier = GIMP_CURSOR_MODIFIER_BAD;
+    }
+  else if (display == tool->display)
     {
       /* FIXME have better cursors  */
 
@@ -434,18 +455,6 @@ gimp_warp_tool_cursor_update (GimpTool         *tool,
           break;
         }
     }
-  else
-    {
-      GimpImage    *image    = gimp_display_get_image (display);
-      GimpDrawable *drawable = gimp_image_get_active_drawable (image);
-
-      if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) ||
-          gimp_item_is_content_locked (GIMP_ITEM (drawable))    ||
-          ! gimp_item_is_visible (GIMP_ITEM (drawable)))
-        {
-          modifier = GIMP_CURSOR_MODIFIER_BAD;
-        }
-    }
 
   gimp_tool_control_set_cursor_modifier (tool->control, modifier);
 
@@ -571,37 +580,77 @@ gimp_warp_tool_draw (GimpDrawTool *draw_tool)
 }
 
 static gboolean
-gimp_warp_tool_start (GimpWarpTool *wt,
-                      GimpDisplay  *display)
+gimp_warp_tool_can_stroke (GimpWarpTool *wt,
+                           GimpDisplay  *display,
+                           gboolean      show_message)
 {
   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;
 
   if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
     {
-      gimp_tool_message_literal (tool, display,
-                                 _("Cannot warp layer groups."));
+      if (show_message)
+        {
+          gimp_tool_message_literal (tool, display,
+                                     _("Cannot warp layer groups."));
+        }
+
       return FALSE;
     }
 
   if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
     {
-      gimp_tool_message_literal (tool, display,
-                                 _("The active layer's pixels are locked."));
+      if (show_message)
+        {
+          gimp_tool_message_literal (tool, display,
+                                     _("The active layer's pixels are locked."));
+        }
+
       return FALSE;
     }
 
   if (! gimp_item_is_visible (GIMP_ITEM (drawable)))
     {
-      gimp_tool_message_literal (tool, display,
-                                 _("The active layer is not visible."));
+      if (show_message)
+        {
+          gimp_tool_message_literal (tool, display,
+                                     _("The active layer is not visible."));
+        }
+
+      return FALSE;
+    }
+
+  if (! options->stroke_during_motion &&
+      ! options->stroke_periodically)
+    {
+      if (show_message)
+        {
+          gimp_tool_message_literal (tool, display,
+                                     _("No stroke events selected."));
+        }
+
       return FALSE;
     }
 
+  return TRUE;
+}
+
+static gboolean
+gimp_warp_tool_start (GimpWarpTool *wt,
+                      GimpDisplay  *display)
+{
+  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;
+
+  if (! gimp_warp_tool_can_stroke (wt, display, TRUE))
+    return FALSE;
+
   tool->display  = display;
   tool->drawable = drawable;
 
@@ -711,26 +760,47 @@ gimp_warp_tool_commit (GimpWarpTool *wt)
     }
 }
 
-static gboolean
-gimp_warp_tool_stroke_timer (GimpWarpTool *wt)
+static void
+gimp_warp_tool_start_stroke_timer (GimpWarpTool *wt)
 {
-  GimpTool        *tool = GIMP_TOOL (wt);
   GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
-  gint             off_x, off_y;
 
-  /* don't append the current point to the path if we're using the MOVE
-   * behavior, and the cursor didn't move since last time; it's a nop, and
-   * results in an unnecessary update.
-   */
-  if (options->behavior != GIMP_WARP_BEHAVIOR_MOVE || wt->cursor_moved)
+  gimp_warp_tool_stop_stroke_timer (wt);
+
+  if (options->stroke_periodically                    &&
+      options->stroke_periodically_rate > 0.0         &&
+      ! (options->behavior == GIMP_WARP_BEHAVIOR_MOVE &&
+         options->stroke_during_motion))
     {
-      gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
+      gdouble fps;
 
-      gegl_path_append (wt->current_stroke,
-                        'L', wt->cursor_x - off_x, wt->cursor_y - off_y);
+      fps = STROKE_TIMER_MAX_FPS * options->stroke_periodically_rate / 100.0;
 
-      wt->cursor_moved = FALSE;
+      wt->stroke_timer = g_timeout_add (1000.0 / fps,
+                                        (GSourceFunc) gimp_warp_tool_stroke_timer,
+                                        wt);
     }
+}
+
+static void
+gimp_warp_tool_stop_stroke_timer (GimpWarpTool *wt)
+{
+  if (wt->stroke_timer)
+    g_source_remove (wt->stroke_timer);
+
+  wt->stroke_timer = 0;
+}
+
+static gboolean
+gimp_warp_tool_stroke_timer (GimpWarpTool *wt)
+{
+  GimpTool *tool = GIMP_TOOL (wt);
+  gint      off_x, off_y;
+
+  gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y);
+
+  gegl_path_append (wt->current_stroke,
+                    'L', wt->cursor_x - off_x, wt->cursor_y - off_y);
 
   return TRUE;
 }
@@ -826,10 +896,10 @@ gimp_warp_tool_update_stroke (GimpWarpTool *wt,
       gegl_path_get_bounds (stroke, &min_x, &max_x, &min_y, &max_y);
       g_object_unref (stroke);
 
-      bbox.x      = min_x - size * 0.5;
-      bbox.y      = min_y - size * 0.5;
-      bbox.width  = max_x - min_x + size;
-      bbox.height = max_y - min_y + size;
+      bbox.x      = floor (min_x - size * 0.5);
+      bbox.y      = floor (min_y - size * 0.5);
+      bbox.width  = ceil (max_x + size * 0.5) - bbox.x;
+      bbox.height = ceil (max_y + size * 0.5) - bbox.y;
 
 #ifdef WARP_DEBUG
   g_printerr ("update stroke: (%d,%d), %dx%d\n",
diff --git a/app/tools/gimpwarptool.h b/app/tools/gimpwarptool.h
index 627e022..932f43a 100644
--- a/app/tools/gimpwarptool.h
+++ b/app/tools/gimpwarptool.h
@@ -43,7 +43,6 @@ struct _GimpWarpTool
 
   gdouble             cursor_x;      /* Hold the cursor x position */
   gdouble             cursor_y;      /* Hold the cursor y position */
-  gboolean            cursor_moved;  /* Did the cursor move since the last stroke? */
 
   GeglBuffer         *coords_buffer; /* Buffer where coordinates are stored */
 


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