;; N-way weighted blend effect ;; ;; Takes N input frames via `inputs` and N per-frame weights. ;; Produces a single frame: the normalised weighted composite. ;; ;; Parameters: ;; weights - list of N floats, one per input (resolved per-frame) ;; mode - blend mode applied when folding each frame in: ;; "alpha" — pure weighted average (default) ;; "multiply" — darken by multiplication ;; "screen" — lighten (inverse multiply) ;; "overlay" — contrast-boosting midtone blend ;; "soft-light" — gentle dodge/burn ;; "hard-light" — strong dodge/burn ;; "color-dodge" — brightens towards white ;; "color-burn" — darkens towards black ;; "difference" — absolute pixel difference ;; "exclusion" — softer difference ;; "add" — additive (clamped) ;; "subtract" — subtractive (clamped) ;; "darken" — per-pixel minimum ;; "lighten" — per-pixel maximum ;; resize_mode - how to match frame dimensions (fit, crop, stretch) ;; ;; Uses a left-fold over inputs[1..N-1]. At each step the running ;; opacity is: w[i] / (w[0] + w[1] + ... + w[i]) ;; which produces the correct normalised weighted result. (require-primitives "image" "blending") (define-effect blend_multi :params ( (weights :type list :default (quote [])) (mode :type string :default "alpha") (resize_mode :type string :default "fit") ) (let [n (len inputs) ;; Target dimensions from first frame target-w (width (nth inputs 0)) target-h (height (nth inputs 0)) ;; Fold over indices 1..n-1 ;; Accumulator is (list blended-frame running-weight-sum) seed (list (nth inputs 0) (nth weights 0)) result (reduce (range 1 n) seed (lambda (pair i) (let [acc (nth pair 0) running (nth pair 1) w (nth weights i) new-running (+ running w) opacity (/ w (max new-running 0.001)) f (resize (nth inputs i) target-w target-h "linear") ;; Apply blend mode then mix with opacity blended (if (= mode "alpha") (blend-images acc f opacity) (blend-images acc (blend-mode acc f mode) opacity))] (list blended new-running))))] (nth result 0)))