From 44332033612d0388bbe60f4662750e2f8e975b99 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 23 Sep 2024 10:15:51 -0400 Subject: [PATCH] Make tangents at subpath end watertight (#695) At subpath end, the last path segment is encoded with the end point chosen so that the vector from the current point to the end point is the tangent, ie the same vector as cubic_start_tangent applied to the first segment in the subpath. In those cases, compute the tangent by subtracting those two points, rather than cubic_start_tangent applied to the line. These are mathematically identical, but may give different results because of roundoff. There are two cases: an open subpath, in which case the case is applied to the tangent at draw_start_cap, or a closed subpath, where the logic is in the tangent calculation in read_neighboring_segment. Both GPU and CPU shaders are updated. It hasn't been carefully validated. Hopefully fixes #616 and #650. --- vello_shaders/shader/flatten.wgsl | 7 +++++-- vello_shaders/src/cpu/flatten.rs | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/vello_shaders/shader/flatten.wgsl b/vello_shaders/shader/flatten.wgsl index 1de07910f..cd42fdeb3 100644 --- a/vello_shaders/shader/flatten.wgsl +++ b/vello_shaders/shader/flatten.wgsl @@ -795,7 +795,10 @@ fn read_neighboring_segment(ix: u32) -> NeighboringSegment { let is_closed = (tag.tag_byte & PATH_TAG_SEG_TYPE) == PATH_TAG_LINETO; let is_stroke_cap_marker = (tag.tag_byte & PATH_TAG_SUBPATH_END) != 0u; let do_join = !is_stroke_cap_marker || is_closed; - let tangent = cubic_start_tangent(pts.p0, pts.p1, pts.p2, pts.p3); + var tangent = pts.p3 - pts.p0; + if !is_stroke_cap_marker { + tangent = cubic_start_tangent(pts.p0, pts.p1, pts.p2, pts.p3); + } return NeighboringSegment(do_join, tangent); } @@ -844,7 +847,7 @@ fn main( if is_stroke_cap_marker { if is_open { // Draw start cap - let tangent = cubic_start_tangent(pts.p0, pts.p1, pts.p2, pts.p3); + let tangent = pts.p3 - pts.p0; let offset_tangent = offset * normalize(tangent); let n = offset_tangent.yx * vec2f(-1., 1.); draw_cap(path_ix, (style_flags & STYLE_FLAGS_START_CAP_MASK) >> 2u, diff --git a/vello_shaders/src/cpu/flatten.rs b/vello_shaders/src/cpu/flatten.rs index bd501d396..11bbd94e6 100644 --- a/vello_shaders/src/cpu/flatten.rs +++ b/vello_shaders/src/cpu/flatten.rs @@ -645,7 +645,11 @@ fn read_neighboring_segment( let is_closed = (tag.tag_byte & PATH_TAG_SEG_TYPE) == PATH_TAG_LINETO; let is_stroke_cap_marker = (tag.tag_byte & PathTag::SUBPATH_END_BIT) != 0; let do_join = !is_stroke_cap_marker || is_closed; - let tangent = cubic_start_tangent(pts.p0, pts.p1, pts.p2, pts.p3); + let tangent = if is_stroke_cap_marker { + pts.p3 - pts.p0 + } else { + cubic_start_tangent(pts.p0, pts.p1, pts.p2, pts.p3) + }; NeighboringSegment { do_join, tangent } } @@ -704,7 +708,7 @@ fn flatten_main( if is_stroke_cap_marker { if is_open { // Draw start cap - let tangent = cubic_start_tangent(pts.p0, pts.p1, pts.p2, pts.p3); + let tangent = pts.p3 - pts.p0; let offset_tangent = offset * tangent.normalize(); let n = Vec2::new(-offset_tangent.y, offset_tangent.x); draw_cap(