gimp r24607 - in trunk: . app/core app/display app/paint



Author: neo
Date: Mon Jan 14 20:34:37 2008
New Revision: 24607
URL: http://svn.gnome.org/viewvc/gimp?rev=24607&view=rev

Log:
2008-01-14  Sven Neumann  <sven gimp org>

	* app/core/core-types.h
	* app/display/gimpdisplayshell-callbacks.c
	* app/display/gimpdisplayshell-coords.[ch]
	* app/display/gimpdisplayshell.h
	* app/paint/gimpink.[ch]
	* app/paint/gimpinkundo.[ch]: applied patch from Alexia Death 
that
	adds an event evaluation function that decides if an event is
	needed or can be discarded. As a side-product some useful 
dynamics
	parameters like velocity are added to the GimpCoords struct. The
	Ink tool is changed to use this information. See bug #508639.



Modified:
   trunk/ChangeLog
   trunk/app/core/core-types.h
   trunk/app/display/gimpdisplayshell-callbacks.c
   trunk/app/display/gimpdisplayshell-coords.c
   trunk/app/display/gimpdisplayshell-coords.h
   trunk/app/display/gimpdisplayshell.h
   trunk/app/paint/gimpink.c
   trunk/app/paint/gimpink.h
   trunk/app/paint/gimpinkundo.c
   trunk/app/paint/gimpinkundo.h

Modified: trunk/app/core/core-types.h
==============================================================================
--- trunk/app/core/core-types.h	(original)
+++ trunk/app/core/core-types.h	Mon Jan 14 20:34:37 2008
@@ -190,6 +190,11 @@
   gdouble xtilt;
   gdouble ytilt;
   gdouble wheel;
+  gdouble delta_time;
+  gdouble delta_x;
+  gdouble delta_y;
+  gdouble distance;
+  gdouble velocity;
 };
 
 

Modified: trunk/app/display/gimpdisplayshell-callbacks.c
==============================================================================
--- trunk/app/display/gimpdisplayshell-callbacks.c	(original)
+++ trunk/app/display/gimpdisplayshell-callbacks.c	Mon Jan 14 20:34:37 2008
@@ -902,7 +902,7 @@
                                                       &image_coords,
                                                       time, state, display);
 
-                    shell->last_motion_time = bevent->time;
+                    shell->last_read_motion_time = bevent->time;
                   }
               }
             break;
@@ -1182,7 +1182,7 @@
                 GdkTimeCoord **history_events;
                 gint           n_history_events;
 
-               /*  if the first mouse button is down, check for automatic
+                /*  if the first mouse button is down, check for automatic
                  *  scrolling...
                  */
                 if ((mevent->x < 0                 ||
@@ -1194,11 +1194,19 @@
                     gimp_display_shell_autoscroll_start (shell, state, mevent);
                   }
 
-                if (gimp_tool_control_get_motion_mode (active_tool->control) ==
-                    GIMP_MOTION_MODE_EXACT &&
+                /* gdk_device_get_history() has several quirks. First is
+                 * that events with borderline timestamps at both ends
+                 * are included. Because of that we need to add 1 to
+                 * lower border. The second is due to poor X event
+                 * resolution. We need to do -1 to ensure that the
+                 * amount of events between timestamps is final or
+                 * risk loosing some.
+                 */
+                if ((gimp_tool_control_get_motion_mode (active_tool->control) ==
+                    GIMP_MOTION_MODE_EXACT) &&
                     gdk_device_get_history (mevent->device, mevent->window,
-                                            shell->last_motion_time,
-                                            mevent->time,
+                                            shell->last_read_motion_time+1,
+                                            mevent->time - 1,
                                             &history_events,
                                             &n_history_events))
                   {
@@ -1206,6 +1214,7 @@
 
                     for (i = 0; i < n_history_events; i++)
                       {
+
                         gimp_display_shell_get_time_coords (shell,
                                                             mevent->device,
                                                             history_events[i],
@@ -1231,33 +1240,71 @@
                                                             x, y, width, height);
                           }
 
-                        tool_manager_motion_active (gimp,
-                                                    &image_coords,
-                                                    history_events[i]->time,
-                                                    state,
-                                                    display);
+                        /* Early removal of useless events saves CPU time.
+                         * Defaulting smoothing to 0.4.
+                         */
+                        if (gimp_display_shell_eval_event (shell,
+                                                           &image_coords, 0.4,
+                                                           history_events[i]->time))
+                          {
+                            tool_manager_motion_active (gimp,
+                                                        &image_coords,
+                                                        history_events[i]->time,
+                                                        state,
+                                                        display);
+
+                            shell->last_coords = image_coords;
+                            shell->last_disp_motion_time = history_events[i]->time;
+                          }
+
+                         shell->last_read_motion_time=history_events[i]->time;
                       }
 
                     gdk_device_free_history (history_events, n_history_events);
                   }
                 else
                   {
-                    tool_manager_motion_active (gimp,
-                                                &image_coords, time, state,
-                                                display);
-                  }
+                    /* Early removal of useless events saves CPU time.
+                     * Defaulting smoothing to 0.4.
+                     */
+                    if (gimp_display_shell_eval_event (shell,
+                                                       &image_coords, 0.4,
+                                                       time))
+                      {
+                        tool_manager_motion_active (gimp,
+                                                    &image_coords,
+                                                    time,
+                                                    state,
+                                                    display);
+
+                        shell->last_coords = image_coords;
+                        shell->last_disp_motion_time = time;
+                      }
 
-                shell->last_motion_time = mevent->time;
+                    shell->last_read_motion_time=time;
+                  }
               }
           }
 
         if (! (state &
                (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)))
           {
-            tool_manager_oper_update_active (gimp,
-                                             &image_coords, state,
-                                             shell->proximity,
-                                             display);
+            /* Early removal of useless events saves CPU time.
+             * Smoothing coasting to avoid unpredicted jumps when making contact.
+             * This may need a different solution but cant properly test it without
+             * adjustment.
+             */
+            if (gimp_display_shell_eval_event (shell, &image_coords, 0.4, time))
+              {
+                tool_manager_oper_update_active (gimp,
+                                                 &image_coords, state,
+                                                 shell->proximity,
+                                                 display);
+                shell->last_coords = image_coords;
+		shell->last_disp_motion_time = time;
+              }
+
+            shell->last_read_motion_time = time;
           }
       }
       break;

Modified: trunk/app/display/gimpdisplayshell-coords.c
==============================================================================
--- trunk/app/display/gimpdisplayshell-coords.c	(original)
+++ trunk/app/display/gimpdisplayshell-coords.c	Mon Jan 14 20:34:37 2008
@@ -20,6 +20,8 @@
 
 #include <gtk/gtk.h>
 
+#include "libgimpmath/gimpmath.h"
+
 #include "display-types.h"
 
 #include "gimpdisplayshell.h"
@@ -131,7 +133,8 @@
    *  requested axis does not exist.
    */
 
-  if (gdk_device_get_axis (device, event->axes, GDK_AXIS_PRESSURE, &coords->pressure))
+  if (gdk_device_get_axis (device,
+                           event->axes, GDK_AXIS_PRESSURE, &coords->pressure))
     coords->pressure = CLAMP (coords->pressure, GIMP_COORDS_MIN_PRESSURE,
                               GIMP_COORDS_MAX_PRESSURE);
   else
@@ -177,3 +180,127 @@
 {
   gdk_device_get_state (device, shell->canvas->window, NULL, state);
 }
+
+/**
+ * gimp_display_shell_eval_event:
+ * @shell:
+ * @coords:
+ * @inertia_factor:
+ * @time:
+ *
+ * This function evaluates the event to decide if the change is
+ * big enough to need handling and returns FALSE, if change is less
+ * than one image pixel or when smoothed event distance covers less
+ * than one pixel taking a whole lot of load off any draw tools that
+ * have no use for these sub-pixel events anyway. If the event is
+ * seen fit at first look, it is evaluated for speed and smoothed.
+ * Due to lousy time resolution of events pretty strong smoothing is
+ * applied to timestamps for sensible speed result. This function is
+ * also ideal for other event adjustment like pressure curve or
+ * calculating other derived dynamics factors like angular velocity
+ * calculation from tilt values, to allow for even more dynamic
+ * brushes. Calculated distance to last event is stored in GimpCoords
+ * because its a sideproduct of velocity calculation and is currently
+ * calculated in each tool. If they were to use this distance, more
+ * resouces on recalculating the same value would be saved.
+ *
+ * Return value:
+ **/
+gboolean
+gimp_display_shell_eval_event (GimpDisplayShell *shell,
+                               GimpCoords       *coords,
+			       gdouble           inertia_factor,
+			       guint32           time)
+{
+  guint32        thistime      = time;
+  gdouble        dist;
+  gdouble        xy_sfactor    = 0;
+  const gdouble  smooth_factor = 0.3;
+
+  if (shell->last_disp_motion_time == 0)
+    {
+      /* First pair is invalid to do any velocity calculation,
+       * so we apply constant values.
+       */
+      coords->velocity   = 100;
+      coords->delta_time = 0.001;
+      coords->distance   = 0;
+
+      shell->last_coords = *coords;
+    }
+  else
+    {
+      gdouble dx = coords->delta_x = shell->last_coords.x - coords->x;
+      gdouble dy = coords->delta_y = shell->last_coords.y - coords->y;
+
+      /* Events with distances less than 1 in either motion direction
+       * are not worth handling.
+       */
+      if (fabs (dx) < 1.0 && fabs (dy) < 1.0)
+        return FALSE;
+
+      coords->delta_time = thistime - shell->last_disp_motion_time;
+      coords->delta_time = (shell->last_coords.delta_time * (1 - smooth_factor)
+                            + coords->delta_time * smooth_factor);
+      coords->distance = dist = sqrt (SQR (dx) + SQR (dy));
+
+      /* If even smoothed time resolution does not allow to guess for speed,
+       * use last velocity.
+       */
+      if ((coords->delta_time == 0))
+        {
+          coords->velocity = shell->last_coords.velocity;
+        }
+      else
+        {
+
+          coords->velocity = (coords->distance / (gdouble) coords->delta_time) / 10;
+
+          /* A little smooth on this too, feels better in tools this way. */
+          coords->velocity = (shell->last_coords.velocity * (1 - smooth_factor)
+                              +  coords->velocity * smooth_factor);
+	  /* Speed needs upper limit */
+          coords->velocity=MIN(coords->velocity,1.0);
+        }
+
+      if (inertia_factor > 0)
+        {
+          /* Apply smoothing to X and Y.
+           *
+           * The very high velocity values (yes, thats around 25
+           * according to tests) happen at great zoom outs and scream
+           * for heavyhanded smooth so I made it automatic. This
+           * should be bound to zoom level once theres a GUI foob to
+           * adjust smooth or disabled completely.
+           */
+
+           /*Apply smoothing to X and Y.
+             Smooth is dampened for high speeds to minimize the whip effect.*/
+           xy_sfactor = inertia_factor - inertia_factor * fabs (coords->velocity);
+           coords->x = shell->last_coords.x - (shell->last_coords.delta_x * xy_sfactor
+                                               + coords->delta_x * (1 - xy_sfactor));
+           coords->y = shell->last_coords.y - (shell->last_coords.delta_y * xy_sfactor
+                                               + coords->delta_y * (1 - xy_sfactor));
+
+           /* Recalculate distance */
+           coords->distance = sqrt ((shell->last_coords.x - coords->x) *
+                                    (shell->last_coords.x - coords->x) +
+                                    (shell->last_coords.y - coords->y) *
+                                    (shell->last_coords.y - coords->y));
+
+        }
+
+#ifdef VERBOSE
+      g_printerr ("DIST: %f, DT:%f, Vel:%f, Press:%f,smooth_dd:%f, sf %f\n",
+                  coords->distance,
+                  coords->delta_time,
+                  shell->last_coords.velocity,
+                  coords->pressure,
+                  coords->distance - dist,
+                  xy_sfactor);
+#endif
+
+    }
+
+  return TRUE;
+}

Modified: trunk/app/display/gimpdisplayshell-coords.h
==============================================================================
--- trunk/app/display/gimpdisplayshell-coords.h	(original)
+++ trunk/app/display/gimpdisplayshell-coords.h	Mon Jan 14 20:34:37 2008
@@ -38,6 +38,10 @@
 void     gimp_display_shell_get_device_state  (GimpDisplayShell *shell,
                                                GdkDevice        *device,
                                                GdkModifierType  *state);
+gboolean gimp_display_shell_eval_event        (GimpDisplayShell *shell,
+                                               GimpCoords       *coords,
+                                               gdouble           inertia_factor,
+                                               guint32           time);
 
 
 #endif /* __GIMP_DISPLAY_SHELL_COORDS_H__ */

Modified: trunk/app/display/gimpdisplayshell.h
==============================================================================
--- trunk/app/display/gimpdisplayshell.h	(original)
+++ trunk/app/display/gimpdisplayshell.h	Mon Jan 14 20:34:37 2008
@@ -178,13 +178,16 @@
   gint              scroll_start_x;
   gint              scroll_start_y;
   gboolean          button_press_before_focus;
-  guint32           last_motion_time;
+  guint32           last_disp_motion_time; /*  previous time of a forwarded motion event  */
+  guint32           last_read_motion_time;
 
   GdkRectangle     *highlight;         /* in image coordinates, can be NULL   */
   GimpDrawable     *mask;
   GimpChannelType   mask_color;
 
   gpointer          scroll_info;
+
+  GimpCoords        last_coords;       /* last motion event */
 };
 
 struct _GimpDisplayShellClass

Modified: trunk/app/paint/gimpink.c
==============================================================================
--- trunk/app/paint/gimpink.c	(original)
+++ trunk/app/paint/gimpink.c	Mon Jan 14 20:34:37 2008
@@ -77,18 +77,6 @@
                                           gdouble           ytilt,
                                           gdouble           velocity);
 
-static void      time_smoother_add       (GimpInk          *ink,
-                                          guint32           value);
-static guint32   time_smoother_result    (GimpInk          *ink);
-static void      time_smoother_init      (GimpInk          *ink,
-                                          guint32           initval);
-
-static void      dist_smoother_add       (GimpInk          *ink,
-                                          gdouble           value);
-static gdouble   dist_smoother_result    (GimpInk          *ink);
-static void      dist_smoother_init      (GimpInk          *ink,
-                                          gdouble           initval);
-
 static void      render_blob             (Blob             *blob,
                                           PixelRegion      *dest);
 
@@ -270,67 +258,25 @@
                                         paint_core->cur_coords.pressure,
                                         paint_core->cur_coords.xtilt,
                                         paint_core->cur_coords.ytilt,
-                                        10.0);
+                                        100);
 
       if (ink->start_blob)
         g_free (ink->start_blob);
 
       ink->start_blob = blob_duplicate (ink->last_blob);
 
-      time_smoother_init (ink, time);
-      ink->last_time = time;
-
-      dist_smoother_init (ink, 0.0);
-      ink->init_velocity = TRUE;
-
       blob_to_render = ink->last_blob;
     }
   else
     {
       Blob    *blob;
-      gdouble  dist;
-      gdouble  velocity;
-      guint32  lasttime = ink->last_time;
-      guint32  thistime;
-
-      time_smoother_add (ink, time);
-      thistime = ink->last_time = time_smoother_result (ink);
-
-      /* The time resolution on X-based GDK motion events is bloody
-       * awful, hence the use of the smoothing function.  Sadly this
-       * also means that there is always the chance of having an
-       * indeterminite velocity since this event and the previous
-       * several may still appear to issue at the same
-       * instant. -ADM
-       */
-      if (thistime == lasttime)
-        thistime = lasttime + 1;
-
-      dist = sqrt ((paint_core->last_coords.x - paint_core->cur_coords.x) *
-                   (paint_core->last_coords.x - paint_core->cur_coords.x) +
-                   (paint_core->last_coords.y - paint_core->cur_coords.y) *
-                   (paint_core->last_coords.y - paint_core->cur_coords.y));
-
-      if (ink->init_velocity)
-        {
-          dist_smoother_init (ink, dist);
-          ink->init_velocity = FALSE;
-        }
-      else
-        {
-          dist_smoother_add (ink, dist);
-          dist = dist_smoother_result (ink);
-        }
-
-      velocity = 10.0 * sqrt ((dist) / (gdouble) (thistime - lasttime));
-
       blob = ink_pen_ellipse (options,
                               paint_core->cur_coords.x,
                               paint_core->cur_coords.y,
                               paint_core->cur_coords.pressure,
                               paint_core->cur_coords.xtilt,
                               paint_core->cur_coords.ytilt,
-                              velocity);
+                              paint_core->cur_coords.velocity * 100);
 
       blob_union = blob_convex_union (ink->last_blob, blob);
       g_free (ink->last_blob);
@@ -510,84 +456,6 @@
 }
 
 
-static void
-time_smoother_init (GimpInk *ink,
-                    guint32  initval)
-{
-  gint i;
-
-  ink->ts_index = 0;
-
-  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
-    ink->ts_buffer[i] = initval;
-}
-
-static guint32
-time_smoother_result (GimpInk *ink)
-{
-  guint64 result = 0;
-  gint    i;
-
-  for (i = 0; i < TIME_SMOOTHER_BUFFER; i++)
-    result += ink->ts_buffer[i];
-
-  return (result / (guint64) TIME_SMOOTHER_BUFFER);
-}
-
-static void
-time_smoother_add (GimpInk *ink,
-                   guint32  value)
-{
-  guint64 long_value = (guint64) value;
-
-  /*  handle wrap-around of time values  */
-  if (long_value < ink->ts_buffer[ink->ts_index])
-    long_value += (guint64) + G_MAXUINT32;
-
-  ink->ts_buffer[ink->ts_index++] = long_value;
-
-  ink->ts_buffer[ink->ts_index++] = value;
-
-  if (ink->ts_index == TIME_SMOOTHER_BUFFER)
-    ink->ts_index = 0;
-}
-
-
-static void
-dist_smoother_init (GimpInk *ink,
-                    gdouble  initval)
-{
-  gint i;
-
-  ink->dt_index = 0;
-
-  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
-    ink->dt_buffer[i] = initval;
-}
-
-static gdouble
-dist_smoother_result (GimpInk *ink)
-{
-  gint    i;
-  gdouble result = 0.0;
-
-  for (i = 0; i < DIST_SMOOTHER_BUFFER; i++)
-    result += ink->dt_buffer[i];
-
-  return (result / (gdouble) DIST_SMOOTHER_BUFFER);
-}
-
-static void
-dist_smoother_add (GimpInk *ink,
-                   gdouble  value)
-{
-  ink->dt_buffer[ink->dt_index++] = value;
-
-  if (ink->dt_index == DIST_SMOOTHER_BUFFER)
-    ink->dt_index = 0;
-}
-
-
 /*********************************/
 /*  Rendering functions          */
 /*********************************/

Modified: trunk/app/paint/gimpink.h
==============================================================================
--- trunk/app/paint/gimpink.h	(original)
+++ trunk/app/paint/gimpink.h	Mon Jan 14 20:34:37 2008
@@ -24,10 +24,6 @@
 #include "gimpink-blob.h"
 
 
-#define DIST_SMOOTHER_BUFFER 10
-#define TIME_SMOOTHER_BUFFER 10
-
-
 #define GIMP_TYPE_INK            (gimp_ink_get_type ())
 #define GIMP_INK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INK, GimpInk))
 #define GIMP_INK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INK, GimpInkClass))
@@ -46,18 +42,6 @@
 
   Blob          *cur_blob;     /*  current blob                   */
   Blob          *last_blob;    /*  blob for last cursor position  */
-
-  /* circular distance history buffer */
-  gdouble        dt_buffer[DIST_SMOOTHER_BUFFER];
-  gint           dt_index;
-
-  /* circular timing history buffer */
-  guint32        ts_buffer[TIME_SMOOTHER_BUFFER];
-  gint           ts_index;
-
-  guint32        last_time;     /*  previous time of a motion event  */
-
-  gboolean       init_velocity;
 };
 
 struct _GimpInkClass

Modified: trunk/app/paint/gimpinkundo.c
==============================================================================
--- trunk/app/paint/gimpinkundo.c	(original)
+++ trunk/app/paint/gimpinkundo.c	Mon Jan 14 20:34:37 2008
@@ -82,20 +82,6 @@
   if (ink->start_blob)
     ink_undo->last_blob = blob_duplicate (ink->start_blob);
 
-  memcpy (ink_undo->dt_buffer, ink->dt_buffer,
-          sizeof (ink_undo->dt_buffer));
-
-  ink_undo->dt_index = ink->dt_index;
-
-  memcpy (ink_undo->ts_buffer, ink->ts_buffer,
-          sizeof (ink_undo->ts_buffer));
-
-  ink_undo->ts_index = ink->ts_index;
-
-  ink_undo->last_time = ink->last_time;
-
-  ink_undo->init_velocity = ink->init_velocity;
-
   return object;
 }
 
@@ -112,44 +98,11 @@
     {
       GimpInk *ink = GIMP_INK (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core);
       Blob    *tmp_blob;
-      gint     tmp_int;
-      gdouble  tmp_double;
-      guint32  tmp_int_buf[DIST_SMOOTHER_BUFFER];
-      gdouble  tmp_double_buf[DIST_SMOOTHER_BUFFER];
 
       tmp_blob = ink->last_blob;
       ink->last_blob = ink_undo->last_blob;
       ink_undo->last_blob = tmp_blob;
 
-      memcpy (tmp_double_buf, ink->dt_buffer,
-              sizeof (tmp_double_buf));
-      memcpy (ink->dt_buffer, ink_undo->dt_buffer,
-              sizeof (tmp_double_buf));
-      memcpy (ink_undo->dt_buffer, tmp_double_buf,
-              sizeof (tmp_double_buf));
-
-      tmp_int = ink->dt_index;
-      ink->dt_index = ink_undo->dt_index;
-      ink_undo->dt_index = tmp_int;
-
-      memcpy (tmp_int_buf, ink->ts_buffer,
-              sizeof (tmp_int_buf));
-      memcpy (ink->ts_buffer, ink_undo->ts_buffer,
-              sizeof (tmp_int_buf));
-      memcpy (ink_undo->ts_buffer, tmp_int_buf,
-              sizeof (tmp_int_buf));
-
-      tmp_int = ink->ts_index;
-      ink->ts_index = ink_undo->ts_index;
-      ink_undo->ts_index = tmp_int;
-
-      tmp_double = ink->last_time;
-      ink->last_time = ink_undo->last_time;
-      ink_undo->last_time = tmp_double;
-
-      tmp_int = ink->init_velocity;
-      ink->init_velocity = ink_undo->init_velocity;
-      ink_undo->init_velocity = tmp_int;
     }
 }
 

Modified: trunk/app/paint/gimpinkundo.h
==============================================================================
--- trunk/app/paint/gimpinkundo.h	(original)
+++ trunk/app/paint/gimpinkundo.h	Mon Jan 14 20:34:37 2008
@@ -38,16 +38,6 @@
   GimpPaintCoreUndo  parent_instance;
 
   Blob              *last_blob;
-
-  gdouble            dt_buffer[DIST_SMOOTHER_BUFFER];
-  gint               dt_index;
-
-  guint32            ts_buffer[TIME_SMOOTHER_BUFFER];
-  gint               ts_index;
-
-  gdouble            last_time;
-
-  gboolean           init_velocity;
 };
 
 struct _GimpInkUndoClass



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