[gtk/wip/otte/lottie: 69/79] path: Add a foreach function that dashes a path
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/lottie: 69/79] path: Add a foreach function that dashes a path
- Date: Thu, 24 Dec 2020 00:43:01 +0000 (UTC)
commit c46447b889441baec87b9d7363675030c4204488
Author: Benjamin Otte <otte redhat com>
Date: Thu Dec 10 17:50:27 2020 +0100
path: Add a foreach function that dashes a path
gsk/gskcurve.c | 67 +++++++++++
gsk/gskcurveprivate.h | 5 +
gsk/gskpathdash.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++
gsk/gskpathdashprivate.h | 49 ++++++++
gsk/meson.build | 1 +
5 files changed, 413 insertions(+)
---
diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c
index 9556ad098d..c6a789615e 100644
--- a/gsk/gskcurve.c
+++ b/gsk/gskcurve.c
@@ -29,6 +29,11 @@ struct _GskCurveClass
{
void (* init) (GskCurve *curve,
gskpathop op);
+ void (* init_foreach) (GskCurve *curve,
+ GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight);
void (* get_point) (const GskCurve *curve,
float t,
graphene_point_t *pos);
@@ -88,6 +93,20 @@ gsk_line_curve_init (GskCurve *curve,
gsk_line_curve_init_from_points (self, gsk_pathop_op (op), &pts[0], &pts[1]);
}
+static void
+gsk_line_curve_init_foreach (GskCurve *curve,
+ GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight)
+{
+ GskLineCurve *self = &curve->line;
+
+ g_assert (n_pts == 2);
+
+ gsk_line_curve_init_from_points (self, op, &pts[0], &pts[1]);
+}
+
static void
gsk_line_curve_get_point (const GskCurve *curve,
float t,
@@ -186,6 +205,7 @@ gsk_line_curve_get_start_end_tangent (const GskCurve *curve,
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
gsk_line_curve_init,
+ gsk_line_curve_init_foreach,
gsk_line_curve_get_point,
gsk_line_curve_get_tangent,
gsk_line_curve_split,
@@ -218,6 +238,20 @@ gsk_curve_curve_init (GskCurve *curve,
gsk_curve_curve_init_from_points (self, gsk_pathop_points (op));
}
+static void
+gsk_curve_curve_init_foreach (GskCurve *curve,
+ GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight)
+{
+ GskCurveCurve *self = &curve->curve;
+
+ g_assert (n_pts == 4);
+
+ gsk_curve_curve_init_from_points (self, pts);
+}
+
static void
gsk_curve_curve_ensure_coefficients (const GskCurveCurve *curve)
{
@@ -399,6 +433,7 @@ gsk_curve_curve_get_end_tangent (const GskCurve *curve,
static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
gsk_curve_curve_init,
+ gsk_curve_curve_init_foreach,
gsk_curve_curve_get_point,
gsk_curve_curve_get_tangent,
gsk_curve_curve_split,
@@ -432,6 +467,26 @@ gsk_conic_curve_init (GskCurve *curve,
gsk_conic_curve_init_from_points (self, gsk_pathop_points (op));
}
+static void
+gsk_conic_curve_init_foreach (GskCurve *curve,
+ GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight)
+{
+ GskConicCurve *self = &curve->conic;
+
+ g_assert (n_pts == 3);
+
+ gsk_conic_curve_init_from_points (self,
+ (graphene_point_t[4]) {
+ pts[0],
+ pts[1],
+ GRAPHENE_POINT_INIT (weight, 0),
+ pts[2]
+ });
+}
+
static inline float
gsk_conic_curve_get_weight (const GskConicCurve *self)
{
@@ -793,6 +848,7 @@ gsk_conic_curve_get_end_tangent (const GskCurve *curve,
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_init,
+ gsk_conic_curve_init_foreach,
gsk_conic_curve_get_point,
gsk_conic_curve_get_tangent,
gsk_conic_curve_split,
@@ -829,6 +885,16 @@ gsk_curve_init (GskCurve *curve,
get_class (gsk_pathop_op (op))->init (curve, op);
}
+void
+gsk_curve_init_foreach (GskCurve *curve,
+ GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight)
+{
+ get_class (op)->init_foreach (curve, op, pts, n_pts, weight);
+}
+
void
gsk_curve_get_point (const GskCurve *curve,
float progress,
@@ -909,3 +975,4 @@ gsk_curve_get_end_tangent (const GskCurve *curve,
{
get_class (curve->op)->get_end_tangent (curve, tangent);
}
+
diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h
index d01f520f07..06884346d0 100644
--- a/gsk/gskcurveprivate.h
+++ b/gsk/gskcurveprivate.h
@@ -81,6 +81,11 @@ typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
void gsk_curve_init (GskCurve *curve,
gskpathop op);
+void gsk_curve_init_foreach (GskCurve *curve,
+ GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight);
void gsk_curve_get_point (const GskCurve *curve,
float progress,
diff --git a/gsk/gskpathdash.c b/gsk/gskpathdash.c
new file mode 100644
index 0000000000..15fa3d10db
--- /dev/null
+++ b/gsk/gskpathdash.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright © 2020 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gskpathdashprivate.h"
+
+#include "gskcontourprivate.h"
+#include "gskcurveprivate.h"
+#include "gskpathprivate.h"
+#include "gskstrokeprivate.h"
+
+typedef struct
+{
+ float offset; /* how much of the current dash we've spent */
+ gsize dash_index; /* goes from 0 to n_dash * 2, so we don't have to care about on/off
+ for uneven dashes */
+ gboolean on; /* If we're currently dashing or not */
+ gboolean may_close; /* TRUE if we haven't turned the dash off in this contour */
+ gboolean needs_move_to; /* If we have emitted the initial move_to() yet */
+ enum {
+ NORMAL, /* no special behavior required */
+ SKIP, /* skip the next dash */
+ ONLY, /* only do the first dash */
+ DONE /* done with the first dash */
+ } first_dash_behavior; /* How to handle the first dash in the loop. We loop closed contours
+ twice to make sure the first dash and the last dash can get joined */
+
+ GskCurve curve; /* Curve we are currently processing */
+
+ float collect_start; /* We're collecting multiple line segments when decomposing. */
+ float collect_length; /* No need to emit a curve for every line segment when the dash is long
enough. */
+
+ /* from the stroke */
+ float *dash;
+ gsize n_dash;
+ float dash_length;
+ float dash_offset;
+
+ float tolerance;
+ GskPathForeachFunc func;
+ gpointer user_data;
+} GskPathDash;
+
+static void
+gsk_path_dash_setup (GskPathDash *self)
+{
+ self->offset = fmodf (self->dash_offset, 2 * self->dash_length);
+
+ self->dash_index = 0;
+ self->on = TRUE;
+ self->may_close = TRUE;
+ while (self->offset > self->dash[self->dash_index % self->n_dash])
+ {
+ self->offset -= self->dash[self->dash_index % self->n_dash];
+ self->dash_index++;
+ self->on = !self->on;
+ }
+ if (self->first_dash_behavior != ONLY)
+ self->needs_move_to = TRUE;
+}
+
+static gboolean
+gsk_path_dash_ensure_move_to (GskPathDash *self,
+ const graphene_point_t *pt)
+{
+ if (!self->needs_move_to)
+ return TRUE;
+
+ if (!self->func (GSK_PATH_MOVE, pt, 1, 0, self->user_data))
+ return FALSE;
+
+ self->needs_move_to = FALSE;
+ return TRUE;
+}
+
+static gboolean
+gsk_path_dash_add_line_segment (const graphene_point_t *start,
+ const graphene_point_t *end,
+ float t_start,
+ float t_end,
+ gpointer user_data)
+{
+ GskPathDash *self = user_data;
+ float remaining, length, t_step;
+
+ length = graphene_point_distance (start, end, NULL, NULL);
+ if (self->collect_length)
+ {
+ t_start = self->collect_start;
+ length += self->collect_length;
+ self->collect_length = 0;
+ }
+
+ t_step = t_end - t_start;
+ remaining = length;
+
+ while (remaining)
+ {
+ float piece;
+
+ if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
+ {
+ /* try collecting multiple line segments */
+ if (t_end < 1.0)
+ {
+ self->collect_start = t_start + t_step * (length - remaining) / length;
+ self->collect_length = remaining;
+ return TRUE;
+ }
+
+ piece = remaining;
+ }
+ else
+ piece = self->dash[self->dash_index % self->n_dash] - self->offset;
+
+ if (self->on)
+ {
+ if (self->first_dash_behavior != SKIP)
+ {
+ GskCurve segment;
+
+ if (piece)
+ {
+ gsk_curve_segment (&self->curve,
+ t_start + t_step * (length - remaining) / length,
+ t_start + t_step * (length - (remaining - piece)) / length,
+ &segment);
+ if (!gsk_path_dash_ensure_move_to (self, gsk_curve_get_start_point (&segment)))
+ return FALSE;
+
+ if (!gsk_pathop_foreach (gsk_curve_pathop (&segment), self->func, self->user_data))
+ return FALSE;
+ }
+ else
+ {
+ graphene_point_t p;
+
+ gsk_curve_get_point (&self->curve, t_start + t_step * (length - remaining) / length, &p);
+ if (!gsk_path_dash_ensure_move_to (self, &p))
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ self->may_close = FALSE;
+ if (self->first_dash_behavior == ONLY)
+ {
+ self->first_dash_behavior = DONE;
+ return FALSE;
+ }
+ self->first_dash_behavior = NORMAL;
+ }
+
+ if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
+ {
+ self->offset += remaining;
+ remaining = 0;
+ }
+ else
+ {
+ remaining -= piece;
+ self->offset = 0;
+ self->dash_index++;
+ self->dash_index %= 2 * self->n_dash;
+ self->on = !self->on;
+ self->needs_move_to = TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gsk_path_dash_foreach (GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight,
+ gpointer user_data)
+{
+ GskPathDash *self = user_data;
+
+ switch (op)
+ {
+ case GSK_PATH_MOVE:
+ gsk_path_dash_setup (self);
+ break;
+
+ case GSK_PATH_CLOSE:
+ if (self->may_close)
+ {
+ if (graphene_point_equal (&pts[0], &pts[1]))
+ return self->func (GSK_PATH_CLOSE, pts, 2, 0, self->user_data);
+ }
+ else
+ op = GSK_PATH_LINE;
+ G_GNUC_FALLTHROUGH;
+
+ case GSK_PATH_LINE:
+ case GSK_PATH_CURVE:
+ case GSK_PATH_CONIC:
+ gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight);
+ if (!gsk_curve_decompose (&self->curve, self->tolerance, gsk_path_dash_add_line_segment, self))
+ return FALSE;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsk_contour_dash (const GskContour *contour,
+ GskStroke *stroke,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data)
+{
+ GskPathDash self = {
+ .offset = 0,
+ .dash = stroke->dash,
+ .n_dash = stroke->n_dash,
+ .dash_length = stroke->dash_length,
+ .dash_offset = stroke->dash_offset,
+ .tolerance = tolerance,
+ .func = func,
+ .user_data = user_data
+ };
+ gboolean is_closed = gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
+
+ self.first_dash_behavior = is_closed ? SKIP : NORMAL;
+ if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self))
+ return FALSE;
+
+ if (is_closed)
+ {
+ if (self.first_dash_behavior == NORMAL)
+ self.first_dash_behavior = ONLY;
+ else
+ self.first_dash_behavior = NORMAL;
+ self.needs_move_to = !self.on;
+ if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self) &&
+ self.first_dash_behavior != DONE)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsk_path_dash (GskPath *path,
+ GskStroke *stroke,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data)
+{
+ gsize i;
+
+ /* Dashing disabled, no need to do any work */
+ if (stroke->dash_length <= 0)
+ return gsk_path_foreach (path, -1, func, user_data);
+
+ for (i = 0; i < gsk_path_get_n_contours (path); i++)
+ {
+ if (!gsk_contour_dash (gsk_path_get_contour (path, i), stroke, tolerance, func, user_data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/gsk/gskpathdashprivate.h b/gsk/gskpathdashprivate.h
new file mode 100644
index 0000000000..4e1734f572
--- /dev/null
+++ b/gsk/gskpathdashprivate.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2020 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+
+#ifndef __GSK_PATH_DASH_PRIVATE_H__
+#define __GSK_PATH_DASH_PRIVATE_H__
+
+#include <gsk/gskpath.h>
+
+G_BEGIN_DECLS
+
+gboolean gsk_path_dash (GskPath *path,
+ GskStroke *stroke,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data);
+
+#ifdef GTK_COMPILATION
+
+#include "gskcontourprivate.h"
+
+gboolean gsk_contour_dash (const GskContour *contour,
+ GskStroke *stroke,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data);
+
+#endif /* GTK_COMPILATION */
+
+G_END_DECLS
+
+#endif /* __GSK_PATH_DASH_PRIVATE_H__ */
+
diff --git a/gsk/meson.build b/gsk/meson.build
index 78e3e88876..cfa3e25f10 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -26,6 +26,7 @@ gsk_public_sources = files([
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
+ 'gskpathdash.c',
'gskpathmeasure.c',
'gskrenderer.c',
'gskrendernode.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]