Skip to content

feat(transition): add zoom_blur, the radial "tunnel" cut - #352

Merged
LeadcodeDev merged 2 commits into
mainfrom
feat/zoom-blur-transition
Sep 26, 2026
Merged

LeadcodeDev merged 2 commits into
mainfrom
feat/zoom-blur-transition

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Refs #344 — its point 3.

A reference video exits a scene through a radial zoom blur: the content streaks outward from a centre point, then the next scene appears. zoom_in only scales a sharp frame buffer, and the per-element motion_blur effect cannot help, because a transition only ever sees two already-painted RGBA buffers — it has no access to a component's own animation samples. That is why this had to be a transition rather than an effect.

{ "type": "zoom_blur", "duration": 0.5, "strength": 1.5, "origin": { "x": 320, "y": 180 } }
  • strength (default 1.0) — how far the streaks reach. 0 collapses to a plain zoom.
  • origin (default: frame centre) — where the streaks radiate from, in pixels.

Zero at both ends is a short-circuit, not a fade

A leftover smear on the last frame bleeds into the next scene, so this needed to be exact rather than asymptotic. peak = 1 - |2p - 1| is exactly 0 at p=0 and p=1 whatever strength is, and when reach <= 0.0 the function returns the sharp composite directly — there is no float-rounding path that could leave a residual.

zero_at_both_ends_even_with_strong_streaks proves it at strength: 4.0: byte-identical to the raw source frame at progress=0 and the raw destination at progress=1.

Verification

7 pixel-level tests in crates/rustmotion-core/tests/zoom_blur.rs, no reference images:

  • zero_at_both_ends_even_with_strong_streaks
  • strength_zero_is_a_plain_zoom_at_every_progress
  • mid_transition_a_sharp_edge_is_measurably_smeared — a bright off-centre stripe shows more distinct intensity values across its edge at strength: 3.0 than at 0.0
  • a_bigger_strength_smears_further
  • custom_origin_shifts_where_the_streaks_radiate_from
  • deterministic_across_repeated_renders
  • nonzero_strength_changes_the_mid_transition_frame

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace (1532) all clean. End-to-end rustmotion validate on a scenario using the new type passes schema and geometry.

The trap the rule file documents

An edge sitting on the pivot cannot appear to smear at any strength: a scale transform's own pivot is a fixed point. That is geometry, not a bug, and worth knowing before concluding the feature is broken — rules/zoom-blur-transition.md says so.

Written comment-free, per the codebase-wide rule from #345.

@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Sep 26, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 26, 2026
zoom_in only scales a sharp frame buffer, and the per-element motion_blur
effect can't help here because it accumulates samples of a single component's
own animation — by the time a transition runs, both scenes are already flat
RGBA buffers with no components left to sample. Closes issue #344 point 3.

zoom_blur composites the same crossfade-zoom as zoom_in, then optionally
layers ~10 extra copies of the outgoing frame at escalating scale and
decreasing alpha, pivoting around `origin` (default: frame centre) — a
directly rendered version of "sum of scaled copies sampled along a ray from
the centre". `strength` scales how far those copies reach; 0 skips the streak
pass entirely and degenerates to a plain zoom.

The streak intensity follows the same peak-in-the-middle curve as
chromatic_wipe's aberration (zero at progress 0 and 1), but instead of
letting that curve fade to a negligible float near the edges, the function
short-circuits and returns the sharp composite outright whenever the reach is
non-positive. That guarantees byte-identical output to the plain
source/destination frame at both ends — no rounding-error residue that could
bleed a smear into the next scene.

Also documents the interplay with the pivot: a scale transform leaves its own
pivot point fixed, so content whose edge coincides with `origin` will not
appear to smear no matter how large `strength` is — geometry, not a bug.
@LeadcodeDev
LeadcodeDev force-pushed the feat/zoom-blur-transition branch from 4d4b784 to 6d7e5f2 Compare September 26, 2026 23:22
@LeadcodeDev
LeadcodeDev merged commit 63c95b7 into main Sep 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant