From a715e69640920c044b25ee18a4e50f0add7651ca Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 21 Jul 2023 23:35:14 -0400 Subject: [PATCH] Add gsk_path_get_stroke_bounds This is a help to compute the bounds for stroke nodes. We keep it private for now. --- gsk/gskcontour.c | 106 ++++++++++++++++++++++++++++++++++++++++ gsk/gskcontourprivate.h | 3 ++ gsk/gskpath.c | 50 +++++++++++++++++++ gsk/gskpathprivate.h | 3 ++ 4 files changed, 162 insertions(+) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 1adfd61a31..3ea2c447d0 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -26,6 +26,7 @@ #include "gskpathprivate.h" #include "gskpathpointprivate.h" #include "gsksplineprivate.h" +#include "gskstrokeprivate.h" typedef struct _GskContourClass GskContourClass; @@ -47,6 +48,9 @@ struct _GskContourClass GString *string); gboolean (* get_bounds) (const GskContour *contour, graphene_rect_t *bounds); + gboolean (* get_stroke_bounds) (const GskContour *contour, + const GskStroke *stroke, + graphene_rect_t *bounds); void (* get_start_end) (const GskContour *self, graphene_point_t *start, graphene_point_t *end); @@ -360,6 +364,55 @@ gsk_standard_contour_get_bounds (const GskContour *contour, return bounds->size.width > 0 && bounds->size.height > 0; } +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 void gsk_standard_contour_get_start_end (const GskContour *contour, graphene_point_t *start, @@ -855,6 +908,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_get_flags, gsk_standard_contour_print, gsk_standard_contour_get_bounds, + gsk_standard_contour_get_stroke_bounds, gsk_standard_contour_get_start_end, gsk_standard_contour_foreach, gsk_standard_contour_reverse, @@ -977,6 +1031,17 @@ gsk_rect_contour_get_bounds (const GskContour *contour, return TRUE; } +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 void gsk_rect_contour_get_start_end (const GskContour *contour, graphene_point_t *start, @@ -1396,6 +1461,7 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS = gsk_rect_contour_get_flags, gsk_rect_contour_print, gsk_rect_contour_get_bounds, + gsk_rect_contour_get_stroke_bounds, gsk_rect_contour_get_start_end, gsk_rect_contour_foreach, gsk_rect_contour_reverse, @@ -1507,6 +1573,23 @@ gsk_circle_contour_get_bounds (const GskContour *contour, return TRUE; } +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 void gsk_circle_contour_get_start_end (const GskContour *contour, graphene_point_t *start, @@ -1834,6 +1917,7 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS = gsk_circle_contour_get_flags, gsk_circle_contour_print, gsk_circle_contour_get_bounds, + gsk_circle_contour_get_stroke_bounds, gsk_circle_contour_get_start_end, gsk_circle_contour_foreach, gsk_circle_contour_reverse, @@ -1993,6 +2077,19 @@ gsk_rounded_rect_contour_get_bounds (const GskContour *contour, return TRUE; } +static gboolean +gsk_rounded_rect_contour_get_stroke_bounds (const GskContour *contour, + const GskStroke *stroke, + graphene_rect_t *bounds) +{ + const GskRoundedRectContour *self = (const GskRoundedRectContour *) contour; + + graphene_rect_init_from_rect (bounds, &self->rect.bounds); + graphene_rect_inset (bounds, - stroke->line_width / 2, - stroke->line_width / 2); + + return TRUE; +} + static void gsk_rounded_rect_contour_get_start_end (const GskContour *contour, graphene_point_t *start, @@ -2313,6 +2410,7 @@ static const GskContourClass GSK_ROUNDED_RECT_CONTOUR_CLASS = gsk_rounded_rect_contour_get_flags, gsk_rounded_rect_contour_print, gsk_rounded_rect_contour_get_bounds, + gsk_rounded_rect_contour_get_stroke_bounds, gsk_rounded_rect_contour_get_start_end, gsk_rounded_rect_contour_foreach, gsk_rounded_rect_contour_reverse, @@ -2395,6 +2493,14 @@ gsk_contour_get_bounds (const GskContour *self, return self->klass->get_bounds (self, bounds); } +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); +} + gboolean gsk_contour_foreach (const GskContour *self, float tolerance, diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index 1ee36d3a0d..8dd8bdd29b 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -58,6 +58,9 @@ void gsk_contour_print (const GskContou GString *string); gboolean gsk_contour_get_bounds (const GskContour *self, graphene_rect_t *bounds); +gboolean gsk_contour_get_stroke_bounds (const GskContour *self, + const GskStroke *stroke, + graphene_rect_t *bounds); gboolean gsk_contour_foreach (const GskContour *self, float tolerance, GskPathForeachFunc func, diff --git a/gsk/gskpath.c b/gsk/gskpath.c index a62b681c92..9d8accdb90 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -407,6 +407,56 @@ gsk_path_get_bounds (GskPath *self, return TRUE; } +/*< private > + * 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; +} + /** * gsk_path_in_fill: * @self: a `GskPath` diff --git a/gsk/gskpathprivate.h b/gsk/gskpathprivate.h index 34262a8d6e..84c098fae2 100644 --- a/gsk/gskpathprivate.h +++ b/gsk/gskpathprivate.h @@ -57,6 +57,9 @@ void gsk_path_builder_svg_arc_to (GskPathBuilder float x, float y); +gboolean gsk_path_get_stroke_bounds (GskPath *self, + const GskStroke *stroke, + graphene_rect_t *bounds); G_END_DECLS