mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Track matte on an adjustment layer limits where its effects show (fixes #1201)
This commit is contained in:
1 parent
470ea7a3af
commit
3095217ce8
4 files changed
+89
-31
No files matched your search
@@ -3,7 +3,7 @@
|
||||
|
||||
use effectcraft_keyframe::{ShapePath, Value};
|
||||
use effectcraft_project::build::{self, Ids};
|
||||
use effectcraft_project::{BitDepth, ColorSpace, MaskMode};
|
||||
use effectcraft_project::{BitDepth, ColorSpace, MaskMode, MatteKind, TrackMatte};
|
||||
use effectcraft_render::{Backend, FxStep, FxTarget, RenderOpts, Renderer};
|
||||
use effectcraft_time::Tick;
|
||||
|
||||
@@ -72,6 +72,24 @@ fn mixed_adjustment_chains_fall_back_per_effect() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A track matte limits where the adjustment shows, as on the CPU (#1201).
|
||||
#[test]
|
||||
fn track_mattes_limit_gpu_adjustments_like_the_cpu() {
|
||||
for depth in [BitDepth::Bpc8, BitDepth::Bpc32] {
|
||||
for kind in [MatteKind::Alpha, MatteKind::AlphaInverted, MatteKind::Luma, MatteKind::LumaInverted] {
|
||||
let mut s = adjustment_scene(depth, &gpu_stack(), false);
|
||||
let mut matte = s.footage(50, 30);
|
||||
matte.switches.video = false;
|
||||
set(&mut matte, "transform/rotation", Value::Scalar(10.0));
|
||||
let mid = s.push(matte);
|
||||
let c = s.p.comp_mut(s.cid).unwrap();
|
||||
let adj = c.layers.iter_mut().find(|l| l.switches.adjustment).unwrap();
|
||||
adj.track_matte = Some(TrackMatte { layer: mid, kind });
|
||||
check(&format!("matted adjustment {kind:?} {depth:?}"), compare_at(&s, opts(), Tick::ZERO), 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Counts where each run of the stack goes.
|
||||
#[derive(Default)]
|
||||
struct Probe {
|
||||
|
||||
+24
-17
@@ -297,12 +297,14 @@ fn draw_precomp(e: &mut Enc, r: &Renderer, ctx: &EvalCtx, layer: &Layer, canvas:
|
||||
|
||||
/// Adjustment layer (`Renderer::draw_adjustment`): the effect stack runs on the comp below,
|
||||
/// resident on the GPU (non-GPU effects read back, run on the CPU and upload), and the result
|
||||
/// replaces the canvas inside the layer's footprint × opacity. `None` = draw it on the CPU.
|
||||
/// replaces the canvas inside the layer's footprint × track matte × opacity. `None` = draw it
|
||||
/// on the CPU.
|
||||
fn draw_adjustment(e: &mut Enc, r: &Renderer, ctx: &EvalCtx, layer: &Layer, canvas: &mut GpuImage) -> Option<()> {
|
||||
let opacity = r.layer_opacity(ctx, layer);
|
||||
let Some(foot) = r.adjustment_footprint(ctx, layer) else { return Some(()) };
|
||||
let (w, h) = (canvas.width, canvas.height);
|
||||
let matte = place(e, r, ctx, layer, &foot, &e.zeros(w, h), BlendMode::Normal, 1.0, None)?;
|
||||
let matte = track_matte(e, r, ctx, layer, matte, None)?;
|
||||
let pipe = r.pipe();
|
||||
// Effects run in the working space, not the linear blending space.
|
||||
let below = match pipe.from_blend() {
|
||||
@@ -353,22 +355,7 @@ fn composite_iso_with(
|
||||
matte_img: Option<GpuImage>,
|
||||
levels: Option<f32>,
|
||||
) -> Option<()> {
|
||||
if let Some((m, kind)) = r.track_matte(ctx, layer) {
|
||||
let mimg = match matte_img.filter(|i| (i.width, i.height) == (canvas.width, canvas.height)) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
let mut mimg = e.zeros(canvas.width, canvas.height);
|
||||
if m.is_active_at(ctx.time)
|
||||
&& let Some(mb) = r.layer_buf(ctx, m)
|
||||
{
|
||||
let mo = ctx.opacity(m) as f32;
|
||||
mimg = place(e, r, ctx, m, &mb, &mimg, BlendMode::Normal, mo, None)?;
|
||||
}
|
||||
mimg
|
||||
}
|
||||
};
|
||||
iso = ops::matte(e, &iso, &mimg, kind);
|
||||
}
|
||||
iso = track_matte(e, r, ctx, layer, iso, matte_img)?;
|
||||
if layer.preserve_transparency {
|
||||
iso = ops::preserve(e, &iso, canvas);
|
||||
}
|
||||
@@ -376,6 +363,26 @@ fn composite_iso_with(
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// `Renderer::apply_track_matte`: `img` (comp-sized) times the layer's track matte, if it has
|
||||
/// one; `matte_img` as in [`composite_iso_with`].
|
||||
fn track_matte(e: &mut Enc, r: &Renderer, ctx: &EvalCtx, layer: &Layer, img: GpuImage, matte_img: Option<GpuImage>) -> Option<GpuImage> {
|
||||
let Some((m, kind)) = r.track_matte(ctx, layer) else { return Some(img) };
|
||||
let mimg = match matte_img.filter(|i| (i.width, i.height) == (img.width, img.height)) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
let mut mimg = e.zeros(img.width, img.height);
|
||||
if m.is_active_at(ctx.time)
|
||||
&& let Some(mb) = r.layer_buf(ctx, m)
|
||||
{
|
||||
let mo = ctx.opacity(m) as f32;
|
||||
mimg = place(e, r, ctx, m, &mb, &mimg, BlendMode::Normal, mo, None)?;
|
||||
}
|
||||
mimg
|
||||
}
|
||||
};
|
||||
Some(ops::matte(e, &img, &mimg, kind))
|
||||
}
|
||||
|
||||
/// Draw a processed layer buffer onto `target` (transform, motion blur sub-samples), quantising
|
||||
/// the result to `levels` when set.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
Reference in new issue
Block a user