🐛 Fix stroke to path extra points

This commit is contained in:
Aitor Moreno 2026-06-15 16:06:24 +02:00
parent b03537fa68
commit 86a8dcec2c
2 changed files with 30 additions and 22 deletions

View File

@ -156,6 +156,7 @@ impl Path {
let mut current_point = 0;
let mut current_conic = 0;
let mut last_point = skia::Point::new(0.0, 0.0);
let mut subpath_start = skia::Point::new(0.0, 0.0);
for verb in verbs {
match verb {
@ -163,12 +164,15 @@ impl Path {
let p = points[current_point];
segments.push(Segment::MoveTo((p.x, p.y)));
last_point = p;
subpath_start = p;
current_point += 1;
}
skia::PathVerb::Line => {
let p = points[current_point];
segments.push(Segment::LineTo((p.x, p.y)));
last_point = p;
if p != last_point {
segments.push(Segment::LineTo((p.x, p.y)));
last_point = p;
}
current_point += 1;
}
skia::PathVerb::Quad => {
@ -239,6 +243,13 @@ impl Path {
current_point += 3;
}
skia::PathVerb::Close => {
if let Some(Segment::LineTo(p)) = segments.last() {
if (p.0 - subpath_start.x).abs() < 1e-5
&& (p.1 - subpath_start.y).abs() < 1e-5
{
segments.pop();
}
}
segments.push(Segment::Close);
}
}

View File

@ -58,27 +58,24 @@ pub fn stroke_to_path(
// For inner/outer strokes, use boolean ops to clip
// the 2×-width stroke outline to the correct region.
// Set EvenOdd to preserve the annular ring's inner hole,
// then as_winding() on the result fixes contour winding
// for Penpot's NonZero fill rule.
// then switch to Winding for Penpot's NonZero fill rule.
// Use set_fill_type instead of as_winding() because as_winding()
// decomposes self-intersecting geometry, which removes points
// at intersections of straight lines in closed paths.
// Center strokes skip the conversion: fill_path_with_paint
// already produces correctly-wound contours.
let final_path = match render_kind {
StrokeKind::Inner => {
stroke_outline.set_fill_type(skia::PathFillType::EvenOdd);
let inner = stroke_outline
.op(&transformed_shape_path, skia::PathOp::Intersect)
.unwrap_or(stroke_outline);
inner.as_winding().unwrap_or(inner)
}
StrokeKind::Outer => {
stroke_outline.set_fill_type(skia::PathFillType::EvenOdd);
let outer = stroke_outline
.op(&transformed_shape_path, skia::PathOp::Difference)
.unwrap_or(stroke_outline);
outer.as_winding().unwrap_or(outer)
}
StrokeKind::Center => {
stroke_outline.set_fill_type(skia::PathFillType::EvenOdd);
stroke_outline.as_winding().unwrap_or(stroke_outline)
}
StrokeKind::Inner => stroke_outline
.simplify()
.unwrap()
.op(&transformed_shape_path, skia::PathOp::Intersect)
.unwrap_or(stroke_outline),
StrokeKind::Outer => stroke_outline
.simplify()
.unwrap()
.op(&transformed_shape_path, skia::PathOp::Difference)
.unwrap_or(stroke_outline),
StrokeKind::Center => stroke_outline.simplify().unwrap_or(stroke_outline),
};
// If there was a path_transform, invert it back to local coords