From 51e2658a25324a12b44e4fdafb2e684ed4a5987e Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Sat, 3 Oct 2026 20:56:46 +0000 Subject: [PATCH] vello_cpu: use isolated clip layers for non-rect clips --- crates/anyrender_vello_cpu/src/scene.rs | 42 ++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/crates/anyrender_vello_cpu/src/scene.rs b/crates/anyrender_vello_cpu/src/scene.rs index 58de81c..fb6751d 100644 --- a/crates/anyrender_vello_cpu/src/scene.rs +++ b/crates/anyrender_vello_cpu/src/scene.rs @@ -117,16 +117,12 @@ impl PaintScene for VelloCpuScenePainter { if let Some(rect) = clip.as_rect() { self.layer_stack.push(LayerKind::Clip); self.render_ctx.push_clip_rect(&rect); - } else if self.render_ctx.is_multi_threaded() { - // The multi-threaded dispatcher flushes its pending work and rasterizes the clip on - // the main thread for every `push_clip_path`, which is slower than an isolated layer. + } else { + // An isolated layer (rather than `push_clip_path`) so that overlapping draws are + // anti-aliased against the clip edge once, instead of once per draw. self.layer_stack.push(LayerKind::Layer); self.render_ctx .push_clip_layer(&clip.into_path(DEFAULT_TOLERANCE)); - } else { - self.layer_stack.push(LayerKind::Clip); - self.render_ctx - .push_clip_path(&clip.into_path(DEFAULT_TOLERANCE)); } } @@ -316,6 +312,38 @@ mod clip_rule_tests { assert_pixels(true, false); } + #[test] + fn overlapping_fills_in_path_clip_are_antialiased_once() { + let render = |fills: usize| { + render_to_buffer::( + |scene| { + scene.push_clip_layer( + Fill::NonZero, + Affine::IDENTITY, + &kurbo::Circle::new((50.0, 50.0), 40.3), + ); + for _ in 0..fills { + scene.fill( + Fill::NonZero, + Affine::IDENTITY, + RED, + None, + &Rect::new(0.0, 0.0, 100.0, 100.0), + ); + } + scene.pop_layer(); + }, + 100, + 100, + ) + }; + let once = render(1); + let twice = render(3); + let edge = (50 * 100 + 90) * 4; + assert!(once[edge + 3] > 0 && once[edge + 3] < 255); + assert_eq!(once, twice); + } + #[test] fn rect_clips_and_fills() { let buffer = render_to_buffer::(