[gtk/path-work-rebased: 47/118] stroke: Add support for dashes




commit b2ec557880ce4c1b2aa344107250c52af08fc216
Author: Benjamin Otte <otte redhat com>
Date:   Mon Nov 30 10:05:18 2020 +0100

    stroke: Add support for dashes
    
    ... and hook it up in the node parser and for Cairo rendering.

 gsk/gskrendernodeparser.c |  81 +++++++++++++++++++++++++++++
 gsk/gskstroke.c           | 126 ++++++++++++++++++++++++++++++++++++++++++++++
 gsk/gskstroke.h           |  15 ++++++
 gsk/gskstrokeprivate.h    |   9 ++++
 4 files changed, 231 insertions(+)
---
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 9b68419a19..cbe7c079a6 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -1822,6 +1822,43 @@ clear_path (gpointer inout_path)
   g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
 }
 
+static gboolean
+parse_dash (GtkCssParser *parser,
+            gpointer      out_dash)
+{
+  GArray *dash;
+  double d;
+
+  /* because CSS does this, too */
+  if (gtk_css_parser_try_ident (parser, "none"))
+    {
+      *((GArray **) out_dash) = NULL;
+      return TRUE;
+    }
+
+  dash = g_array_new (FALSE, FALSE, sizeof (float));
+  do {
+    if (!gtk_css_parser_consume_number (parser, &d))
+      {
+        g_array_free (dash, TRUE);
+        return FALSE;
+      }
+
+    g_array_append_vals (dash, (float[1]) { d }, 1);
+  } while (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) ||
+           gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER));
+
+  *((GArray **) out_dash) = dash;
+
+  return TRUE;
+}
+
+static void
+clear_dash (gpointer inout_array)
+{
+  g_clear_pointer ((GArray **) inout_array, g_array_unref);
+}
+
 static gboolean
 parse_enum (GtkCssParser *parser,
             GType         type,
@@ -1915,6 +1952,8 @@ parse_stroke_node (GtkCssParser *parser)
   int line_cap = GSK_LINE_CAP_BUTT;
   int line_join = GSK_LINE_JOIN_MITER;
   double miter_limit = 4.0;
+  GArray *dash = NULL;
+  double dash_offset = 0.0;
   GskStroke *stroke;
 
   const Declaration declarations[] = {
@@ -1924,6 +1963,8 @@ parse_stroke_node (GtkCssParser *parser)
     { "line-cap", parse_line_cap, NULL, &line_cap },
     { "line-join", parse_line_join, NULL, &line_join },
     { "miter-limit", parse_double, NULL, &miter_limit },
+    { "dash", parse_dash, clear_dash, &dash },
+    { "dash-offset", parse_double, NULL, &dash_offset},
   };
   GskRenderNode *result;
 
@@ -1937,6 +1978,12 @@ parse_stroke_node (GtkCssParser *parser)
   gsk_stroke_set_line_cap (stroke, line_cap);
   gsk_stroke_set_line_join (stroke, line_join);
   gsk_stroke_set_miter_limit (stroke, miter_limit);
+  if (dash)
+    {
+      gsk_stroke_set_dash (stroke, (float *) dash->data, dash->len);
+      g_array_free (dash, TRUE);
+    }
+  gsk_stroke_set_dash_offset (stroke, dash_offset);
 
   result = gsk_stroke_node_new (child, path, stroke);
 
@@ -2627,6 +2674,34 @@ append_path_param (Printer    *p,
   g_free (str);
 }
 
+static void
+append_dash_param (Printer     *p,
+                   const char  *param_name,
+                   const float *dash,
+                   gsize        n_dash)
+{
+  _indent (p);
+  g_string_append (p->str, "dash: ");
+
+  if (n_dash == 0)
+    {
+      g_string_append (p->str, "none");
+    }
+  else
+    {
+      gsize i;
+
+      string_append_double (p->str, dash[0]);
+      for (i = 1; i < n_dash; i++)
+        {
+          g_string_append_c (p->str, ' ');
+          string_append_double (p->str, dash[i]);
+        }
+    }
+
+  g_string_append (p->str, ";\n");
+}
+
 static void
 render_node_print (Printer       *p,
                    GskRenderNode *node)
@@ -2794,6 +2869,8 @@ render_node_print (Printer       *p,
     case GSK_STROKE_NODE:
       {
         const GskStroke *stroke;
+        const float *dash;
+        gsize n_dash;
 
         start_node (p, "stroke");
 
@@ -2805,6 +2882,10 @@ render_node_print (Printer       *p,
         append_enum_param (p, "line-cap", GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (stroke));
         append_enum_param (p, "line-join", GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (stroke));
         append_float_param (p, "miter-limit", gsk_stroke_get_miter_limit (stroke), 4.0f);
+        dash = gsk_stroke_get_dash (stroke, &n_dash);
+        if (dash)
+          append_dash_param (p, "dash", dash, n_dash);
+        append_float_param (p, "dash-offset", gsk_stroke_get_dash_offset (stroke), 0.0f);
 
         end_node (p);
       }
diff --git a/gsk/gskstroke.c b/gsk/gskstroke.c
index 5b5f6ca35c..dda45dd65f 100644
--- a/gsk/gskstroke.c
+++ b/gsk/gskstroke.c
@@ -138,6 +138,20 @@ gsk_stroke_to_cairo (const GskStroke *self,
     }
 
   cairo_set_miter_limit (cr, self->miter_limit);
+
+  if (self->dash_length)
+    {
+      gsize i;
+      double *dash = g_newa (double, self->n_dash);
+
+      for (i = 0; i < self->n_dash; i++)
+        {
+          dash[i] = self->dash[i];
+        }
+      cairo_set_dash (cr, dash, self->n_dash, self->dash_offset);
+    }
+  else
+    cairo_set_dash (cr, NULL, 0, 0.0);
 }
 
 /**
@@ -303,3 +317,115 @@ gsk_stroke_get_miter_limit (const GskStroke *self)
 
   return self->miter_limit;
 }
+
+/**
+ * gsk_stroke_set_dash:
+ * @self: a `GskStroke`
+ * @dash: (array length=n_dash) (transfer none) (allow-none): the array of dashes
+ * @n_dash: number of elements in @dash
+ *
+ * Sets the dash pattern to use by this stroke. A dash pattern is specified by
+ * an array of alternating non-negative values. Each value provides the length
+ * of alternate "on" and "off" portions of the stroke.
+ *
+ * Each "on" segment will have caps applied as if the segment were a separate
+ * contour. In particular, it is valid to use an "on" length of 0 with
+ * `GSK_LINE_CAP_ROUND` or `GSK_LINE_CAP_SQUARE` to draw dots or squares along
+ * a path.
+ *
+ * If @n_dash is 0, if all elements in @dash are 0, or if there are negative
+ * values in @dash, then dashing is disabled.
+ *
+ * If @n_dash is 1, an alternating "on" and "off" pattern with the single
+ * dash length provided is assumed.
+ *
+ * If @n_dash is uneven, the dash array will be used with the first element
+ * in @dash defining an "on" or "off" in alternating passes through the array.
+ *
+ * You can specify a starting offset into the dash with [method@Gsk.Stroke.set_dash_offset].
+ **/
+void
+gsk_stroke_set_dash (GskStroke   *self,
+                     const float *dash,
+                     gsize        n_dash)
+{
+  float dash_length;
+  gsize i;
+
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (dash != NULL || n_dash == 0);
+
+  dash_length = 0;
+  for (i = 0; i < n_dash; i++)
+    {
+      if (!(dash[i] >= 0)) /* should catch NaN */
+        {
+          g_critical ("invalid value in dash array at position %zu", i);
+          return;
+        }
+      dash_length += dash[i];
+    }
+
+  self->dash_length = dash_length;
+  g_free (self->dash);
+  self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
+  self->n_dash = n_dash;
+
+}
+
+/**
+ * gsk_stroke_get_dash:
+ * @self: a `GskStroke`
+ * @n_dash: (out caller-allocates): number of elements
+ *   in the array returned
+ *
+ * Gets the dash array in use or %NULL if dashing is disabled.
+ *
+ * Returns: (array length=n_dash) (transfer none) (allow-none):
+ *   The dash array or %NULL if the dash array is empty.
+ **/
+const float *
+gsk_stroke_get_dash (const GskStroke *self,
+                     gsize           *n_dash)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+  g_return_val_if_fail (n_dash != NULL, NULL);
+
+  *n_dash = self->n_dash;
+
+  return self->dash;
+}
+
+/**
+ * gsk_stroke_set_dash_offset:
+ * @self: a `GskStroke`
+ * @offset: offset into the dash pattern
+ *
+ * Sets the offset into the dash pattern set via [method@Gsk.Stroke.set_dash]
+ * where dashing should begin.
+ *
+ * This is an offset into the length of the path, not an index into
+ * the array values of the dash array.
+ **/
+void
+gsk_stroke_set_dash_offset (GskStroke *self,
+                            float      offset)
+{
+  g_return_if_fail (self != NULL);
+
+  self->dash_offset = offset;
+}
+
+/**
+ * gsk_stroke_get_dash_offset:
+ * @self: a `GskStroke`
+ *
+ * Returns the dash offset of a `GskStroke`.
+ */
+float
+gsk_stroke_get_dash_offset (const GskStroke *self)
+{
+  g_return_val_if_fail (self != NULL, 4.f);
+
+  return self->dash_offset;
+}
diff --git a/gsk/gskstroke.h b/gsk/gskstroke.h
index 0605262f3e..233de38f85 100644
--- a/gsk/gskstroke.h
+++ b/gsk/gskstroke.h
@@ -66,6 +66,21 @@ void                    gsk_stroke_set_miter_limit              (GskStroke
 GDK_AVAILABLE_IN_ALL
 float                   gsk_stroke_get_miter_limit              (const GskStroke        *self);
 
+GDK_AVAILABLE_IN_ALL
+void                    gsk_stroke_set_dash                     (GskStroke              *self,
+                                                                 const float            *dash,
+                                                                 gsize                   n_dash);
+GDK_AVAILABLE_IN_ALL
+const float *           gsk_stroke_get_dash                     (const GskStroke        *self,
+                                                                 gsize                  *n_dash);
+
+GDK_AVAILABLE_IN_ALL
+void                    gsk_stroke_set_dash_offset              (GskStroke              *self,
+                                                                 float                   offset);
+GDK_AVAILABLE_IN_ALL
+float                   gsk_stroke_get_dash_offset              (const GskStroke        *self);
+
+
 
 G_END_DECLS
 
diff --git a/gsk/gskstrokeprivate.h b/gsk/gskstrokeprivate.h
index 850356b1ec..24594b1e09 100644
--- a/gsk/gskstrokeprivate.h
+++ b/gsk/gskstrokeprivate.h
@@ -31,6 +31,11 @@ struct _GskStroke
   GskLineCap line_cap;
   GskLineJoin line_join;
   float miter_limit;
+
+  float *dash;
+  gsize n_dash;
+  float dash_length; /* sum of all dashes in the array */
+  float dash_offset;
 };
 
 static inline void
@@ -38,11 +43,15 @@ gsk_stroke_init_copy (GskStroke       *stroke,
                       const GskStroke *other)
 {
   *stroke = *other;
+
+  stroke->dash = g_memdup (other->dash, stroke->n_dash * sizeof (float));
 }
 
 static inline void
 gsk_stroke_clear (GskStroke *stroke)
 {
+  g_clear_pointer (&stroke->dash, g_free);
+  stroke->n_dash = 0; /* better safe than sorry */
 }
 
 void                    gsk_stroke_to_cairo                     (const GskStroke        *self,


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