From 776dc54c8aafc56fbf0f46ed68b84f72e18f267c 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. --- docs/reference/gsk/gsk4-sections.txt | 1 + gsk/gskcontour.c | 98 ++++++++++++++++++++++++++++++++++-- gsk/gskcontourprivate.h | 3 ++ gsk/gskpath.c | 50 ++++++++++++++++++ gsk/gskpath.h | 4 ++ 5 files changed, 153 insertions(+), 3 deletions(-) diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt index 4a5d8c39c0..cc734745b0 100644 --- a/docs/reference/gsk/gsk4-sections.txt +++ b/docs/reference/gsk/gsk4-sections.txt @@ -330,6 +330,7 @@ gsk_path_to_cairo gsk_path_is_empty gsk_path_get_bounds +gsk_path_get_stroke_bounds GskPathOperation GskPathForeachFlags diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index c7c74e9cff..075482c9a5 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -25,6 +25,7 @@ #include "gskpathbuilder.h" #include "gskpathprivate.h" #include "gsksplineprivate.h" +#include "gskstrokeprivate.h" typedef struct _GskContourClass GskContourClass; @@ -81,6 +82,9 @@ struct _GskContourClass gpointer measure_data, const graphene_point_t *point, gboolean *on_edge); + gboolean (* get_stroke_bounds) (const GskContour *contour, + const GskStroke *stroke, + graphene_rect_t *bounds); }; static gsize @@ -469,6 +473,17 @@ gsk_rect_contour_get_winding (const GskContour *contour, return 0; } +static gboolean +gsk_rect_contour_get_stroke_bounds (const GskContour *contour, + const 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 +500,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 +834,23 @@ gsk_circle_contour_get_winding (const GskContour *contour, return 0; } +static gboolean +gsk_circle_contour_get_stroke_bounds (const GskContour *contour, + const 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 +867,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 +1504,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, + const 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, 1.f) * 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 +1569,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 +1733,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, + const 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..d9d80b898d 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, + const GskStroke *stroke, + graphene_rect_t *bounds); G_END_DECLS diff --git a/gsk/gskpath.c b/gsk/gskpath.c index 034107ac35..fda323992e 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -1168,3 +1168,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, + const 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..6df9250a33 100644 --- a/gsk/gskpath.h +++ b/gsk/gskpath.h @@ -99,6 +99,10 @@ gboolean gsk_path_is_empty (GskPath GDK_AVAILABLE_IN_ALL gboolean gsk_path_get_bounds (GskPath *path, graphene_rect_t *bounds); +GDK_AVAILABLE_IN_ALL +gboolean gsk_path_get_stroke_bounds (GskPath *path, + const GskStroke *stroke, + graphene_rect_t *bounds); GDK_AVAILABLE_IN_ALL gboolean gsk_path_foreach (GskPath *path, -- cgit v1.2.1