[gtk/path-work-rebased: 48/123] stroke: Add support for dashes
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/path-work-rebased: 48/123] stroke: Add support for dashes
- Date: Thu, 7 Apr 2022 20:40:56 +0000 (UTC)
commit 697e4e46e11728f0a2b07d125aef41bf90a2b084
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 | 138 ++++++++++++++++++++++++++++++++++++++++++++++
gsk/gskstroke.h | 15 +++++
gsk/gskstrokeprivate.h | 9 +++
4 files changed, 243 insertions(+)
---
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 501a9f3c65..146b03fd81 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 4b0256f911..bb60684324 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);
}
/**
@@ -162,6 +176,18 @@ gsk_stroke_equal (gconstpointer stroke1,
self1->miter_limit != self2->miter_limit)
return FALSE;
+ if (self1->n_dash != self2->n_dash)
+ return FALSE;
+
+ for (int i = 0; i < self1->n_dash; i++)
+ {
+ if (self1->dash[i] != self2->dash[i])
+ return FALSE;
+ }
+
+ if (self1->dash_offset != self2->dash_offset)
+ return FALSE;
+
return TRUE;
}
@@ -309,3 +335,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]