diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index 47e301666..54948c8dd 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -766,6 +766,9 @@ impl<'a> Renderer<'a> { top: false, through: [None; MAX_THROUGH], through_lost: false, + // Effects read other layers through it in layer space (layer parameters, Echo…): + // they need the whole layer, not the part this frame shows (see `raster_clip`). + whole_raster: true, ..*self } } @@ -1573,7 +1576,8 @@ impl<'a> Renderer<'a> { /// (what the motion tracker analyses, like After Effects' Layer panel). `None` for layers /// without pixels. pub fn layer_source(&self, ctx: &EvalCtx, layer: &Layer) -> Option { - self.source(ctx, layer) + // The whole layer, not the part the frame shows (see `raster_clip`). + Renderer { whole_raster: true, ..*self }.source(ctx, layer) } /// Layer pixels after source → masks → effects (no layer styles). diff --git a/crates/render/src/tests_raster_clip.rs b/crates/render/src/tests_raster_clip.rs index 74da0e616..da0b40884 100644 --- a/crates/render/src/tests_raster_clip.rs +++ b/crates/render/src/tests_raster_clip.rs @@ -268,6 +268,34 @@ fn a_camera_moving_any_way_renders_every_frame_as_the_whole_layer() { moving("text turning near the camera", &[(0.0, [cx, cy, z])], &still, &[(0.0, [cx, cy, z + 70.0])], &[(0.0, -70.0), (1.0, 70.0)]); } +/// Reads of a layer in its own space get the whole layer, not the part the frame shows: an effect +/// taking another layer as a parameter (Set Matte, stretched to fit, from hidden text close to the +/// camera), and the source the motion tracker analyses. +#[test] +fn layer_space_reads_get_the_whole_layer() { + let (mut p, cid, comp) = setup(); + let mut text = text3(&mut p, &comp, [W as f64 / 2.0, H as f64 / 2.0, -zoom() + 40.0], 0.0); + text.switches.video = false; + let text_id = text.id; + let sid = + p.add_item("Solid", Label::Red, None, ItemKind::Solid(effectcraft_project::Solid { color: [0.2, 0.6, 1.0], width: W, height: H, pixel_aspect: 1.0 })); + let mut solid = build::layer(&mut p, &comp, "Solid", LayerSource::Solid { item: sid }, (W, H), None); + let spec = effectcraft_effects::find("ec.channel.setmatte").unwrap(); + let mut next = p.next_id; + let mut g = effectcraft_effects::instantiate(spec, &mut Ids(&mut next), spec.name, [W as f64, H as f64]); + 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]; + 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(); + let layer = &p.comp(cid).unwrap().layers[0]; + let mut whole = Renderer::new(&p, &NoFootage, RenderOpts::default()); + whole.whole_raster = true; + assert_eq!(r.layer_source(&ctx, layer).unwrap().img, whole.layer_source(&ctx, layer).unwrap().img, "the tracker's source"); +} + /// A layer whose pixels outside the frame can matter (an effect here) is rasterised whole. #[test] fn layers_with_effects_rasterise_whole() {