From abebd92b190b340df3cf539a3c2d0aee41317a1d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 14 Sep 2023 23:28:57 -0400 Subject: [PATCH] contour: Fixes for rect contours Handle all the special cases (zero width and/or height), and omit 'empty' curves in foreach. --- gsk/gskcontour.c | 157 ++++++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 58 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 8dc159e00c..0ce21c7721 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -378,6 +378,44 @@ contour_add_segment (const GskContour *contour, gsk_contour_foreach (contour, add_segment_cb, &sd); } +static inline gboolean +maybe_emit_line (const graphene_point_t pts[2], + GskPathForeachFunc func, + gpointer user_data) +{ + if (graphene_point_equal (&pts[0], &pts[1])) + return TRUE; + + return func (GSK_PATH_LINE, pts, 2, 0.f, user_data); +} + +/* Assumes a closed contour */ +static void +apply_corner_direction (GskPathDirection direction, + gsize *idx, + float *t, + gsize n_ops) +{ + if (*t == 0 && + (direction == GSK_PATH_FROM_START || direction == GSK_PATH_TO_START)) + { + if (*idx > 1) + *idx = *idx - 1; + else + *idx = n_ops - 1; + *t = 1; + } + else if (*t == 1 && + (direction == GSK_PATH_FROM_END || direction == GSK_PATH_TO_END)) + { + if (*idx < n_ops - 1) + *idx = *idx + 1; + else + *idx = 1; + *t = 0; + } +} + /* }}} */ /* {{{ Default implementations */ @@ -743,7 +781,7 @@ gsk_standard_contour_get_tangent (const GskContour *contour, if (G_UNLIKELY (point->idx == 0)) { - graphene_vec2_init (tangent, 1, 0); + graphene_vec2_init (tangent, 0, 0); return; } @@ -1651,6 +1689,8 @@ struct _GskRectContour float y; float width; float height; + + gsize n_ops; }; static void @@ -1725,9 +1765,9 @@ gsk_rect_contour_foreach (const GskContour *contour, }; return func (GSK_PATH_MOVE, &pts[0], 1, 0.f, user_data) && - func (GSK_PATH_LINE, &pts[0], 2, 0.f, user_data) && - func (GSK_PATH_LINE, &pts[1], 2, 0.f, user_data) && - func (GSK_PATH_LINE, &pts[2], 2, 0.f, user_data) && + maybe_emit_line (&pts[0], func, user_data) && + maybe_emit_line (&pts[1], func, user_data) && + maybe_emit_line (&pts[2], func, user_data) && func (GSK_PATH_CLOSE, &pts[3], 2, 0.f, user_data); } @@ -1765,7 +1805,9 @@ gsk_rect_contour_get_winding (const GskContour *contour, static gsize gsk_rect_contour_get_n_ops (const GskContour *contour) { - return 5; + const GskRectContour *self = (const GskRectContour *) contour; + + return self->n_ops; } static gboolean @@ -1795,31 +1837,14 @@ gsk_rect_contour_get_tangent (const GskContour *contour, GskPathDirection direction, graphene_vec2_t *tangent) { + const GskRectContour *self = (const GskRectContour *) contour; gsize idx = point->idx; float t = point->t; GskCurve curve; - if (t == 0 && - (direction == GSK_PATH_FROM_START || direction == GSK_PATH_TO_START)) - { - if (idx == 1) - idx = 4; - else - idx--; - t = 1; - } - else if (t == 1 && - (direction == GSK_PATH_FROM_END || direction == GSK_PATH_TO_END)) - { - if (idx == 4) - idx = 1; - else - idx++; - t = 0; - } - + apply_corner_direction (direction, &idx, &t, self->n_ops); contour_init_curve (contour, idx, &curve); - gsk_curve_get_tangent (&curve, point->t, tangent); + gsk_curve_get_tangent (&curve, t, tangent); if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END) graphene_vec2_negate (tangent, tangent); } @@ -1861,6 +1886,26 @@ gsk_rect_contour_free_measure (const GskContour *contour, { } +static inline int +rect_contour_get_sides (const GskRectContour *self, + float sides[5]) +{ + int n_sides = 0; + + sides[n_sides++] = 0; + + if (self->width != 0) + sides[n_sides++] = fabsf (self->width); + if (self->height != 0) + sides[n_sides++] = fabsf (self->height); + if (self->width != 0) + sides[n_sides++] = fabsf (self->width); + + sides[n_sides++] = fabsf (self->height); + + return n_sides; +} + static void gsk_rect_contour_get_point (const GskContour *contour, gpointer measure_data, @@ -1868,36 +1913,32 @@ gsk_rect_contour_get_point (const GskContour *contour, GskPathPoint *result) { const GskRectContour *self = (const GskRectContour *) contour; + float sides[5]; + int n_sides = 0; - if (distance <= fabsf (self->width)) + if (distance == 0) { result->idx = 1; - result->t = distance / fabsf (self->width); + result->t = 0; return; } - distance -= fabsf (self->width); + n_sides = rect_contour_get_sides (self, sides); - if (distance <= fabs (self->height)) + for (int i = 0; i < n_sides; i++) { - result->idx = 2; - result->t = distance / fabsf (self->height); - return; + if (distance <= sides[i]) + { + result->idx = i; + result->t = distance / sides[i]; + return; + } + + distance -= sides[i]; } - distance -= fabs (self->height); - - if (distance <= fabsf (self->width)) - { - result->idx = 3; - result->t = distance / fabsf (self->width); - return; - } - - distance -= fabsf (self->width); - - result->idx = 4; - result->t = CLAMP (distance / fabsf (self->height), 0, 1); + result->idx = n_sides - 1; + result->t = 1; } static float @@ -1906,24 +1947,22 @@ gsk_rect_contour_get_distance (const GskContour *contour, gpointer measure_data) { const GskRectContour *self = (const GskRectContour *) contour; + float sides[5]; + int n_sides G_GNUC_UNUSED; + float distance; - switch (point->idx) - { - case 1: - return point->t * fabsf (self->width); + n_sides = rect_contour_get_sides (self, sides); - case 2: - return fabsf (self->width) + point->t * fabsf (self->height); + g_assert (point->idx < n_sides); - case 3: - return (1 + point->t) * fabsf (self->width) + fabsf (self->height); + distance = 0; - case 4: - return 2 * fabsf (self->width) + (1 + point->t) * fabsf (self->height); + for (int i = 0; i < point->idx; i++) + distance += sides[i]; - default: - g_assert_not_reached (); - } + distance += point->t * sides[point->idx]; + + return distance; } static const GskContourClass GSK_RECT_CONTOUR_CLASS = @@ -1955,6 +1994,7 @@ GskContour * gsk_rect_contour_new (const graphene_rect_t *rect) { GskRectContour *self; + gsize n_ops[] = { 2, 3, 5 }; self = g_new0 (GskRectContour, 1); @@ -1964,6 +2004,7 @@ gsk_rect_contour_new (const graphene_rect_t *rect) self->y = rect->origin.y; self->width = rect->size.width; self->height = rect->size.height; + self->n_ops = n_ops[(self->width != 0) + (self->height != 0)]; return (GskContour *) self; }