From bba5dd047edbc7ffb589d08e5ba49b07e55fd550 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 13 Aug 2023 11:07:58 -0400 Subject: [PATCH] contour: Add length<>point api --- gsk/gskcontour.c | 156 ++++++++++++++++++++++++++++++++++++++-- gsk/gskcontourprivate.h | 8 +++ 2 files changed, 160 insertions(+), 4 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index fd79e28c71..51cb4a755f 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -82,6 +82,13 @@ struct _GskContourClass gboolean emit_move_to, GskRealPathPoint *start, GskRealPathPoint *end); + void (* get_point) (const GskContour *contour, + float distance, + float precision, + GskRealPathPoint *result); + float (* get_distance) (const GskContour *contour, + GskRealPathPoint *point); + float (* get_length) (const GskContour *contour); }; /* {{{ Utilities */ @@ -119,9 +126,13 @@ struct _GskStandardContour GskPathFlags flags; + float length; + gsize has_length; + gsize n_ops; gsize n_points; graphene_point_t *points; + float *lengths; gskpathop ops[]; }; @@ -134,6 +145,7 @@ gsk_standard_contour_compute_size (gsize n_ops, G_ALIGNOF (GskStandardContour))); gsize s = sizeof (GskStandardContour) + sizeof (gskpathop) * n_ops + + sizeof (float) * n_ops + sizeof (graphene_point_t) * n_points; return s + (align - (s % align)); } @@ -164,6 +176,29 @@ gsk_standard_contour_get_size (const GskContour *contour) return gsk_standard_contour_compute_size (self->n_ops, self->n_points); } +static void +gsk_standard_contour_ensure_lengths (const GskContour *contour) +{ + GskStandardContour *self = (GskStandardContour *) contour; + + if (g_once_init_enter (&self->has_length)) + { + GskCurve curve; + + self->lengths[0] = 0; + + for (gsize i = 1; i < self->n_ops; i++) + { + gsk_curve_init (&curve, self->ops[i]); + self->lengths[i] = self->lengths[i - 1] + gsk_curve_get_length (&curve); + } + + self->length = self->lengths[self->n_ops - 1]; + + g_once_init_leave (&self->has_length, 1); + } +} + static gboolean gsk_standard_contour_foreach (const GskContour *contour, float tolerance, @@ -633,6 +668,91 @@ gsk_standard_contour_add_segment (const GskContour *contour, } } +static void +gsk_standard_contour_get_point (const GskContour *contour, + float distance, + float precision, + GskRealPathPoint *result) +{ + const GskStandardContour *self = (const GskStandardContour *) contour; + gsize i, i0, i1; + + gsk_standard_contour_ensure_lengths (contour); + + distance = CLAMP (distance, 0, self->length); + + i0 = 0; + i1 = self->n_ops - 1; + while (i0 + 1 < i1) + { + i = (i0 + i1) / 2; + + if (self->lengths[i] < distance) + i0 = i; + else + i1 = i; + } + + g_assert (self->lengths[i0] <= distance && distance <= self->lengths[i1]); + + if (distance >= self->lengths[i1]) + { + if (i1 == self->n_ops - 1) + { + result->idx = i1; + result->t = 1; + } + else + { + result->idx = i1 + 1; + result->t = 0; + } + } + else + { + GskCurve curve; + + gsk_curve_init (&curve, self->ops[i1]); + + result->idx = i1; + result->t = gsk_curve_at_length (&curve, distance - self->lengths[i1 - 1], precision); + } +} + +static float +gsk_standard_contour_get_distance (const GskContour *contour, + GskRealPathPoint *point) +{ + const GskStandardContour *self = (const GskStandardContour *) contour; + GskCurve curve, curve1; + + if (G_UNLIKELY (point->idx == 0)) + return 0; + + gsk_standard_contour_ensure_lengths (contour); + + if (point->t == 0) + return self->lengths[point->idx - 1]; + else if (point->t == 1) + return self->lengths[point->idx]; + else + { + gsk_curve_init (&curve, self->ops[point->idx]); + gsk_curve_split (&curve, point->t, &curve1, NULL); + return self->lengths[point->idx - 1] + gsk_curve_get_length (&curve1); + } +} + +static float +gsk_standard_contour_get_length (const GskContour *contour) +{ + const GskStandardContour *self = (const GskStandardContour *) contour; + + gsk_standard_contour_ensure_lengths (contour); + + return self->length; +} + static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = { sizeof (GskStandardContour), @@ -653,6 +773,9 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_get_tangent, gsk_standard_contour_get_curvature, gsk_standard_contour_add_segment, + gsk_standard_contour_get_point, + gsk_standard_contour_get_distance, + gsk_standard_contour_get_length, }; /* You must ensure the contour has enough size allocated, @@ -677,14 +800,17 @@ gsk_standard_contour_init (GskContour *contour, self->n_ops = n_ops; self->n_points = n_points; self->points = (graphene_point_t *) &self->ops[n_ops]; + self->lengths = (float *) &self->points[n_points]; memcpy (self->points, points, sizeof (graphene_point_t) * n_points); + memset (self->lengths, 0, sizeof (float) * n_ops); offset += self->points - points; for (i = 0; i < n_ops; i++) - { - self->ops[i] = gsk_pathop_encode (gsk_pathop_op (ops[i]), - gsk_pathop_points (ops[i]) + offset); - } + self->ops[i] = gsk_pathop_encode (gsk_pathop_op (ops[i]), + gsk_pathop_points (ops[i]) + offset); + + self->length = 0.f; + self->has_length = 0; } GskContour * @@ -840,6 +966,28 @@ gsk_contour_add_segment (const GskContour *self, self->klass->add_segment (self, builder, emit_move_to, start, end); } +void +gsk_contour_get_point (const GskContour *self, + float distance, + float precision, + GskRealPathPoint *result) +{ + self->klass->get_point (self, distance, precision, result); +} + +float +gsk_contour_get_distance (const GskContour *self, + GskRealPathPoint *point) +{ + return self->klass->get_distance (self, point); +} + +float +gsk_contour_get_length (const GskContour *self) +{ + return self->klass->get_length (self); +} + /* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index 2e5bf7ee5d..a9f4f07dba 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -79,5 +79,13 @@ void gsk_contour_add_segment (const GskContou GskRealPathPoint *start, GskRealPathPoint *end); +void gsk_contour_get_point (const GskContour *self, + float distance, + float precision, + GskRealPathPoint *result); +float gsk_contour_get_distance (const GskContour *self, + GskRealPathPoint *point); +float gsk_contour_get_length (const GskContour *self); + G_END_DECLS