From 4aca0358b40dd4666bab3280096d6e8b3f45dd9f Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 18 Nov 2020 05:15:28 +0100 Subject: path: Add gsk_path_foreach() --- gsk/gskenums.h | 22 +++++++++++ gsk/gskpath.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++---- gsk/gskpath.h | 23 ++++++++++++ gsk/gskpathprivate.h | 3 ++ 4 files changed, 142 insertions(+), 8 deletions(-) diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 98d5afd505..44e8854874 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -195,6 +195,28 @@ typedef enum { GSK_FILL_RULE_EVEN_ODD } GskFillRule; +/** + * GskPathOperation: + * @GSK_PATH_MOVE: A move-to operation, with 1 point describing the + * target point. + * @GSK_PATH_LINE: A line-to operation, with 2 points describing the + * start and end point of a straight line. + * @GSK_PATH_CLOSE: A close operation ending the current contour with + * a line back to the starting point. Two points describe the start + * and end of the line. + * @GSK_PATH_CURVE: A curve-to operation describing a cubic bezier curve + * with 4 points describing the start point, the two control points + * and the end point of the curve. + * + * Path operations can be used to approximate a #GskPath. + **/ +typedef enum { + GSK_PATH_MOVE, + GSK_PATH_CLOSE, + GSK_PATH_LINE, + GSK_PATH_CURVE, +} GskPathOperation; + /** * GskSerializationError: * @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be diff --git a/gsk/gskpath.c b/gsk/gskpath.c index b69dd44fb8..c79ba541db 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -62,6 +62,10 @@ struct _GskContourClass cairo_t *cr); gboolean (* get_bounds) (const GskContour *contour, graphene_rect_t *bounds); + gboolean (* foreach) (const GskContour *contour, + float tolerance, + GskPathForeachFunc func, + gpointer user_data); gpointer (* init_measure) (const GskContour *contour, float *out_length); void (* free_measure) (const GskContour *contour, @@ -157,6 +161,29 @@ gsk_rect_contour_get_bounds (const GskContour *contour, return TRUE; } +static gboolean +gsk_rect_contour_foreach (const GskContour *contour, + float tolerance, + GskPathForeachFunc func, + gpointer user_data) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + graphene_point_t pts[] = { + GRAPHENE_POINT_INIT (self->x, self->y), + GRAPHENE_POINT_INIT (self->x + self->width, self->y), + GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height), + GRAPHENE_POINT_INIT (self->x, self->y + self->height), + GRAPHENE_POINT_INIT (self->x, self->y) + }; + + return func (GSK_PATH_MOVE, &pts[0], 1, user_data) + && func (GSK_PATH_LINE, &pts[0], 2, user_data) + && func (GSK_PATH_LINE, &pts[1], 2, user_data) + && func (GSK_PATH_LINE, &pts[2], 2, user_data) + && func (GSK_PATH_CLOSE, &pts[3], 2, user_data); +} + static gpointer gsk_rect_contour_init_measure (const GskContour *contour, float *out_length) @@ -258,6 +285,7 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS = gsk_rect_contour_print, gsk_rect_contour_to_cairo, gsk_rect_contour_get_bounds, + gsk_rect_contour_foreach, gsk_rect_contour_init_measure, gsk_rect_contour_free_measure, gsk_rect_contour_copy, @@ -282,14 +310,6 @@ gsk_rect_contour_init (GskContour *contour, /* STANDARD CONTOUR */ -typedef enum -{ - GSK_PATH_MOVE, - GSK_PATH_CLOSE, - GSK_PATH_LINE, - GSK_PATH_CURVE, -} GskPathOperation; - typedef struct _GskStandardOperation GskStandardOperation; struct _GskStandardOperation { @@ -327,6 +347,30 @@ gsk_standard_contour_get_size (const GskContour *contour) return gsk_standard_contour_compute_size (self->n_ops, self->n_points); } +static gboolean +gsk_standard_contour_foreach (const GskContour *contour, + float tolerance, + GskPathForeachFunc func, + gpointer user_data) +{ + const GskStandardContour *self = (const GskStandardContour *) contour; + gsize i; + const gsize n_points[] = { + [GSK_PATH_MOVE] = 1, + [GSK_PATH_CLOSE] = 2, + [GSK_PATH_LINE] = 2, + [GSK_PATH_CURVE] = 4 + }; + + for (i = 0; i < self->n_ops; i ++) + { + if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data)) + return FALSE; + } + + return TRUE; +} + static GskPathFlags gsk_standard_contour_get_flags (const GskContour *contour) { @@ -627,6 +671,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_print, gsk_standard_contour_to_cairo, gsk_standard_contour_get_bounds, + gsk_standard_contour_foreach, gsk_standard_contour_init_measure, gsk_standard_contour_free_measure, gsk_standard_contour_copy, @@ -682,6 +727,15 @@ gsk_contour_dup (const GskContour *src) return copy; } +static gboolean +gsk_contour_foreach (const GskContour *contour, + float tolerance, + GskPathForeachFunc func, + gpointer user_data) +{ + return contour->klass->foreach (contour, tolerance, func, user_data); +} + gpointer gsk_contour_init_measure (GskPath *path, gsize i, @@ -1012,6 +1066,38 @@ gsk_path_get_bounds (GskPath *self, return TRUE; } +/** + * gsk_path_foreach: + * @self: a #GskPath + * @func: (scope call) (closure user_data): the function to call for operations + * @user_data: (nullable): user data passed to @func + * + * Calls @func for every operation of the path. Note that this only approximates + * @self, because paths can contain optimizations for various specialized contours. + * + * Returns: %FALSE if @func returned %FALSE, %TRUE otherwise. + **/ +gboolean +gsk_path_foreach (GskPath *self, + GskPathForeachFunc func, + gpointer user_data) +{ + gsize i; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (func, FALSE); + + for (i = 0; i < self->n_contours; i++) + { + if (!gsk_contour_foreach (self->contours[i], GSK_PATH_TOLERANCE_DEFAULT, func, user_data)) + return FALSE; + } + + return TRUE; +} + +/* BUILDER */ + /** * GskPathBuilder: * diff --git a/gsk/gskpath.h b/gsk/gskpath.h index 1be4ec189f..8011aba4eb 100644 --- a/gsk/gskpath.h +++ b/gsk/gskpath.h @@ -29,6 +29,24 @@ G_BEGIN_DECLS +/** + * GskPathForeachFunc: + * @op: The operation to perform + * @pts: The points of the operation + * @n_pts: The number of points + * @user_data: The user data provided with the function + * + * Prototype of the callback to iterate throught the operations of + * a path. + * + * Returns: %TRUE to continue evaluating the path, %FALSE to + * immediately abort and not call the function again. + */ +typedef gboolean (* GskPathForeachFunc) (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + gpointer user_data); + #define GSK_TYPE_PATH (gsk_path_get_type ()) GDK_AVAILABLE_IN_ALL @@ -61,6 +79,11 @@ GDK_AVAILABLE_IN_ALL gboolean gsk_path_get_bounds (GskPath *path, graphene_rect_t *bounds); +GDK_AVAILABLE_IN_ALL +gboolean gsk_path_foreach (GskPath *path, + GskPathForeachFunc func, + gpointer user_data); + #define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ()) typedef struct _GskPathBuilder GskPathBuilder; diff --git a/gsk/gskpathprivate.h b/gsk/gskpathprivate.h index 7238c44418..2e47ac465f 100644 --- a/gsk/gskpathprivate.h +++ b/gsk/gskpathprivate.h @@ -25,6 +25,9 @@ G_BEGIN_DECLS +/* Same as Cairo, so looks like a good value. ¯\_(ツ)_/¯ */ +#define GSK_PATH_TOLERANCE_DEFAULT (0.1) + gsize gsk_path_get_n_contours (GskPath *path); gpointer gsk_contour_init_measure (GskPath *path, -- cgit v1.2.1