From d643e7f8f0babbf9c80aeba1fbd82dea7d9bd9f8 Mon Sep 17 00:00:00 2001 From: FredApps Date: Mon, 9 Nov 2026 18:55:46 +0100 Subject: [PATCH] #1388: distinguish fine raster clips and update layer test fixtures --- crates/render/src/pipeline.rs | 8 +++++++- crates/render/src/tests/raster_clip.rs | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/render/src/pipeline.rs b/crates/render/src/pipeline.rs index 5966ebf2e..029438cc5 100644 --- a/crates/render/src/pipeline.rs +++ b/crates/render/src/pipeline.rs @@ -499,7 +499,7 @@ impl<'a> Renderer<'a> { pub(crate) fn clipped_key(&self, ctx: &EvalCtx, layer: &Layer, key: Option) -> Option { let k = key?; Some(match self.raster_clip(ctx, layer) { - Some(c) => [c.x0, c.y0, c.x1, c.y1].iter().fold(cache::derive(k, 0xc11b), |d, v| cache::derive(d, (v * 16.0).round() as i64 as u64)), + Some(c) => raster_clip_key(k, c), None => k, }) } @@ -1209,3 +1209,9 @@ impl<'a> Renderer<'a> { Some(self.keep(key, Arc::new(buf?), &files).0) } } + +/// Snap cells can be finer than 1/16 unit: preserve every coordinate bit so visible +/// clips cannot collide with another cell or the empty behind-camera raster. +pub(crate) fn raster_clip_key(key: u64, clip: kurbo::Rect) -> u64 { + [clip.x0, clip.y0, clip.x1, clip.y1].iter().fold(cache::derive(key, 0xc11b), |key, coordinate| cache::derive(key, coordinate.to_bits())) +} diff --git a/crates/render/src/tests/raster_clip.rs b/crates/render/src/tests/raster_clip.rs index 8bcadb7ee..f7227f634 100644 --- a/crates/render/src/tests/raster_clip.rs +++ b/crates/render/src/tests/raster_clip.rs @@ -190,7 +190,7 @@ fn moving(label: &str, pos: Keys, poi: Keys, text_pos: Keys, text_rot: &[(f64, f set(&mut cam, "cameraOptions/zoom", Value::Scalar(zoom())); keyed(&mut cam, "transform/position", pos); keyed(&mut cam, "transform/poi", poi); - p.comp_mut(cid).unwrap().layers = vec![cam, text]; + p.comp_mut(cid).unwrap().layers = vec![cam, text].into(); let cache = crate::LayerCache::default(); let (mut exact, mut near, mut capped) = (0, 0, 0); for f in 0..30 { @@ -286,7 +286,7 @@ fn layer_space_reads_get_the_whole_layer() { p.next_id = next; g.prop_mut("takeMatteFromLayer").unwrap().value = Value::Layer(Some(text_id.0)); solid.props.sub_mut("effects").unwrap().children.push(g.into()); - p.comp_mut(cid).unwrap().layers = vec![text, solid]; + p.comp_mut(cid).unwrap().layers = vec![text, solid].into(); assert_eq!(frame(&p, cid, false).data, frame(&p, cid, true).data, "Set Matte from the text"); let r = Renderer::new(&p, &NoFootage, RenderOpts::default()); let ctx = r.eval_ctx(cid, Tick::ZERO).unwrap(); @@ -321,3 +321,14 @@ fn layer_panel_input_reads_the_whole_layer_behind_the_camera() { assert!(expected.img.data.iter().any(|pixel| pixel[3] > 0.5)); assert_eq!(r.layer_input(&ctx, layer, 0).unwrap().img, expected.img); } + +#[test] +fn fine_clip_cells_have_distinct_cache_keys() { + let a = kurbo::Rect::new(0.0, 0.0, 1.0 / 128.0, 1.0 / 128.0); + let b = kurbo::Rect::new(1.0 / 128.0, 0.0, 2.0 / 128.0, 1.0 / 128.0); + let key = |clip| crate::pipeline::raster_clip_key(42, clip); + // All three clips rounded to identical coordinates in the old 1/16-unit key. + assert_ne!(key(a), key(b)); + assert_ne!(key(a), key(kurbo::Rect::ZERO)); + assert_ne!(key(b), key(kurbo::Rect::ZERO)); +}