mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-27 19:47:12 -06:00
Track matte on an adjustment layer limits where its effects show (fixes #1201)
This commit is contained in:
+20
-13
@@ -1945,13 +1945,26 @@ impl<'a> Renderer<'a> {
|
||||
/// [`Self::composite_iso`] with the track matte's pixels already drawn (`matte`, e.g. a 3D
|
||||
/// matte seen through the camera); otherwise the matte layer is placed in 2D.
|
||||
pub(crate) fn composite_iso_with(&self, ctx: &EvalCtx, layer: &Layer, mut iso: Image, canvas: &mut Image, opacity: f32, matte_img: Option<Image>) {
|
||||
self.apply_track_matte(ctx, layer, &mut iso, matte_img);
|
||||
if layer.preserve_transparency {
|
||||
iso.data.par_iter_mut().zip(canvas.data.par_iter()).for_each(|(p, q)| {
|
||||
for c in p.iter_mut() {
|
||||
*c *= q[3];
|
||||
}
|
||||
});
|
||||
}
|
||||
canvas.blend_from(&iso, layer.blend_mode, opacity, layer.id.0 as u32);
|
||||
}
|
||||
|
||||
/// Multiply `img` (comp-sized) by the layer's track matte, if it has one; `matte_img` as in
|
||||
/// [`Self::composite_iso_with`].
|
||||
fn apply_track_matte(&self, ctx: &EvalCtx, layer: &Layer, img: &mut Image, matte_img: Option<Image>) {
|
||||
let matte = layer.track_matte.and_then(|tm| ctx.comp.layer(tm.layer).filter(|m| m.id != layer.id).map(|m| (m, tm.kind)));
|
||||
let preserve = layer.preserve_transparency;
|
||||
if let Some((m, kind)) = matte {
|
||||
let mimg = match matte_img {
|
||||
Some(i) if i.width == canvas.width && i.height == canvas.height => i,
|
||||
Some(i) if i.width == img.width && i.height == img.height => i,
|
||||
_ => {
|
||||
let mut mimg = Image::new(canvas.width, canvas.height);
|
||||
let mut mimg = Image::new(img.width, img.height);
|
||||
if m.is_active_at(ctx.time)
|
||||
&& let Some(mb) = self.layer_buf(ctx, m)
|
||||
{
|
||||
@@ -1961,7 +1974,7 @@ impl<'a> Renderer<'a> {
|
||||
mimg
|
||||
}
|
||||
};
|
||||
iso.data.par_iter_mut().zip(mimg.data.par_iter()).for_each(|(p, q)| {
|
||||
img.data.par_iter_mut().zip(mimg.data.par_iter()).for_each(|(p, q)| {
|
||||
let k = match kind {
|
||||
MatteKind::Alpha => q[3],
|
||||
MatteKind::AlphaInverted => 1.0 - q[3],
|
||||
@@ -1974,22 +1987,16 @@ impl<'a> Renderer<'a> {
|
||||
}
|
||||
});
|
||||
}
|
||||
if preserve {
|
||||
iso.data.par_iter_mut().zip(canvas.data.par_iter()).for_each(|(p, q)| {
|
||||
for c in p.iter_mut() {
|
||||
*c *= q[3];
|
||||
}
|
||||
});
|
||||
}
|
||||
canvas.blend_from(&iso, layer.blend_mode, opacity, layer.id.0 as u32);
|
||||
}
|
||||
|
||||
/// Adjustment layer: effects applied to the comp below, limited to the layer's footprint.
|
||||
/// Adjustment layer: effects applied to the comp below, limited to the layer's footprint
|
||||
/// and its track matte.
|
||||
fn draw_adjustment(&self, ctx: &EvalCtx, layer: &Layer, canvas: &mut Image, opacity: f32) {
|
||||
let Some(mut foot) = self.source(ctx, layer) else { return };
|
||||
let _ = masks::apply(ctx, layer, &mut foot, self.mask_blur(ctx, layer));
|
||||
let mut matte = Image::new(canvas.width, canvas.height);
|
||||
self.place(ctx, layer, &foot, &mut matte, BlendMode::Normal, 1.0);
|
||||
self.apply_track_matte(ctx, layer, &mut matte, None);
|
||||
let o = self.roi_offset().map(|(x, y, _, _)| [-x, -y]).unwrap_or([0.0; 2]);
|
||||
let mut below = Buf { img: canvas.clone(), offset: o, scale: self.opts.scale };
|
||||
// Effects run in the working space, not the linear blending space.
|
||||
|
||||
@@ -161,6 +161,32 @@ fn track_matte_kinds_keep_and_invert() {
|
||||
}
|
||||
}
|
||||
|
||||
/// An adjustment layer with a track matte applies its effects only where the matte allows: an
|
||||
/// Invert over a 0.25 grey with an opaque white 50×50 matte at the left of the comp (#1201).
|
||||
#[test]
|
||||
fn track_matte_limits_an_adjustment_layer() {
|
||||
for (kind, inside) in [(MatteKind::Alpha, true), (MatteKind::AlphaInverted, false), (MatteKind::Luma, true), (MatteKind::LumaInverted, false)] {
|
||||
let (mut p, cid, comp) = setup();
|
||||
let mut matte = solid(&mut p, &comp, [1.0, 1.0, 1.0], 50, 50);
|
||||
matte.props.prop_mut("transform/position").unwrap().value = Value::Vec3([50.0, 50.0, 0.0]);
|
||||
matte.switches.video = false;
|
||||
let mut adj = solid(&mut p, &comp, [1.0, 1.0, 1.0], 200, 100);
|
||||
adj.switches.adjustment = true;
|
||||
add_effect_200(&mut p, &mut adj, "ec.channel.invert", &[]);
|
||||
adj.track_matte = Some(TrackMatte { layer: matte.id, kind });
|
||||
let bg = solid(&mut p, &comp, [0.25, 0.25, 0.25], 200, 100);
|
||||
let c = p.comp_mut(cid).unwrap();
|
||||
c.layers.push(matte);
|
||||
c.layers.push(adj);
|
||||
c.layers.push(bg);
|
||||
let img = render_frame(&p, cid, Tick::ZERO, 1.0);
|
||||
let (g_in, g_out) = (img.get(50, 50)[0], img.get(150, 50)[0]);
|
||||
let (want_in, want_out) = if inside { (0.75, 0.25) } else { (0.25, 0.75) };
|
||||
assert!((g_in - want_in).abs() < 1e-4 && (g_out - want_out).abs() < 1e-4, "{kind:?}: {g_in} inside, {g_out} outside");
|
||||
assert_eq!(img.get(150, 50)[3], 1.0, "{kind:?} keeps the comp opaque");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shape_layer_draws_fill_and_stroke() {
|
||||
let (mut p, cid, comp) = setup();
|
||||
|
||||
Reference in New Issue
Block a user