From 95476ffe1b6032348f0f2876c8568904a3619c64 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 5 Dec 2020 19:23:33 -0500 Subject: Add gsk_path_get_stroke_bounds A relatively cheap way to get bounds for the area that would be affected by stroking a path. --- gsk/gskcontour.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++-- gsk/gskcontourprivate.h | 3 ++ gsk/gskpath.c | 50 +++++++++++++++++++++++++ gsk/gskpath.h | 5 +++ 4 files changed, 152 insertions(+), 3 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index c7c74e9cff..d6d1427ac5 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -81,6 +81,9 @@ struct _GskContourClass gpointer measure_data, const graphene_point_t *point, gboolean *on_edge); + gboolean (* get_stroke_bounds) (const GskContour *contour, + GskStroke *stroke, + graphene_rect_t *bounds); }; static gsize @@ -469,6 +472,17 @@ gsk_rect_contour_get_winding (const GskContour *contour, return 0; } +static gboolean +gsk_rect_contour_get_stroke_bounds (const GskContour *contour, + GskStroke *stroke, + graphene_rect_t *bounds) +{ + const GskRectContour *self = (const GskRectContour *) contour; + graphene_rect_init (bounds, self->x, self->y, self->width, self->height); + graphene_rect_inset (bounds, - stroke->line_width / 2, - stroke->line_width / 2); + return TRUE; +} + static const GskContourClass GSK_RECT_CONTOUR_CLASS = { sizeof (GskRectContour), @@ -485,7 +499,8 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS = gsk_rect_contour_get_closest_point, gsk_rect_contour_copy, gsk_rect_contour_add_segment, - gsk_rect_contour_get_winding + gsk_rect_contour_get_winding, + gsk_rect_contour_get_stroke_bounds }; GskContour * @@ -818,6 +833,23 @@ gsk_circle_contour_get_winding (const GskContour *contour, return 0; } +static gboolean +gsk_circle_contour_get_stroke_bounds (const GskContour *contour, + GskStroke *stroke, + graphene_rect_t *bounds) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + graphene_rect_init (bounds, + self->center.x - self->radius, + self->center.y - self->radius, + 2 * self->radius, + 2 * self->radius); + graphene_rect_inset (bounds, - stroke->line_width / 2, - stroke->line_width / 2); + + return TRUE; +} + static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS = { sizeof (GskCircleContour), @@ -834,7 +866,8 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS = gsk_circle_contour_get_closest_point, gsk_circle_contour_copy, gsk_circle_contour_add_segment, - gsk_circle_contour_get_winding + gsk_circle_contour_get_winding, + gsk_circle_contour_get_stroke_bounds }; GskContour * @@ -1470,6 +1503,55 @@ gsk_standard_contour_get_winding (const GskContour *contour, return winding; } +static gboolean +add_stroke_bounds (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight, + gpointer user_data) +{ + struct { + graphene_rect_t *bounds; + float lw; + float mw; + } *data = user_data; + graphene_rect_t bounds; + + for (int i = 1; i < n_pts - 1; i++) + { + graphene_rect_init (&bounds, pts[i].x - data->lw/2, pts[i].y - data->lw/2, data->lw, data->lw); + graphene_rect_union (&bounds, data->bounds, data->bounds); + } + + graphene_rect_init (&bounds, pts[n_pts - 1].x - data->mw/2, pts[n_pts - 1].y - data->mw/2, data->mw, data->mw); + graphene_rect_union (&bounds, data->bounds, data->bounds); + + return TRUE; +} + +static gboolean +gsk_standard_contour_get_stroke_bounds (const GskContour *contour, + GskStroke *stroke, + graphene_rect_t *bounds) +{ + GskStandardContour *self = (GskStandardContour *) contour; + struct { + graphene_rect_t *bounds; + float lw; + float mw; + } data; + + data.bounds = bounds; + data.lw = stroke->line_width; + data.mw = MAX (stroke->miter_limit, 0.5) * stroke->line_width; + + graphene_rect_init (bounds, self->points[0].x - data.mw/2, self->points[0].y - data.mw/2, data.mw, data.mw); + + gsk_standard_contour_foreach (contour, GSK_PATH_TOLERANCE_DEFAULT, add_stroke_bounds, &data); + + return TRUE; +} + static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = { sizeof (GskStandardContour), @@ -1486,7 +1568,8 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_get_closest_point, gsk_standard_contour_copy, gsk_standard_contour_add_segment, - gsk_standard_contour_get_winding + gsk_standard_contour_get_winding, + gsk_standard_contour_get_stroke_bounds }; /* You must ensure the contour has enough size allocated, @@ -1649,6 +1732,14 @@ gsk_contour_get_winding (const GskContour *self, return self->klass->get_winding (self, measure_data, point, on_edge); } +gboolean +gsk_contour_get_stroke_bounds (const GskContour *self, + GskStroke *stroke, + graphene_rect_t *bounds) +{ + return self->klass->get_stroke_bounds (self, stroke, bounds); +} + void gsk_contour_copy (GskContour *dest, const GskContour *src) diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index bce0689d17..4905038f2c 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -92,6 +92,9 @@ void gsk_contour_add_segment (const GskContou gpointer measure_data, float start, float end); +gboolean gsk_contour_get_stroke_bounds (const GskContour *self, + GskStroke *stroke, + graphene_rect_t *bounds); G_END_DECLS diff --git a/gsk/gskpath.c b/gsk/gskpath.c index ce49298473..3eed2a0978 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -1165,3 +1165,53 @@ error: return NULL; } + +/** + * gsk_path_get_stroke_bounds: + * @self: a #GtkPath + * @stroke: stroke parameters + * @bounds: (out) (caller-allocates): the bounds to fill in + * + * Computes the bounds for stroking the given path with the + * parameters in @stroke. + * + * The returned bounds may be larger than necessary, because this + * function aims to be fast, not accurate. The bounds are guaranteed + * to contain the area affected by the stroke, including protrusions + * like miters. + * + * Returns: %TRUE if the path has bounds, %FALSE if the path is known + * to be empty and have no bounds. + */ +gboolean +gsk_path_get_stroke_bounds (GskPath *self, + GskStroke *stroke, + graphene_rect_t *bounds) +{ + gsize i; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (bounds != NULL, FALSE); + + for (i = 0; i < self->n_contours; i++) + { + if (gsk_contour_get_stroke_bounds (self->contours[i], stroke, bounds)) + break; + } + + if (i >= self->n_contours) + { + graphene_rect_init_from_rect (bounds, graphene_rect_zero ()); + return FALSE; + } + + for (i++; i < self->n_contours; i++) + { + graphene_rect_t tmp; + + if (gsk_contour_get_stroke_bounds (self->contours[i], stroke, &tmp)) + graphene_rect_union (bounds, &tmp, bounds); + } + + return TRUE; +} diff --git a/gsk/gskpath.h b/gsk/gskpath.h index 31f4cd10e9..51405f7e5d 100644 --- a/gsk/gskpath.h +++ b/gsk/gskpath.h @@ -106,6 +106,11 @@ gboolean gsk_path_foreach (GskPath GskPathForeachFunc func, gpointer user_data); +GDK_AVAILABLE_IN_ALL +gboolean gsk_path_get_stroke_bounds (GskPath *path, + GskStroke *stroke, + graphene_rect_t *bounds); + G_END_DECLS #endif /* __GSK_PATH_H__ */ -- cgit v1.2.1