Module compose

Source
Expand description

Compositor: renders the timeline at time t into an RGBA canvas. Used by preview (small canvas) and export (project-size canvas) — same output, so preview == export. The one gap left against the GL renderer: node graphs (Clip.graph) and the GPU-only effect kinds (effects::gpu_only) are not evaluated here; export::cpu_gaps names them in the export progress line.

Layer order: video tracks bottom→top in project.tracks order (V1 first, then V2 drawn over it…). For each active (project.active(track)), enabled, visual clip containing t:

  • Video/Image: pool.video(asset.path).frame_at(clip.src_time(t), dw, dh, ..) where (dw,dh) is the placement size (clamped to native size, min 1) — decoders scale for us.
  • Text / Shape: text.render(style, canvas_w / project.width) / shapes.render(style, s, local_t) (already at canvas scale) — contain=false.
  • Sequence: the nested timeline is rendered recursively at clip.src_time(t) into its own canvas (the sequence’s size, fit like a video layer), depth ≤ 8.
  • Adjustment: no layer of its own — the effect stack re-runs over the canvas below it.
  • clip.effects run in stack order on the decoded layer (Wobble moves the placement instead: dx/dy/roll, and yaw/pitch render through a perspective homography). Effect.mask limits an effect to its shape (layer space); Clip.mask limits the whole layer (canvas space) — both mirror shaders.rs::MASK.
  • Transform the layer into canvas space per placement() (sampling per project.scaler; straight row copies for the common axis-aligned case), then blend::composite_row / blend::composite_rect. Transitions render both clips of the cut — extended virtually into the window, which is clamped to the two clips (trans_window) so an over-long one cannot hide the rest of the track — and blend per kind. Subtitles (Project::cue_at) draw last, bottom-centre. Background is opaque black. Out-of-view layers are skipped.

Structs§

Compositor
Holds scratch buffers so rendering does not allocate per frame.
Extra 🔒
Per-clip render tweaks used by transitions (opacity fade, push offset, fade-to-colour).
Placement
Where a layer lands on a canvas of (cw, ch) pixels: centre, size and rotation (degrees, clockwise). yaw/pitch (degrees) tilt the layer about the vertical/horizontal axis (camera shake) — rendered through a perspective homography with focal length ≈ canvas width; 0 = flat.

Constants§

MAX_DEPTH 🔒
Recursion limit for nested sequences.
MAX_MASK_POINTS 🔒
Mask polygon vertex cap — same as the GPU’s m_points[64].

Functions§

apply_effects 🔒
Run the clip’s effect stack on the layer image; geometric effects (Wobble) move p instead. s = canvas px per project px; img_scale = layer-image px per project px. A masked effect is mixed back over the pre-effect image by the mask’s coverage (as shaders.rs::apply_mask does).
catmull 🔒
Catmull-Rom weights for taps at offsets -1, 0, 1, 2 for fractional position t.
clip_mask 🔒
Coverage for clip.mask over the whole canvas, or None when the clip has no enabled mask. The mask lives in canvas space around the layer’s placed centre (matching gpu.rs).
dir_vec 🔒
Direction unit vector scaled to canvas size: 0 = left, 1 = right, 2 = up, 3 = down.
draw_layer
Draw src into dst (canvas) at p with blend mode & opacity, using scratch as the canvas-sized layer buffer. Sampling per scaler; pixels outside src are transparent.
draw_layer_masked 🔒
draw_layer with an optional canvas-sized coverage mask (0..255 per pixel) multiplied into the layer’s alpha. A masked draw always takes the general path, which is where coverage is applied.
draw_layer_perspective 🔒
Perspective path: project the placed rect’s corners tilted by yaw (about Y) and pitch (about X) with focal length = canvas width, then inverse-map each canvas pixel through the homography.
fade_to 🔒
Mix the layer’s RGB towards color by f (FadeToColor transitions).
invert3 🔒
Inverse of a 3×3 (adjugate / det); None when singular.
mask_coverage 🔒
Rasterise mask into out (one coverage byte per pixel of a w×h buffer) — the CPU twin of shaders.rs::MASK, so preview and export agree. scale = buffer px per project px, centre = the layer centre in buffer px.
placement
Placement of a clip’s layer on a (cw, ch) canvas at timeline time t. native = the layer image size. With contain (video/images) the image is first fitted inside the canvas preserving aspect; text is already rendered at canvas scale so contain = false. Then: scale about the centre by clip.scale, offset by (clip.x, clip.y) project pixels (scaled to canvas), rotate by clip.rotation.
placement_w 🔒
placement with an explicit project width (nested sequences use the sequence’s own size).
sample 🔒
Sample src at (sx, sy) — already clamped to [0, sw-1]/[0, sh-1] — returning straight RGBA (r, g, b in 0..255, a in 0..255).
sd_box 🔒
Signed distance to a box of half-size (bx, by).
sd_ellipse 🔒
Cheap ellipse SDF (exact only on circles), matching the shader.
sd_poly 🔒
Signed distance to the closed polygon through pts (negative inside).
square_to_quad 🔒
Homography mapping the unit square (0,0)(1,0)(1,1)(0,1) onto q (Heckbert).
track_active 🔒
Mute/solo resolution over an arbitrary track list (mirrors Project::active).
trans_progress
Eased progress 0..1 over the clamped window of any placement (edge transitions have one side).
trans_progress_at
Eased progress 0..1 across a transition window of cut ± half.