1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <string.h>
21 
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24 
25 #include "libgimpmath/gimpmath.h"
26 
27 #include "display-types.h"
28 
29 #include "core/gimpcoords.h"
30 #include "core/gimpcoords-interpolate.h"
31 #include "core/gimpmarshal.h"
32 
33 #include "gimpmotionbuffer.h"
34 
35 
36 /* Velocity unit is screen pixels per millisecond we pass to tools as 1. */
37 #define VELOCITY_UNIT        3.0
38 #define EVENT_FILL_PRECISION 6.0
39 #define DIRECTION_RADIUS     (1.0 / MAX (scale_x, scale_y))
40 #define SMOOTH_FACTOR        0.3
41 
42 
43 enum
44 {
45   PROP_0
46 };
47 
48 enum
49 {
50   STROKE,
51   HOVER,
52   LAST_SIGNAL
53 };
54 
55 
56 /*  local function prototypes  */
57 
58 static void     gimp_motion_buffer_dispose             (GObject          *object);
59 static void     gimp_motion_buffer_finalize            (GObject          *object);
60 static void     gimp_motion_buffer_set_property        (GObject          *object,
61                                                         guint             property_id,
62                                                         const GValue     *value,
63                                                         GParamSpec       *pspec);
64 static void     gimp_motion_buffer_get_property        (GObject          *object,
65                                                         guint             property_id,
66                                                         GValue           *value,
67                                                         GParamSpec       *pspec);
68 
69 static void     gimp_motion_buffer_push_event_history  (GimpMotionBuffer *buffer,
70                                                         const GimpCoords *coords);
71 static void     gimp_motion_buffer_pop_event_queue     (GimpMotionBuffer *buffer,
72                                                         GimpCoords       *coords);
73 
74 static void     gimp_motion_buffer_interpolate_stroke  (GimpMotionBuffer *buffer,
75                                                         GimpCoords       *coords);
76 static gboolean gimp_motion_buffer_event_queue_timeout (GimpMotionBuffer *buffer);
77 
78 
79 G_DEFINE_TYPE (GimpMotionBuffer, gimp_motion_buffer, GIMP_TYPE_OBJECT)
80 
81 #define parent_class gimp_motion_buffer_parent_class
82 
83 static guint motion_buffer_signals[LAST_SIGNAL] = { 0 };
84 
85 
86 static void
gimp_motion_buffer_class_init(GimpMotionBufferClass * klass)87 gimp_motion_buffer_class_init (GimpMotionBufferClass *klass)
88 {
89   GObjectClass *object_class = G_OBJECT_CLASS (klass);
90 
91   motion_buffer_signals[STROKE] =
92     g_signal_new ("stroke",
93                   G_TYPE_FROM_CLASS (klass),
94                   G_SIGNAL_RUN_FIRST,
95                   G_STRUCT_OFFSET (GimpMotionBufferClass, stroke),
96                   NULL, NULL,
97                   gimp_marshal_VOID__POINTER_UINT_FLAGS,
98                   G_TYPE_NONE, 3,
99                   G_TYPE_POINTER,
100                   G_TYPE_UINT,
101                   GDK_TYPE_MODIFIER_TYPE);
102 
103   motion_buffer_signals[HOVER] =
104     g_signal_new ("hover",
105                   G_TYPE_FROM_CLASS (klass),
106                   G_SIGNAL_RUN_FIRST,
107                   G_STRUCT_OFFSET (GimpMotionBufferClass, hover),
108                   NULL, NULL,
109                   gimp_marshal_VOID__POINTER_FLAGS_BOOLEAN,
110                   G_TYPE_NONE, 3,
111                   G_TYPE_POINTER,
112                   GDK_TYPE_MODIFIER_TYPE,
113                   G_TYPE_BOOLEAN);
114 
115   object_class->dispose      = gimp_motion_buffer_dispose;
116   object_class->finalize     = gimp_motion_buffer_finalize;
117   object_class->set_property = gimp_motion_buffer_set_property;
118   object_class->get_property = gimp_motion_buffer_get_property;
119 }
120 
121 static void
gimp_motion_buffer_init(GimpMotionBuffer * buffer)122 gimp_motion_buffer_init (GimpMotionBuffer *buffer)
123 {
124   buffer->event_history = g_array_new (FALSE, FALSE, sizeof (GimpCoords));
125   buffer->event_queue   = g_array_new (FALSE, FALSE, sizeof (GimpCoords));
126 }
127 
128 static void
gimp_motion_buffer_dispose(GObject * object)129 gimp_motion_buffer_dispose (GObject *object)
130 {
131   GimpMotionBuffer *buffer = GIMP_MOTION_BUFFER (object);
132 
133   if (buffer->event_delay_timeout)
134     {
135       g_source_remove (buffer->event_delay_timeout);
136       buffer->event_delay_timeout = 0;
137     }
138 
139   G_OBJECT_CLASS (parent_class)->dispose (object);
140 }
141 
142 static void
gimp_motion_buffer_finalize(GObject * object)143 gimp_motion_buffer_finalize (GObject *object)
144 {
145   GimpMotionBuffer *buffer = GIMP_MOTION_BUFFER (object);
146 
147   if (buffer->event_history)
148     {
149       g_array_free (buffer->event_history, TRUE);
150       buffer->event_history = NULL;
151     }
152 
153   if (buffer->event_queue)
154     {
155       g_array_free (buffer->event_queue, TRUE);
156       buffer->event_queue = NULL;
157     }
158 
159   G_OBJECT_CLASS (parent_class)->finalize (object);
160 }
161 
162 static void
gimp_motion_buffer_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)163 gimp_motion_buffer_set_property (GObject      *object,
164                                  guint         property_id,
165                                  const GValue *value,
166                                  GParamSpec   *pspec)
167 {
168   switch (property_id)
169     {
170     default:
171       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
172       break;
173     }
174 }
175 
176 static void
gimp_motion_buffer_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)177 gimp_motion_buffer_get_property (GObject    *object,
178                                  guint       property_id,
179                                  GValue     *value,
180                                  GParamSpec *pspec)
181 {
182   switch (property_id)
183     {
184     default:
185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
186       break;
187     }
188 }
189 
190 
191 /*  public functions  */
192 
193 GimpMotionBuffer *
gimp_motion_buffer_new(void)194 gimp_motion_buffer_new (void)
195 {
196   return g_object_new (GIMP_TYPE_MOTION_BUFFER,
197                        NULL);
198 }
199 
200 void
gimp_motion_buffer_begin_stroke(GimpMotionBuffer * buffer,guint32 time,GimpCoords * last_motion)201 gimp_motion_buffer_begin_stroke (GimpMotionBuffer *buffer,
202                                  guint32           time,
203                                  GimpCoords       *last_motion)
204 {
205   g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer));
206   g_return_if_fail (last_motion != NULL);
207 
208   buffer->last_read_motion_time = time;
209 
210   *last_motion = buffer->last_coords;
211 }
212 
213 void
gimp_motion_buffer_end_stroke(GimpMotionBuffer * buffer)214 gimp_motion_buffer_end_stroke (GimpMotionBuffer *buffer)
215 {
216   g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer));
217 
218   if (buffer->event_delay_timeout)
219     {
220       g_source_remove (buffer->event_delay_timeout);
221       buffer->event_delay_timeout = 0;
222     }
223 
224   gimp_motion_buffer_event_queue_timeout (buffer);
225 }
226 
227 /**
228  * gimp_motion_buffer_motion_event:
229  * @buffer:
230  * @coords:
231  * @time:
232  * @event_fill:
233  *
234  * This function evaluates the event to decide if the change is big
235  * enough to need handling and returns FALSE, if change is less than
236  * set filter level taking a whole lot of load off any draw tools that
237  * have no use for these events anyway. If the event is seen fit at
238  * first look, it is evaluated for speed and smoothed.  Due to lousy
239  * time resolution of events pretty strong smoothing is applied to
240  * timestamps for sensible speed result. This function is also ideal
241  * for other event adjustment like pressure curve or calculating other
242  * derived dynamics factors like angular velocity calculation from
243  * tilt values, to allow for even more dynamic brushes. Calculated
244  * distance to last event is stored in GimpCoords because its a
245  * sideproduct of velocity calculation and is currently calculated in
246  * each tool. If they were to use this distance, more resources on
247  * recalculating the same value would be saved.
248  *
249  * Return value: %TRUE if the motion was significant enough to be
250  *               processed, %FALSE otherwise.
251  **/
252 gboolean
gimp_motion_buffer_motion_event(GimpMotionBuffer * buffer,GimpCoords * coords,guint32 time,gboolean event_fill)253 gimp_motion_buffer_motion_event (GimpMotionBuffer *buffer,
254                                  GimpCoords       *coords,
255                                  guint32           time,
256                                  gboolean          event_fill)
257 {
258   gdouble  delta_time  = 0.001;
259   gdouble  delta_x     = 0.0;
260   gdouble  delta_y     = 0.0;
261   gdouble  distance    = 1.0;
262   gdouble  scale_x     = coords->xscale;
263   gdouble  scale_y     = coords->yscale;
264 
265   g_return_val_if_fail (GIMP_IS_MOTION_BUFFER (buffer), FALSE);
266   g_return_val_if_fail (coords != NULL, FALSE);
267 
268   /*  the last_read_motion_time most be set unconditionally, so set
269    *  it early
270    */
271   buffer->last_read_motion_time = time;
272 
273   delta_time = (buffer->last_motion_delta_time * (1 - SMOOTH_FACTOR) +
274                 (time - buffer->last_motion_time) * SMOOTH_FACTOR);
275 
276   if (buffer->last_motion_time == 0)
277     {
278       /*  First pair is invalid to do any velocity calculation, so we
279        *  apply a constant value.
280        */
281       coords->velocity = 1.0;
282     }
283   else
284     {
285       GimpCoords last_dir_event = buffer->last_coords;
286       gdouble    filter;
287       gdouble    dist;
288       gdouble    delta_dir;
289       gdouble    dir_delta_x = 0.0;
290       gdouble    dir_delta_y = 0.0;
291 
292       delta_x = last_dir_event.x - coords->x;
293       delta_y = last_dir_event.y - coords->y;
294 
295       /*  Events with distances less than the screen resolution are
296        *  not worth handling.
297        */
298       filter = MIN (1.0 / scale_x, 1.0 / scale_y) / 2.0;
299 
300       if (fabs (delta_x) < filter &&
301           fabs (delta_y) < filter)
302         {
303           return FALSE;
304         }
305 
306       distance = dist = sqrt (SQR (delta_x) + SQR (delta_y));
307 
308       /*  If even smoothed time resolution does not allow to guess for
309        *  speed, use last velocity.
310        */
311       if (delta_time == 0)
312         {
313           coords->velocity = buffer->last_coords.velocity;
314         }
315       else
316         {
317           /*  We need to calculate the velocity in screen coordinates
318            *  for human interaction
319            */
320           gdouble screen_distance = (distance * MIN (scale_x, scale_y));
321 
322           /* Calculate raw valocity */
323           coords->velocity = ((screen_distance / delta_time) / VELOCITY_UNIT);
324 
325           /* Adding velocity dependent smoothing, feels better in tools. */
326           coords->velocity = (buffer->last_coords.velocity *
327                               (1 - MIN (SMOOTH_FACTOR, coords->velocity)) +
328                               coords->velocity *
329                               MIN (SMOOTH_FACTOR, coords->velocity));
330 
331           /* Speed needs upper limit */
332           coords->velocity = MIN (coords->velocity, 1.0);
333         }
334 
335       if (((fabs (delta_x) > DIRECTION_RADIUS) &&
336            (fabs (delta_y) > DIRECTION_RADIUS)) ||
337           (buffer->event_history->len < 4))
338         {
339           dir_delta_x = delta_x;
340           dir_delta_y = delta_y;
341         }
342       else
343         {
344           gint x = CLAMP ((buffer->event_history->len - 1), 3, 15);
345 
346           while (((fabs (dir_delta_x) < DIRECTION_RADIUS) ||
347                   (fabs (dir_delta_y) < DIRECTION_RADIUS)) &&
348                  (x >= 0))
349             {
350               last_dir_event = g_array_index (buffer->event_history,
351                                               GimpCoords, x);
352 
353               dir_delta_x = last_dir_event.x - coords->x;
354               dir_delta_y = last_dir_event.y - coords->y;
355 
356               x--;
357             }
358         }
359 
360       if ((fabs (dir_delta_x) < DIRECTION_RADIUS) ||
361           (fabs (dir_delta_y) < DIRECTION_RADIUS))
362         {
363           coords->direction = buffer->last_coords.direction;
364         }
365       else
366         {
367           coords->direction = gimp_coords_direction (&last_dir_event, coords);
368         }
369 
370       coords->direction = coords->direction - floor (coords->direction);
371 
372       delta_dir = coords->direction - buffer->last_coords.direction;
373 
374       if (delta_dir < -0.5)
375         {
376           coords->direction = (0.5 * coords->direction +
377                                0.5 * (buffer->last_coords.direction - 1.0));
378         }
379       else if (delta_dir > 0.5)
380         {
381           coords->direction = (0.5 * coords->direction +
382                                0.5 * (buffer->last_coords.direction + 1.0));
383         }
384       else
385         {
386           coords->direction = (0.5 * coords->direction +
387                                0.5 * buffer->last_coords.direction);
388         }
389 
390       coords->direction = coords->direction - floor (coords->direction);
391 
392       /* do event fill for devices that do not provide enough events */
393       if (distance >= EVENT_FILL_PRECISION &&
394           event_fill                       &&
395           buffer->event_history->len >= 2)
396         {
397           if (buffer->event_delay)
398             {
399               gimp_motion_buffer_interpolate_stroke (buffer, coords);
400             }
401           else
402             {
403               buffer->event_delay = TRUE;
404               gimp_motion_buffer_push_event_history (buffer, coords);
405             }
406         }
407       else
408         {
409           if (buffer->event_delay)
410             buffer->event_delay = FALSE;
411 
412           gimp_motion_buffer_push_event_history (buffer, coords);
413         }
414 
415 #ifdef EVENT_VERBOSE
416       g_printerr ("DIST: %f, DT:%f, Vel:%f, Press:%f,smooth_dd:%f, POS: (%f, %f)\n",
417                   distance,
418                   delta_time,
419                   buffer->last_coords.velocity,
420                   coords->pressure,
421                   distance - dist,
422                   coords->x,
423                   coords->y);
424 #endif
425     }
426 
427   g_array_append_val (buffer->event_queue, *coords);
428 
429   buffer->last_coords            = *coords;
430   buffer->last_motion_time       = time;
431   buffer->last_motion_delta_time = delta_time;
432   buffer->last_motion_delta_x    = delta_x;
433   buffer->last_motion_delta_y    = delta_y;
434   buffer->last_motion_distance   = distance;
435 
436   return TRUE;
437 }
438 
439 guint32
gimp_motion_buffer_get_last_motion_time(GimpMotionBuffer * buffer)440 gimp_motion_buffer_get_last_motion_time (GimpMotionBuffer *buffer)
441 {
442   g_return_val_if_fail (GIMP_IS_MOTION_BUFFER (buffer), 0);
443 
444   return buffer->last_read_motion_time;
445 }
446 
447 void
gimp_motion_buffer_request_stroke(GimpMotionBuffer * buffer,GdkModifierType state,guint32 time)448 gimp_motion_buffer_request_stroke (GimpMotionBuffer *buffer,
449                                    GdkModifierType   state,
450                                    guint32           time)
451 {
452   GdkModifierType  event_state;
453   gint             keep = 0;
454 
455   g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer));
456 
457   if (buffer->event_delay)
458     {
459       /* If we are in delay we use LAST state, not current */
460       event_state = buffer->last_active_state;
461 
462       keep = 1; /* Holding one event in buf */
463     }
464   else
465     {
466       /* Save the state */
467       event_state = state;
468     }
469 
470   if (buffer->event_delay_timeout)
471     {
472       g_source_remove (buffer->event_delay_timeout);
473       buffer->event_delay_timeout = 0;
474     }
475 
476   buffer->last_active_state = state;
477 
478   while (buffer->event_queue->len > keep)
479     {
480       GimpCoords buf_coords;
481 
482       gimp_motion_buffer_pop_event_queue (buffer, &buf_coords);
483 
484       g_signal_emit (buffer, motion_buffer_signals[STROKE], 0,
485                      &buf_coords, time, event_state);
486     }
487 
488   if (buffer->event_delay)
489     {
490       buffer->event_delay_timeout =
491         g_timeout_add (50,
492                        (GSourceFunc) gimp_motion_buffer_event_queue_timeout,
493                        buffer);
494     }
495 }
496 
497 void
gimp_motion_buffer_request_hover(GimpMotionBuffer * buffer,GdkModifierType state,gboolean proximity)498 gimp_motion_buffer_request_hover (GimpMotionBuffer *buffer,
499                                   GdkModifierType   state,
500                                   gboolean          proximity)
501 {
502   g_return_if_fail (GIMP_IS_MOTION_BUFFER (buffer));
503 
504   if (buffer->event_queue->len > 0)
505     {
506       GimpCoords buf_coords = g_array_index (buffer->event_queue,
507                                              GimpCoords,
508                                              buffer->event_queue->len - 1);
509 
510       g_signal_emit (buffer, motion_buffer_signals[HOVER], 0,
511                      &buf_coords, state, proximity);
512 
513       g_array_set_size (buffer->event_queue, 0);
514     }
515 }
516 
517 
518 /*  private functions  */
519 
520 static void
gimp_motion_buffer_push_event_history(GimpMotionBuffer * buffer,const GimpCoords * coords)521 gimp_motion_buffer_push_event_history (GimpMotionBuffer *buffer,
522                                        const GimpCoords *coords)
523 {
524   if (buffer->event_history->len == 4)
525     g_array_remove_index (buffer->event_history, 0);
526 
527   g_array_append_val (buffer->event_history, *coords);
528 }
529 
530 static void
gimp_motion_buffer_pop_event_queue(GimpMotionBuffer * buffer,GimpCoords * coords)531 gimp_motion_buffer_pop_event_queue (GimpMotionBuffer *buffer,
532                                     GimpCoords       *coords)
533 {
534   *coords = g_array_index (buffer->event_queue, GimpCoords, 0);
535 
536   g_array_remove_index (buffer->event_queue, 0);
537 }
538 
539 static void
gimp_motion_buffer_interpolate_stroke(GimpMotionBuffer * buffer,GimpCoords * coords)540 gimp_motion_buffer_interpolate_stroke (GimpMotionBuffer *buffer,
541                                        GimpCoords       *coords)
542 {
543   GimpCoords  catmull[4];
544   GArray     *ret_coords;
545   gint        i = buffer->event_history->len - 1;
546 
547   /* Note that there must be exactly one event in buffer or bad things
548    * can happen. This must never get called under other circumstances.
549    */
550   ret_coords = g_array_new (FALSE, FALSE, sizeof (GimpCoords));
551 
552   catmull[0] = g_array_index (buffer->event_history, GimpCoords, i - 1);
553   catmull[1] = g_array_index (buffer->event_history, GimpCoords, i);
554   catmull[2] = g_array_index (buffer->event_queue,   GimpCoords, 0);
555   catmull[3] = *coords;
556 
557   gimp_coords_interpolate_catmull (catmull, EVENT_FILL_PRECISION / 2,
558                                    ret_coords, NULL);
559 
560   /* Push the last actual event in history */
561   gimp_motion_buffer_push_event_history (buffer,
562                                          &g_array_index (buffer->event_queue,
563                                                          GimpCoords, 0));
564 
565   g_array_set_size (buffer->event_queue, 0);
566 
567   g_array_append_vals (buffer->event_queue,
568                        &g_array_index (ret_coords, GimpCoords, 0),
569                        ret_coords->len);
570 
571   g_array_free (ret_coords, TRUE);
572 }
573 
574 static gboolean
gimp_motion_buffer_event_queue_timeout(GimpMotionBuffer * buffer)575 gimp_motion_buffer_event_queue_timeout (GimpMotionBuffer *buffer)
576 {
577   buffer->event_delay         = FALSE;
578   buffer->event_delay_timeout = 0;
579 
580   if (buffer->event_queue->len > 0)
581     {
582       GimpCoords last_coords = g_array_index (buffer->event_queue,
583                                               GimpCoords,
584                                               buffer->event_queue->len - 1);
585 
586       gimp_motion_buffer_push_event_history (buffer, &last_coords);
587 
588       gimp_motion_buffer_request_stroke (buffer,
589                                          buffer->last_active_state,
590                                          buffer->last_read_motion_time);
591     }
592 
593   return FALSE;
594 }
595