Merge pull request #1527 from marcoarnulfo/fix/adjustment-layer-transform-replaces-below

Adjustment layers: a Transform replaces the picture below instead of drawing over it
This commit is contained in:
Brandon Thomas committed 2026-10-13 01:01:09 -04:00
commit 9418816580
2 files changed
+39 -6

No files matched your search

+20
View File
@@ -156,6 +156,26 @@ fn motion_and_masks_restrict_the_region() {
assert!(near(img.get(280, 90), [1.0, 0.0, 0.0], 1e-4));
}
#[test]
fn a_transform_replaces_the_picture_below() {
// Premiere: Transform on an adjustment layer moves the composite below; it is not a copy
// drawn over the untouched original (#1524).
let mut s = scene(2);
let red = matte(&mut s.p, &mut s.map, [1.0, 0.0, 0.0, 1.0]);
place(&mut s.p, s.seq, 0, red);
let adj = adjustment(&mut s.p, s.seq, 1);
fx(&mut s, adj, "transform");
let tf = clip(&mut s, adj).effects.iter_mut().find(|e| e.effect == "transform").unwrap();
tf.params.get_mut("scale_height").unwrap().value = ParamValue::Float(50.0);
let img = render(&s);
assert!(near(img.get(160, 90), [1.0, 0.0, 0.0], 1e-4), "{:?}", img.get(160, 90));
assert_eq!(img.get(10, 10), [0.0; 4], "the original is gone outside the scaled picture");
// at 50 % opacity: half the original, half the (empty) transformed picture
clip(&mut s, adj).effect_mut("opacity").unwrap().params.get_mut("opacity").unwrap().value = ParamValue::Float(50.0);
let c = render(&s).get(10, 10);
assert!((c[0] - 0.5).abs() < 1e-3 && (c[3] - 0.5).abs() < 1e-3, "{c:?}");
}
#[test]
fn nested_adjustment_layers_stay_in_their_sequence() {
let mut s = scene(2);
+19 -6
View File
@@ -229,9 +229,14 @@ pub(crate) fn render_seq_tracks(project: &Project, seq: &Sequence, t: Tick, opts
}
let below = canvas.snapshot();
let (adj, bl) = adjustment_layer(project, seq, item, t, opts, &tc, &below);
let mut out = below;
blend::composite(&mut out, &adj, 1.0, bl);
canvas.set(out);
if bl == Blend::Normal {
// The adjusted picture already replaces the original (see `adjustment_layer`).
canvas.set(adj);
} else {
let mut out = below;
blend::composite(&mut out, &adj, 1.0, bl);
canvas.set(out);
}
continue;
}
match item_layer_ex(project, seq, item, t, opts, sources, &tc, true) {
@@ -290,12 +295,20 @@ fn adjustment_layer(project: &Project, seq: &Sequence, item: &TrackItem, t: Tick
mask::apply_effect(&mut adj, e, &cx);
}
let (op, bl) = opacity_blend(item, mt);
// Adjustment layer opacity (and opacity masks, in sequence pixels) mix adjusted over original.
adj.scale_alpha(op);
mask::apply_opacity_masks(&mut adj, item, mt, opts.scale);
// Adjustment layer opacity (and opacity masks, in sequence pixels) weigh adjusted against original.
let mut weight = Image::filled(w, h, [op; 4]);
mask::apply_opacity_masks(&mut weight, item, mt, opts.scale);
// Motion moves / scales the adjustment layer's frame: it only applies inside it.
if let Some(region) = adjustment_region(seq, item, project, mt, opts.scale, w, h) {
let cov: Vec<f32> = region.px.as_chunks::<4>().0.iter().map(|p| p[3]).collect();
mask::scale_by(&mut weight, &cov);
}
let cov: Vec<f32> = weight.px.as_chunks::<4>().0.iter().map(|p| p[3]).collect();
if bl == Blend::Normal {
// The adjusted picture replaces the original instead of being drawn over it: what
// Transform or Basic 3D leave empty shows nothing of the untransformed picture (#1524).
mask::mix(&mut adj, below, &cov);
} else {
mask::scale_by(&mut adj, &cov);
}
(adj, bl)