diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 62a6389f04..31b974f964 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -77,6 +77,11 @@ struct _GskContourClass float (* get_curvature) (const GskContour *contour, GskRealPathPoint *point, graphene_point_t *center); + void (* add_segment) (const GskContour *contour, + GskPathBuilder *builder, + gboolean emit_move_to, + GskRealPathPoint *start, + GskRealPathPoint *end); }; /* {{{ Utilities */ @@ -551,6 +556,71 @@ gsk_standard_contour_get_curvature (const GskContour *contour, return gsk_curve_get_curvature (&curve, point->t, center); } +static void +add_curve (GskCurve *curve, + GskPathBuilder *builder, + gboolean *emit_move_to) +{ + if (*emit_move_to) + { + const graphene_point_t *s; + + s = gsk_curve_get_start_point (curve); + gsk_path_builder_move_to (builder, s->x, s->y); + *emit_move_to = FALSE; + } + gsk_curve_builder_to (curve, builder); +} + +static void +gsk_standard_contour_add_segment (const GskContour *contour, + GskPathBuilder *builder, + gboolean emit_move_to, + GskRealPathPoint *start, + GskRealPathPoint *end) +{ + GskStandardContour *self = (GskStandardContour *) contour; + GskCurve c, c1, c2; + + gsk_curve_init (&c, self->ops[start->idx]); + + if (start->idx == end->idx) + { + gsk_curve_segment (&c, start->t, end->t, &c1); + add_curve (&c1, builder, &emit_move_to); + return; + } + if (start->t == 0) + { + add_curve (&c, builder, &emit_move_to); + } + else if (start->t < 1) + { + gsk_curve_split (&c, start->t, &c1, &c2); + add_curve (&c2, builder, &emit_move_to); + } + + for (gsize i = start->idx + 1; i < end->idx; i++) + { + gsk_curve_init (&c, self->ops[i]); + add_curve (&c, builder, &emit_move_to); + } + + gsk_curve_init (&c, self->ops[end->idx]); + if (c.op == GSK_PATH_CLOSE) + c.op = GSK_PATH_LINE; + + if (end->t == 1) + { + add_curve (&c, builder, &emit_move_to); + } + else if (end->t > 0) + { + gsk_curve_split (&c, end->t, &c1, &c2); + add_curve (&c, builder, &emit_move_to); + } +} + static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = { sizeof (GskStandardContour), @@ -570,6 +640,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_get_position, gsk_standard_contour_get_tangent, gsk_standard_contour_get_curvature, + gsk_standard_contour_add_segment, }; /* You must ensure the contour has enough size allocated, @@ -747,6 +818,16 @@ gsk_contour_get_curvature (const GskContour *self, return self->klass->get_curvature (self, point, center); } +void +gsk_contour_add_segment (const GskContour *self, + GskPathBuilder *builder, + gboolean emit_move_to, + GskRealPathPoint *start, + GskRealPathPoint *end) +{ + self->klass->add_segment (self, builder, emit_move_to, start, end); +} + /* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index 93f4ad9584..7d1b8880df 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -73,5 +73,11 @@ void gsk_contour_get_tangent (const GskContou float gsk_contour_get_curvature (const GskContour *self, GskRealPathPoint *point, graphene_point_t *center); +void gsk_contour_add_segment (const GskContour *self, + GskPathBuilder *builder, + gboolean emit_move_to, + GskRealPathPoint *start, + GskRealPathPoint *end); + G_END_DECLS diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c index 7fc6591d88..08388a3d9b 100644 --- a/gsk/gskpathbuilder.c +++ b/gsk/gskpathbuilder.c @@ -983,3 +983,77 @@ gsk_path_builder_add_layout (GskPathBuilder *self, cairo_destroy (cr); cairo_surface_destroy (surface); } + +/** + * gsk_path_builder_add_segment: + * @self: a `GskPathBuilder` + * @path: the `GskPath` to take the segment to + * @start: the point on @path to start at + * @end: the point on @path to end at + * + * Adds to @self the segment of @path from @start to @end. + * + * If @start is equal to or after @end, 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. + * + * Note that this method always adds a path with the given start point + * and end point. To add a closed path, use [method@Gsk.PathBuilder.add_path]. + * + * Since: 4.14 + */ +void +gsk_path_builder_add_segment (GskPathBuilder *self, + GskPath *path, + const GskPathPoint *start, + const GskPathPoint *end) +{ + GskRealPathPoint *s = (GskRealPathPoint *) start; + GskRealPathPoint *e = (GskRealPathPoint *) end; + const GskContour *contour; + gsize n_contours = gsk_path_get_n_contours (path); + + g_return_if_fail (self != NULL); + g_return_if_fail (path != NULL); + g_return_if_fail (start != NULL); + g_return_if_fail (end != NULL); + g_return_if_fail (s->contour < n_contours); + g_return_if_fail (e->contour < n_contours); + + contour = gsk_path_get_contour (path, s->contour); + + if (s->contour == e->contour) + { + if (gsk_path_point_compare (start, end) < 0) + { + gsk_contour_add_segment (contour, self, TRUE, s, e); + goto out; + } + else if (n_contours == 1) + { + gsk_contour_add_segment (contour, self, TRUE, + s, + &(GskRealPathPoint) { s->contour, gsk_contour_get_n_points (contour) - 1, 1 }); + gsk_contour_add_segment (contour, self, FALSE, + &(GskRealPathPoint) { s->contour, 1, 0 }, + e); + goto out; + } + } + + gsk_contour_add_segment (contour, self, TRUE, + s, + &(GskRealPathPoint) { s->contour, gsk_contour_get_n_points (contour) - 1, 1 }); + + for (gsize i = (s->contour + 1) % n_contours; i != e->contour; i = (i + 1) % n_contours) + gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (path, i))); + + contour = gsk_path_get_contour (path, e->contour); + gsk_contour_add_segment (contour, self, FALSE, + &(GskRealPathPoint) { e->contour, 1, 0 }, + e); + +out: + gsk_path_builder_end_current (self); +} diff --git a/gsk/gskpathbuilder.h b/gsk/gskpathbuilder.h index 8e32564dc3..dc5ae03444 100644 --- a/gsk/gskpathbuilder.h +++ b/gsk/gskpathbuilder.h @@ -68,7 +68,11 @@ GDK_AVAILABLE_IN_4_14 void gsk_path_builder_add_circle (GskPathBuilder *self, const graphene_point_t *center, float radius); - +GDK_AVAILABLE_IN_4_14 +void gsk_path_builder_add_segment (GskPathBuilder *self, + GskPath *path, + const GskPathPoint *start, + const GskPathPoint *end); GDK_AVAILABLE_IN_4_14 void gsk_path_builder_move_to (GskPathBuilder *self, float x,