;; Inset blend - fade a smaller frame into a larger background ;; Uses distance-based alpha for smooth transition (no hard edges) (require-primitives "xector") (define-effect xector_inset_blend :params ( (x :type int :default 0 :desc "X position of inset") (y :type int :default 0 :desc "Y position of inset") (fade-width :type int :default 50 :desc "Width of fade region in pixels") (overlay :type frame :default nil :desc "The smaller frame to inset") ) (let* ( ;; Get dimensions (bg-h (first (list (nth (list (red frame)) 0)))) ;; TODO: need image:height (bg-w bg-h) ;; placeholder ;; For now, create a simple centered circular blend ;; Distance from center of overlay position (cx (+ x (/ (- bg-w (* 2 x)) 2))) (cy (+ y (/ (- bg-h (* 2 y)) 2))) ;; Get coordinates as xectors (px (x-coords frame)) (py (y-coords frame)) ;; Distance from center (dx (α- px cx)) (dy (α- py cy)) (dist (αsqrt (α+ (α* dx dx) (α* dy dy)))) ;; Inner radius (fully overlay) and outer radius (fully background) (inner-r (- (/ bg-w 2) x fade-width)) (outer-r (- (/ bg-w 2) x)) ;; Blend factor: 1.0 inside inner-r, 0.0 outside outer-r, linear between (t (α/ (α- dist inner-r) fade-width)) (blend (α- 1 (αclamp t 0 1))) ;; Extract channels from both frames (bg-r (red frame)) (bg-g (green frame)) (bg-b (blue frame))) ;; If overlay provided, blend it (if overlay (let* ((ov-r (red overlay)) (ov-g (green overlay)) (ov-b (blue overlay)) ;; Linear blend: result = bg * (1-blend) + overlay * blend (r-out (α+ (α* bg-r (α- 1 blend)) (α* ov-r blend))) (g-out (α+ (α* bg-g (α- 1 blend)) (α* ov-g blend))) (b-out (α+ (α* bg-b (α- 1 blend)) (α* ov-b blend)))) (rgb r-out g-out b-out)) ;; No overlay - just show the blend mask for debugging (let ((mask-vis (α* blend 255))) (rgb mask-vis mask-vis mask-vis)))))