[gimp] Bug 768872 - Tiling Symmetry doesn't wrap vertical coordinates over...



commit 1993d8a4a78fbd424a535ed31be9e5b36f37f8f9
Author: Jehan <jehan girinstud io>
Date:   Wed Apr 11 02:09:21 2018 +0200

    Bug 768872 - Tiling Symmetry doesn't wrap vertical coordinates over...
    
    ... edges for MyPaint brush.
    
    Adding the concept of "stateful" symmetry when a tool needs to make sure
    of corresponding stroke numbers and orders while painting (i.e. stroke N
    at time T+1 is the continuation of stroke N at time T). This is the case
    for the MyPaint brushes and the ink tool.

 app/core/gimpsymmetry-tiling.c |  117 ++++++++++++++++++++++++++++++----------
 app/core/gimpsymmetry.c        |   27 +++++++++
 app/core/gimpsymmetry.h        |    3 +
 app/paint/gimpink.c            |    2 +
 app/paint/gimpmybrushcore.c    |    2 +
 5 files changed, 123 insertions(+), 28 deletions(-)
---
diff --git a/app/core/gimpsymmetry-tiling.c b/app/core/gimpsymmetry-tiling.c
index 7b519f8..dfd2db3 100644
--- a/app/core/gimpsymmetry-tiling.c
+++ b/app/core/gimpsymmetry-tiling.c
@@ -324,45 +324,106 @@ gimp_tiling_update_strokes (GimpSymmetry *sym,
   width  = gimp_item_get_width (GIMP_ITEM (drawable));
   height = gimp_item_get_height (GIMP_ITEM (drawable));
 
-  if (origin->x > 0 && tiling->max_x == 0 && tiling->interval_x >= 1.0)
-    startx = fmod (origin->x, tiling->interval_x) - tiling->interval_x;
-
-  if (origin->y > 0 && tiling->max_y == 0 && tiling->interval_y >= 1.0)
+  if (sym->stateful)
     {
-      starty = fmod (origin->y, tiling->interval_y) - tiling->interval_y;
+      /* While I can compute exactly the right number of strokes to
+       * paint on-canvas for stateless tools, stateful tools need to
+       * always have the same number and order of strokes. For this
+       * reason, I compute strokes to fill 2 times the width and height.
+       * This makes the symmetry less efficient with stateful tools, but
+       * also weird behavior may happen if you decide to paint out of
+       * canvas and expect tiling to work in-canvas since it won't
+       * actually be infinite (as no new strokes can be added while
+       * painting since we are stateful).
+       */
+      gint i, j;
+
+      if (tiling->interval_x < 1.0)
+        {
+          x_count = 1;
+        }
+      else if (tiling->max_x == 0)
+        {
+          x_count = (gint) ceil (width / tiling->interval_x);
+          startx -= tiling->interval_x * (gdouble) x_count;
+          x_count = 2 * x_count + 1;
+        }
+      else
+        {
+          x_count = tiling->max_x;
+        }
 
-      if (tiling->shift > 0.0)
-        startx -= tiling->shift * floor (origin->y / tiling->interval_y + 1);
-    }
+      if (tiling->interval_y < 1.0)
+        {
+          y_count = 1;
+        }
+      else if (tiling->max_y == 0)
+        {
+          y_count = (gint) ceil (height / tiling->interval_y);
+          starty -= tiling->interval_y * (gdouble) y_count;
+          y_count = 2 * y_count + 1;
+        }
+      else
+        {
+          y_count = tiling->max_y;
+        }
+
+      for (i = 0, x = startx; i < x_count; i++)
+        {
+          for (j = 0, y = starty; j < y_count; j++)
+            {
+              coords = g_memdup (origin, sizeof (GimpCoords));
+              coords->x = x;
+              coords->y = y;
+              strokes = g_list_prepend (strokes, coords);
 
-  for (y_count = 0, y = starty; y < height + tiling->interval_y;
-       y_count++, y += tiling->interval_y)
+              y += tiling->interval_y;
+            }
+          x += tiling->interval_x;
+        }
+    }
+  else
     {
-      if (tiling->max_y && y_count >= tiling->max_y)
-        break;
+      if (origin->x > 0 && tiling->max_x == 0 && tiling->interval_x >= 1.0)
+        startx = fmod (origin->x, tiling->interval_x) - tiling->interval_x;
 
-      for (x_count = 0, x = startx; x < width + tiling->interval_x;
-           x_count++, x += tiling->interval_x)
+      if (origin->y > 0 && tiling->max_y == 0 && tiling->interval_y >= 1.0)
         {
-          if (tiling->max_x && x_count >= tiling->max_x)
-            break;
+          starty = fmod (origin->y, tiling->interval_y) - tiling->interval_y;
 
-          coords = g_memdup (origin, sizeof (GimpCoords));
-          coords->x = x;
-          coords->y = y;
-          strokes = g_list_prepend (strokes, coords);
+          if (tiling->shift > 0.0)
+            startx -= tiling->shift * floor (origin->y / tiling->interval_y + 1);
+        }
 
-          if (tiling->interval_x < 1.0)
+      for (y_count = 0, y = starty; y < height + tiling->interval_y;
+           y_count++, y += tiling->interval_y)
+        {
+          if (tiling->max_y && y_count >= tiling->max_y)
             break;
-        }
 
-      if (tiling->max_x || startx + tiling->shift <= 0.0)
-        startx = startx + tiling->shift;
-      else
-        startx = startx - tiling->interval_x + tiling->shift;
+          for (x_count = 0, x = startx; x < width + tiling->interval_x;
+               x_count++, x += tiling->interval_x)
+            {
+              if (tiling->max_x && x_count >= tiling->max_x)
+                break;
 
-      if (tiling->interval_y < 1.0)
-        break;
+              coords = g_memdup (origin, sizeof (GimpCoords));
+              coords->x = x;
+              coords->y = y;
+              strokes = g_list_prepend (strokes, coords);
+
+              if (tiling->interval_x < 1.0)
+                break;
+            }
+
+          if (tiling->max_x || startx + tiling->shift <= 0.0)
+            startx = startx + tiling->shift;
+          else
+            startx = startx - tiling->interval_x + tiling->shift;
+
+          if (tiling->interval_y < 1.0)
+            break;
+        }
     }
 
   sym->strokes = strokes;
diff --git a/app/core/gimpsymmetry.c b/app/core/gimpsymmetry.c
index c4090f8..d2d875a 100644
--- a/app/core/gimpsymmetry.c
+++ b/app/core/gimpsymmetry.c
@@ -273,6 +273,33 @@ gimp_symmetry_real_update_version (GimpSymmetry *symmetry)
 /***** Public Functions *****/
 
 /**
+ * gimp_symmetry_set_stateful:
+ * @sym:      the #GimpSymmetry
+ * @stateful: whether the symmetry should be stateful or stateless.
+ *
+ * By default, symmetry is made stateless, which means in particular
+ * that the size of points can change from one stroke to the next, and
+ * in particular you cannot map the coordinates from a stroke to the
+ * next. I.e. stroke N at time T+1 is not necessarily the continuation
+ * of stroke N at time T.
+ * To obtain corresponding strokes, stateful tools, such as MyPaint
+ * brushes or the ink tool, need to run this function. They should reset
+ * to stateless behavior once finished painting.
+ *
+ * One of the first consequence of being stateful is that the number of
+ * strokes cannot be changed, so more strokes than possible on canvas
+ * may be computed, and oppositely it will be possible to end up in
+ * cases with missing strokes (e.g. a tiling, theoretically infinite,
+ * won't be for the ink tool if one draws too far out of canvas).
+ **/
+void
+gimp_symmetry_set_stateful (GimpSymmetry *symmetry,
+                            gboolean      stateful)
+{
+  symmetry->stateful = stateful;
+}
+
+/**
  * gimp_symmetry_set_origin:
  * @sym:      the #GimpSymmetry
  * @drawable: the #GimpDrawable where painting will happen
diff --git a/app/core/gimpsymmetry.h b/app/core/gimpsymmetry.h
index 7e2de45..9cd0317 100644
--- a/app/core/gimpsymmetry.h
+++ b/app/core/gimpsymmetry.h
@@ -50,6 +50,7 @@ struct _GimpSymmetry
   gint          version;
 
   GList        *strokes;
+  gboolean      stateful;
 };
 
 struct _GimpSymmetryClass
@@ -74,6 +75,8 @@ struct _GimpSymmetryClass
 
 GType          gimp_symmetry_get_type       (void) G_GNUC_CONST;
 
+void           gimp_symmetry_set_stateful   (GimpSymmetry       *symmetry,
+                                             gboolean            stateful);
 void           gimp_symmetry_set_origin     (GimpSymmetry       *symmetry,
                                              GimpDrawable       *drawable,
                                              GimpCoords         *origin);
diff --git a/app/paint/gimpink.c b/app/paint/gimpink.c
index 3a3e6d8..32af20a 100644
--- a/app/paint/gimpink.c
+++ b/app/paint/gimpink.c
@@ -168,6 +168,7 @@ gimp_ink_paint (GimpPaintCore    *paint_core,
           GimpContext *context = GIMP_CONTEXT (paint_options);
           GimpRGB      foreground;
 
+          gimp_symmetry_set_stateful (sym, TRUE);
           gimp_context_get_foreground (context, &foreground);
           gimp_palettes_add_color_history (context->gimp,
                                            &foreground);
@@ -217,6 +218,7 @@ gimp_ink_paint (GimpPaintCore    *paint_core,
       break;
 
     case GIMP_PAINT_STATE_FINISH:
+      gimp_symmetry_set_stateful (sym, FALSE);
       break;
     }
 }
diff --git a/app/paint/gimpmybrushcore.c b/app/paint/gimpmybrushcore.c
index eb65057..52ef69e 100644
--- a/app/paint/gimpmybrushcore.c
+++ b/app/paint/gimpmybrushcore.c
@@ -212,6 +212,7 @@ gimp_mybrush_core_paint (GimpPaintCore    *paint_core,
     case GIMP_PAINT_STATE_INIT:
       gimp_context_get_foreground (context, &fg);
       gimp_palettes_add_color_history (context->gimp, &fg);
+      gimp_symmetry_set_stateful (sym, TRUE);
 
       mybrush->private->surface =
         gimp_mypaint_surface_new (gimp_drawable_get_buffer (drawable),
@@ -233,6 +234,7 @@ gimp_mybrush_core_paint (GimpPaintCore    *paint_core,
       break;
 
     case GIMP_PAINT_STATE_FINISH:
+      gimp_symmetry_set_stateful (sym, FALSE);
       mypaint_surface_unref ((MyPaintSurface *) mybrush->private->surface);
       mybrush->private->surface = NULL;
 


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