From 4d4f4791e4855c8db14c42d1929ce0545bd28bf2 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 17 Dec 2020 06:55:54 +0100 Subject: [PATCH] path: Change semantics of gtk_path_builder_add_segment() Allow start >= end to mean that the path continues at the beginning after reaching the end until it reaches the point at @end. --- gsk/gskcontour.c | 28 ++++++++---- gsk/gskcontourprivate.h | 1 + gsk/gskpathmeasure.c | 99 ++++++++++++++++++++++++++++------------- testsuite/gsk/path.c | 23 ++++++++-- 4 files changed, 106 insertions(+), 45 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 95cf8fcec8..122ce4967d 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -76,6 +76,7 @@ struct _GskContourClass void (* add_segment) (const GskContour *contour, GskPathBuilder *builder, gpointer measure_data, + gboolean emit_move_to, float start, float end); int (* get_winding) (const GskContour *contour, @@ -395,6 +396,7 @@ static void gsk_rect_contour_add_segment (const GskContour *contour, GskPathBuilder *builder, gpointer measure_data, + gboolean emit_move_to, float start, float end) { @@ -404,7 +406,8 @@ gsk_rect_contour_add_segment (const GskContour *contour, if (start < w) { - gsk_path_builder_move_to (builder, self->x + start * (w / self->width), self->y); + if (emit_move_to) + gsk_path_builder_move_to (builder, self->x + start * (w / self->width), self->y); if (end <= w) { gsk_path_builder_line_to (builder, self->x + end * (w / self->width), self->y); @@ -417,7 +420,7 @@ gsk_rect_contour_add_segment (const GskContour *contour, if (start < h) { - if (start >= 0) + if (start >= 0 && emit_move_to) gsk_path_builder_move_to (builder, self->x + self->width, self->y + start * (h / self->height)); if (end <= h) { @@ -431,7 +434,7 @@ gsk_rect_contour_add_segment (const GskContour *contour, if (start < w) { - if (start >= 0) + if (start >= 0 && emit_move_to) gsk_path_builder_move_to (builder, self->x + (w - start) * (w / self->width), self->y + self->height); if (end <= w) { @@ -445,7 +448,7 @@ gsk_rect_contour_add_segment (const GskContour *contour, if (start < h) { - if (start >= 0) + if (start >= 0 && emit_move_to) gsk_path_builder_move_to (builder, self->x, self->y + (h - start) * (h / self->height)); if (end <= h) { @@ -776,6 +779,7 @@ static void gsk_circle_contour_add_segment (const GskContour *contour, GskPathBuilder *builder, gpointer measure_data, + gboolean emit_move_to, float start, float end) { @@ -784,6 +788,8 @@ gsk_circle_contour_add_segment (const GskContour *contour, float length = self->radius * DEG_TO_RAD (delta); GskContour *segment; + if (!emit_move_to) + g_warning ("FIXME: somebody needs to decompose contours into segments differently"); segment = gsk_circle_contour_new (&self->center, self->radius, self->start_angle + start/length * delta, self->start_angle + end/length * delta); @@ -1340,6 +1346,7 @@ static void gsk_standard_contour_add_segment (const GskContour *contour, GskPathBuilder *builder, gpointer measure_data, + gboolean emit_move_to, float start, float end) { @@ -1391,7 +1398,8 @@ gsk_standard_contour_add_segment (const GskContour *contour, { gsk_curve_segment (&curve, start_progress, end_progress, &cut); start_point = gsk_curve_get_start_point (&cut); - gsk_path_builder_move_to (builder, start_point->x, start_point->y); + if (emit_move_to) + gsk_path_builder_move_to (builder, start_point->x, start_point->y); gsk_curve_builder_to (&cut, builder); return; } @@ -1399,12 +1407,13 @@ gsk_standard_contour_add_segment (const GskContour *contour, gsk_curve_split (&curve, start_progress, NULL, &cut); start_point = gsk_curve_get_start_point (&cut); - gsk_path_builder_move_to (builder, start_point->x, start_point->y); + if (emit_move_to) + gsk_path_builder_move_to (builder, start_point->x, start_point->y); gsk_curve_builder_to (&cut, builder); i = start_measure->op + 1; } - else - i = 0; + else + i = emit_move_to ? 0 : 1; for (; i < (end_measure ? end_measure->op : self->n_ops - 1); i++) { @@ -1718,10 +1727,11 @@ void gsk_contour_add_segment (const GskContour *self, GskPathBuilder *builder, gpointer measure_data, + gboolean emit_move_to, float start, float end) { - self->klass->add_segment (self, builder, measure_data, start, end); + self->klass->add_segment (self, builder, measure_data, emit_move_to, start, end); } int diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index d9d80b898d..927205fdac 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -90,6 +90,7 @@ int gsk_contour_get_winding (const GskContou void gsk_contour_add_segment (const GskContour *self, GskPathBuilder *builder, gpointer measure_data, + gboolean emit_move_to, float start, float end); gboolean gsk_contour_get_stroke_bounds (const GskContour *self, diff --git a/gsk/gskpathmeasure.c b/gsk/gskpathmeasure.c index f15a566ba2..f2cd2e93a4 100644 --- a/gsk/gskpathmeasure.c +++ b/gsk/gskpathmeasure.c @@ -530,40 +530,16 @@ gsk_path_measure_in_fill (GskPathMeasure *self, } } - -/** - * gsk_path_builder_add_segment: - * @self: a #GskPathBuilder - * @measure: the #GskPathMeasure to take the segment to - * @start: start distance into the path - * @end: end distance into the path - * - * Adds to @self the segment of @measure inbetween @start and @end. - * - * The distances are given relative to the length of @measure's path, - * from 0 for the beginning of the path to - * gsk_path_measure_get_length() for the end of the path. The values - * will be clamped to that range. - * - * If @start >= @end after clamping, no path will be added. - **/ -void -gsk_path_builder_add_segment (GskPathBuilder *self, - GskPathMeasure *measure, - float start, - float end) +static void +gsk_path_builder_add_segment_chunk (GskPathBuilder *self, + GskPathMeasure *measure, + gboolean emit_move_to, + float start, + float end) { - gsize i; + g_assert (start < end); - g_return_if_fail (self != NULL); - g_return_if_fail (measure != NULL); - - start = gsk_path_measure_clamp_distance (measure, start); - end = gsk_path_measure_clamp_distance (measure, end); - if (start >= end) - return; - - for (i = measure->first; i < measure->last; i++) + for (gsize i = measure->first; i < measure->last; i++) { if (measure->measures[i].length < start) { @@ -576,6 +552,7 @@ gsk_path_builder_add_segment (GskPathBuilder *self, gsk_contour_add_segment (gsk_path_get_contour (measure->path, i), self, measure->measures[i].contour_data, + emit_move_to, start, len); end -= len; @@ -588,6 +565,64 @@ gsk_path_builder_add_segment (GskPathBuilder *self, end -= measure->measures[i].length; gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i))); } + emit_move_to = TRUE; + } +} + +/** + * gsk_path_builder_add_segment: + * @self: a #GskPathBuilder + * @measure: the #GskPathMeasure to take the segment to + * @start: start distance into the path + * @end: end distance into the path + * + * Adds to @self the segment of @measure from @start to @end. + * + * The distances are given relative to the length of @measure's path, + * from 0 for the beginning of the path to + * gsk_path_measure_get_length() for the end of the path. The values + * will be clamped to that range. + * + * If @start >= @end after clamping, the path will first add the segment + * from @start to the end of the path, and then add the segment from + * the beginning to @end. If the path is closed, these segments will + * be connected. + **/ +void +gsk_path_builder_add_segment (GskPathBuilder *self, + GskPathMeasure *measure, + float start, + float end) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (measure != NULL); + + start = gsk_path_measure_clamp_distance (measure, start); + end = gsk_path_measure_clamp_distance (measure, end); + + if (start < end) + { + gsk_path_builder_add_segment_chunk (self, measure, TRUE, start, end); + } + else + { + /* If the path is closed, we can connect the 2 subpaths. */ + gboolean closed = gsk_path_measure_is_closed (measure); + gboolean need_move_to = !closed; + + if (start < measure->length) + gsk_path_builder_add_segment_chunk (self, measure, + TRUE, + start, measure->length); + else + need_move_to = TRUE; + + if (end > 0) + gsk_path_builder_add_segment_chunk (self, measure, + need_move_to, + 0, end); + if (start == end && closed) + gsk_path_builder_close (self); } } diff --git a/testsuite/gsk/path.c b/testsuite/gsk/path.c index 58776acef9..8335166c8e 100644 --- a/testsuite/gsk/path.c +++ b/testsuite/gsk/path.c @@ -609,7 +609,10 @@ test_segment_start (void) path1 = gsk_path_builder_free_to_path (builder); measure1 = gsk_path_measure_new (path1); - g_assert_cmpfloat_with_epsilon (seg_length, gsk_path_measure_get_length (measure1), epsilon); + if (seg_length == 0) + g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure), gsk_path_measure_get_length (measure1), epsilon); + else + g_assert_cmpfloat_with_epsilon (seg_length, gsk_path_measure_get_length (measure1), epsilon); gsk_path_measure_unref (measure1); gsk_path_unref (path1); @@ -642,7 +645,10 @@ test_segment_end (void) path1 = gsk_path_builder_free_to_path (builder); measure1 = gsk_path_measure_new (path1); - g_assert_cmpfloat_with_epsilon (seg_length, gsk_path_measure_get_length (measure1), epsilon); + if (seg_length == 0) + g_assert_cmpfloat_with_epsilon (gsk_path_measure_get_length (measure), gsk_path_measure_get_length (measure1), epsilon); + else + g_assert_cmpfloat_with_epsilon (seg_length, gsk_path_measure_get_length (measure1), epsilon); gsk_path_measure_unref (measure1); gsk_path_unref (path1); @@ -655,8 +661,8 @@ test_segment_end (void) static void test_segment_chunk (void) { - GskPath *path, *path1; - GskPathMeasure *measure, *measure1; + GskPath *path, *path1, *path2; + GskPathMeasure *measure, *measure1, *measure2; GskPathBuilder *builder; float epsilon, length; guint i; @@ -677,6 +683,15 @@ test_segment_chunk (void) g_assert_cmpfloat_with_epsilon (length / 2, gsk_path_measure_get_length (measure1), epsilon); + builder = gsk_path_builder_new (); + gsk_path_builder_add_segment (builder, measure, seg_start + length / 2, seg_start); + path2 = gsk_path_builder_free_to_path (builder); + measure2 = gsk_path_measure_new (path2); + + g_assert_cmpfloat_with_epsilon (length / 2, gsk_path_measure_get_length (measure2), epsilon); + + gsk_path_measure_unref (measure2); + gsk_path_unref (path2); gsk_path_measure_unref (measure1); gsk_path_unref (path1); }