Commit Graph
139 Commits
Author SHA1 Message Date
BFlat e2128a6743 Merge pull request #2118 from ChristopherLinnett/fix/issue-2092-camera-focus-crash
fix: bound Classic 3D depth-of-field blur and reject non-finite focus distances (#2092)
2026-10-30 05:09:17 +07:00
BFlat b08c2bc76d Merge pull request #2348 from vangel76/effects-cache-analysis-keys
Reuse Warp Stabilizer and Camera Tracker analysis keys
2026-10-30 01:26:28 +07:00
bflatastic 3830d43c4e Merge main into #2348; resolve crates/effects/src/warp_stab.rs 2026-10-30 01:25:07 +07:00
BFlat a0bf1b13d8 Merge pull request #1382 from tylerjenningsw/fix/particle-birth-history
Particles: preserve emitted particles when Birth Rate changes (#1373)
2026-10-30 01:20:20 +07:00
bflatastic 89fcd892eb Merge remote-tracking branch 'origin/main' into HEAD 2026-10-30 00:27:26 +07:00
Christopher Linnett 280a400be4 Merge origin/main into fix/issue-2092-camera-focus-crash
Conflicts were main's new code next to this branch's additions; both kept:
- crates/effects/src/lib.rs: main's Buf::pad_sides stays as main wrote it
  (Transform's transform_grow bounds that padding itself); Buf::pad keeps
  this branch's effective-pad clamp.
- crates/raster/src/lib.rs: main's quantize_px/round_half_up and this
  branch's MAX_PADDED_SIDE (still equal to limits::MAX_SIZE on main).
2026-10-29 18:44:49 +08:00
Christopher Linnett 60da124f11 Merge remote-tracking branch 'origin/main' into fix/issue-2092-camera-focus-crash 2026-10-28 00:13:40 +08:00
tylerjenningsw 5808550c5a Merge main into fix/particle-birth-history-ps2-mercury 2026-10-25 14:26:29 -04:00
tylerjenningsw 20d8501ea2 Merge main into fix/particle-birth-history 2026-10-25 14:26:29 -04:00
vangel76 66a94f3ca1 Reuse analysis content keys across effect renders
Keep parsed analyses, their FNV key and source text in a bounded shared cache. Compare source contents on hits instead of rehashing the JSON, so cloned parameter strings remain fast while in-place edits invalidate the cache. Cover analysis replacement, key equality, same-allocation edits and retention bounds.

Part of #2322
2026-10-25 00:22:46 +02:00
vangel76andClaude Opus 5.5 31c624f478 Merge upstream/main into rfx-blur-stylize
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:23:59 +02:00
Christopher Linnett 34242e8c1b fix: move Buf offsets by the pad Image::padded actually applies (#2092 review follow-up)
Image::padded clamps the pad so each side stays within MAX_PADDED_SIDE, but
Buf::pad still added the requested pad to its offset, so a clamped pad left
the pixels and the offset disagreeing (a hostile pad of u32::MAX shifted the
pixels by 5 and the offset by ~4.3e9). The clamp now lives in one place,
Image::effective_pad, used by both padded() and Buf::pad.

The padded() doc now states what the clamp guarantees (output within the
largest image the engine supports) and that memory budgets belong to callers
that pad by a derived amount (DOF_PAD_BUDGET, GPU fits()).

Adds an effects regression test: Buf::pad(u32::MAX) moves the offset by the
clamped pad and the source pixel stays at its layer position (fail-before:
offset off by u32::MAX). cargo test -p effectcraft-effects 457/457,
-p effectcraft-raster 35/35, -p effectcraft-render 238/238; clippy -D
warnings and fmt clean on touched crates.
2026-10-24 11:49:53 +08:00
aiongg a622e6bc5d Merge #1604: Cryptomatte: skip the ID colours unless Colors shows them, key the layer in place, and cache each rank's last ID colour (@MatejKozlovsky) 2026-10-24 01:21:39 +00:00
aiongg 6d0dc7ac57 Merge #2206: effects: CC Radial Fast Blur grows its buffer so streaks pass the layer edge (#2095) (@mercuryVM) 2026-10-24 01:20:43 +00:00
aiongg bae27ebbe7 Merge #1841: Cryptomatte: pick the ID under a point into the Selection (effect.pickCryptomatte) (@MatejKozlovsky) 2026-10-23 23:33:10 +00:00
aiongg d74fe2d51e Merge #2003: Motion Tile's Output size works on adjustment layers (#1727) (@ded-achtung) 2026-10-23 23:22:30 +00:00
Gabriel Luís da Silva 6d87dd6217 effects: CC Radial Fast Blur grows its buffer so streaks pass the layer edge (#2095)
Content r from Center smears out to r / (1 - s), so the buffer is padded by
the farthest corner's reach (capped at 4096, skipped on adjustment layers),
on the CPU and GPU paths alike.
2026-10-23 17:34:59 -03:00
dedandClaude Opus 5.5 915ba28a9d Motion Tile's Output size works on adjustment layers (#1727)
On an adjustment layer Motion Tile kept the comp-sized buffer whatever the
Output Width / Height, and the adjustment draw dropped any stack result whose
size differed from the canvas. After Effects grows (or crops) the comp
rectangle there as on any layer, so a Transform after it brings the extra
tiles into view, as in the frames attached to #1727 and #1409.

- motion_tile_frame: one output frame for the CPU and GPU effect; on an
  adjustment it grows about the comp rectangle on whole pixels, keeps the
  buffer at 100 % and past 16 384 pixels a side.
- adjustment_crop: the CPU and GPU adjustment draws put a stack result whose
  frame changed back onto the canvas instead of dropping it (Enc::crop on
  the GPU).

Tests: the #1727 After Effects frames (910 x 340 % and 90 x 340 % then
Scale 12 %), tiles past the frame and cropping on CPU, GPU vs CPU for
adjustment and full-frame layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:35:47 +03:00
vangel76andClaude Opus 5.5 83c76f266f Review: Glow's WGSL literals tied to GLOW_GAIN / GLOW_KNEE; parity summary consistent
kernels.wgsl repeats Glow's bright-pass gain 8.0 and knee 0.0611 as literals; they now
name effects::GLOW_GAIN / GLOW_KNEE (exported) and the test
glow_constants_match_the_cpu (no GPU needed) fails if they drift. docs/effects-parity.md:
the summary row no longer says no effect is compared with After Effects while the
Blur & Sharpen row reports Radial Blur's comparison (CodeRabbit on #1873), and the
Radial Blur changelog row says CPU only (it has no GPU path).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:49:00 +02:00
vangel76andClaude Opus 5.5 e559a7ce9a Review: tie the colour kernels' literals to the CPU constants; visible GPU skip; B&C wording
WGSL cannot read Rust constants, so the Rec. 601 luma (Tint, Tritone, Luma Key /
Extract), Black & White's tint weights and Exposure's 2.2 power stay literals in the
kernels; they now name their Rust source (effects::LUMA_601, BW_TINT_LUMA,
EXPOSURE_LINEAR_GAMMA, newly exported) and the test shader_constants_match_the_cpu
(no GPU needed) fails if a literal drifts. gpu_tint_defaults_match_after_effects_greys
says "skipped: no GPU adapter" when it skips (EFFECTCRAFT_REQUIRE_GPU=1 still makes it
fail). docs/effects-parity.md: Brightness & Contrast is close to, not the same as,
After Effects (up to 6.2/255), and the summary row lists what is measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:38:41 +02:00
vangel76andClaude Opus 5.5 d37c087975 Transform effect: skew after the rotation and drawn outside the layer, as After Effects
After Effects draws the Transform effect as position + K * R * S * (p - anchor)
with the skew K = rot(-axis) * shearY(tan skew) * rot(axis) applied after the
rotation, and keeps what lands outside the layer's bounds. EffectCraft skewed
before rotating (shearX about the axis) and clipped to the layer.

Measured in AE 2026 on a grey 960x540 solid (cases 723, 819, 820): 723 (anchor,
position, scale 120/80, skew 10 axis 20, rotation 30, opacity 70) differed in
11.25 % of the frame and now in 0.065 % (edges, max 16/255), CPU and GPU alike;
819 / 820 (skew 15 axis 60, rotation -25, scale 130/70, anchor) match to the
edge. The buffer grows on each side by that side's overshoot (also for the
motion-blur samples), capped at 16 384 px a side and 16 Mpx in all so a layer
moved far away costs a bounded allocation (regression test); adjustment layers
keep their size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:25:44 +02:00
vangel76andClaude Opus 5.5 60cf195236 Fractal / Turbulent Noise: lattice cells are two thirds of Scale, as After Effects
After Effects 2026 draws Fractal Noise's Block noise at Scale 100 and
Complexity 1 in cells of 66.67 px (the value changes every 66.67 px along a
row), not 100 px; Soft Linear noise has the matching correlation length.
EffectCraft's lattice is now Scale x 2/3 px (NOISE_CELL), CPU and GPU. Case
904 E03 (Soft Linear, Complexity 4, Scale 100): the noise's autocorrelation
at 32 px was 0.68 (AE 0.41), now 0.54; pixel values cannot match (other
seeds). Still open: AE's noise spreads about 20 % wider around its mean (std
48 vs 40/255 in E03). Existing projects and the Backgrounds presets with
Fractal Noise get finer noise (two thirds of the former cell size).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:25:44 +02:00
vangel76andClaude Opus 5.5 8d41ef7218 Radial Wipe: Feather in pixels on both edges of the wedge, as After Effects
After Effects feathers both edges of the wiped wedge (the start ray too) with
a Gaussian whose width is constant in pixels: Completion 40, Start Angle 30,
Counterclockwise, Feather 20 (AE 2026, case 904 E12) fits sigma = 0.4 x
Feather = 8.0 px at every radius (mean alpha error 0.0002 over the frame).
EffectCraft ramped only the moving edge, linearly over Feather degrees, so
the edge was 14 px wide near the centre and 140 px wide at 400 px out, and
the start edge was hard: 378 of 5184 grid points off (max 111/255, 6.6 % of
pixels > 8), now 1 (max 3), 0.009 % of pixels > 8 (near the centre). CPU and
GPU (the kernel now measures distances to the two rays). 0 % and 100 % are
unchanged, so the three Transitions presets keep their end frames; their
Feather (6 / 10) now means pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:25:44 +02:00
vangel76andClaude Opus 5.5 47b3582a0a Venetian Blinds: stripes from the layer centre, Gaussian feather on both edges, as After Effects
After Effects measures the blinds' phase from the layer's centre and softens
both edges of the transparent band with a Gaussian of sigma = Feather / 2; the
band runs from the origin's phase to Completion x Width. EffectCraft measured
from the buffer corner and ramped only one edge linearly over Feather (a hard
edge on the other side). Fitted to AE 2026 renders of Completion 40,
Direction 30, Width 50, Feather 5 on a 960 x 540 and a 480 x 270 layer (the
origin is the centre + 0.6 px): case 715 was 499 of 5184 grid points off
(max 91/255, 7.1 % of pixels > 8), now 28 (0.056 %), all within ~6 px of the
layer edge where AE differs (up to 48/255, not modelled); away from it within
6/255 (test: one AE row of the 480 x 270 render). CPU and GPU (new erf-based
kernel, same term count). Completion 0 / 100 are unchanged, so the
Transitions presets keep their start and end frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:24:05 +02:00
vangel76andClaude Opus 5.5 f4fbdc6c25 Glow: the glow covers the layer's blurred alpha, blur sigma 0.384 x radius, as After Effects
With Glow Based On Color Channels, After Effects gives the glow the layer's whole
alpha (blurred), not the thresholded part, and Intensity scales only its colour:
a layer below the threshold darkens what lies under its edge. Fitted to AE 2026
renders of a grey solid over red (cases 524, 807-809): the halo is the same at
Intensity 1 and 3 and with Composite Original Behind or On Top, and its edge is
an erfc step of sigma = 0.384 x Glow Radius at radius 10 and 30 (was radius / 2).

524 (Glow defaults on a 50 % grey solid): EffectCraft had no halo (max 116/255
off at the edge); now red 248/234/208/165/140 at 7.5/5.5/3.5/1.5/0.5 px outside
vs AE 249/235/206/164/140, max 2/255 over the frame, CPU and GPU. The blur
width applies to every glow; alpha-based glows keep their coverage rule
(thresholded alpha, scaled by Intensity). Not matched yet: the brightness of glows above the
threshold (AE about 3.7 x brighter for a white solid, cases 806, 810, 811).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:23:25 +02:00
vangel76andClaude Opus 5.5 d7854b84a3 4-Color Gradient: weights 1 / (d² + Blend²), as After Effects
After Effects weighs each of the four points by its inverse squared
distance softened by Blend squared, 1 / (d^2 + Blend^2) in pixels: fitted to
AE 2026 renders of the default gradient at Blend 10, 100 and 300 and case
904 E16 at Blend 50 (power 2.00 at every Blend, softening 0 / 2367 / 9817 /
90127 = about Blend^2; mean error 0.27/255, max 0.9). EffectCraft used
1 / (d^2 / (100 Blend))^1.5, in which Blend cancels out: E16 was 34/255 off
on 99 % of the grid, now 0 grid points off, max 1/255, CPU and GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:16:02 +02:00
vangel76andClaude Opus 5.5 515cc3d5f7 Levels: channel controls before RGB, "Off for 32 bpc Color" unclipped in 32 bpc, as After Effects
After Effects 2026 in a 32 bpc project, Levels (Individual Controls) with RGB
(0.1, 0.9, gamma 1.5, 0.05, 0.95), Red Gamma 0.8, Green Input White 0.8 and
Blue Output Black 0.2 on grey steps 0..255 (case 904 E07): every step matches
"each channel's controls, then the RGB controls" with nothing clipped
(values below Input Black end below Output Black, above Input White above
Output White). EffectCraft ran RGB first and clipped "Off for 32 bpc Color"
(it could not see the bit depth): up to 27/255 off on 95 % of the grid; now
0 grid points off, max 1/255, CPU and GPU.

EffectEnv gains float_depth (the renderer sets it for 32 bpc projects), so
"Off for 32 bpc Color" clips in 8 / 16 bpc projects as before. Unclipped
values below 0 take the gamma mirrored through 0. Plain Levels keeps its
order (its channel controls were not measured); its clipping follows the same
rule. EffectCraft's own projects default to 8 bpc and are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:14:42 +02:00
Matej Kozlovský d74cb70938 Cryptomatte pick: snap to the effect's input buffer grid, not the preview scale
At preview scales from 0.75 to 1 footage keeps native pixels, a proxy sets
its own buffer scale at any render scale, a Preserve Resolution precomp is
drawn at full size, and earlier effects can pad or offset the buffer, so
snapping to the preview grid could pick a different ID than the rendered
matte. Use the scale and offset of the buffer the Cryptomatte effect
receives (layer_input, rendered once and then cached).

The pixel centre goes to an aux point through effectcraft_effects::aux_point,
which index_map_at now calls too, so the two round identically (a division
vs the reciprocal picked the other ID on a 104 px proxy of 1920 px footage).

An unknown effect uid errors "no such effect" and a layer without pixels
"this layer has no pixels", as effect.pickColor does.
2026-10-19 08:40:16 +02:00
Matej Kozlovský 76098665b0 Cryptomatte: effect.pickCryptomatte picks the ID under a point into the Selection (#473)
Engine part of viewer picking: replace / add / remove the top-coverage ID at a
layer-space point in one undo step, at the time the Selection was read. The
aux channels come from the render's own path (Renderer::layer_aux, shared with
the effect host; footage at its source time, 3D precomps) through a renderer
set up like Session::render (expressions, render prefs, layer cache). An
optional scale snaps the point to the pixel a preview at that resolution
samples. New effects::crypto_select quotes names and falls back to <hex> for
unnamed, wildcard-looking or ambiguous names. effect.cryptomatteAt is the
read-only hover query.
2026-10-19 06:10:27 +02:00
Matej Kozlovský 0fbe76b2bf Cryptomatte: CryptoAux enum instead of a positional bool; GPU test for every display mode (#473)
crypto_aux_image_with takes CryptoAux::{ColorsAndMatte, MatteOnly}, and
CryptoAux::for_display holds the rule for which displays read the ID
colours. A new GPU test checks every display x Matte Only against the CPU,
so the GPU fails the test if the shader starts reading RGB in a mode that
skips it (checked by mutation: forcing MatteOnly fails display 0).
2026-10-17 01:48:48 +02:00
Matej Kozlovský 74517a52da Cryptomatte: the GPU path skips the ID colours unless the Colors display reads them, and the CPU path keys the layer in place instead of copying it (#473)
Output is bit-identical (all displays, CPU and GPU). At 2560x1440 with 3 ranks and 2000 IDs on 8 cores the CPU effect drops from ~87-135 ms to ~44-54 ms and the GPU path's aux reduction from 38 to 8 ms for Matte, Preview and Matte Only.
2026-10-15 23:01:42 +02:00
tylerjenningsw 6fcfc355e3 Render: CC Particle Systems II and CC Mr. Mercury layers with expressions aren't layer cached (#1373)
They now read their Birth Rate at earlier times too, so
effects::reads_param_history names them along with CC Particle World. A
layer key can't see what an expression returned then.
2026-10-13 22:35:37 -04:00
tylerjenningsw aa300d7f2e Render: CC Particle World layers with expressions aren't layer cached (#1373)
The simulation checks its checkpoints against the Birth Rate history,
but a cached layer is returned before the effect runs. The layer key
sees the layer's keyframes and its current values, not what an
expression returned at earlier times: a Birth Rate expression reading
another layer could change the history while the current rate, 0, stayed
the same.

effects::reads_param_history names CC Particle World. The renderer
already declines to cache layers whose time effects (ec.time.*) meet
expressions. It now treats Particle World the same way, in the layer key,
the input key (only for the effects that input runs) and the dependency
walk.
2026-10-13 21:59:03 -04:00
tylerjenningsw f263a32ab7 Render: an effect parameter's history is read at its exact layer time (#1373)
FxHost::param_at mapped the requested layer time to comp time, and
EvalCtx::value mapped it back. Under a fractional stretch (33.3 %,
133.33 %) that round trip truncates a tick early, so a Hold key at
0.5 s read its previous value at the step starting there: a Birth Rate
cut to 0 at 0.5 s bore one more step of particles (six).

EvalCtx::value_at_layer_time reads the keyframes at the given layer
time exactly, and still runs an expression at the comp time.
EvalCtx::value now uses it, and param_at reads through it.

EffectHost::param_is_static says when a parameter has no keyframes and
no expression. CC Particle World then skips sampling its Birth Rate at
every step, since it's the same throughout.
2026-10-13 21:59:03 -04:00
tylerjenningsw 7b0771fe5a Effects: a host evaluates one effect parameter at another layer time (#1373)
EffectHost::param_at(effect_index, id, layer_time) evaluates a single
parameter (by its flattened id, such as birthRate or group/param) of the
effect at that index in the layer's stack. A simulation can then sample
one parameter at every step without flattening the whole effect each
time.

The effect index is passed in, rather than read from the renderer's
running-effect index as params_at does: GPU-batched effects never update
that index, so a batched effect would read another effect's parameters.
The renderer's implementation walks the effect group by match id and
evaluates just that property (keyframes and expressions) at the comp
time of the layer time.
2026-10-13 20:45:20 -04:00
Brandon Thomas 4574fb62bc Merge pull request #792 from vangel76/gap-object-matte
# Conflicts:
#	crates/ui-egui/src/i18n/ui.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 04:30:49 -04:00
Brandon Thomas 90621e2b04 Merge pull request #1111 from eliasx45/upstream/effect-fixes 2026-10-13 04:29:09 -04:00
Elias c3fe609ee4 Effects: Time effects see the effects applied before them
Echo, Posterize Time and the other Time effects read the layer at other times with none of
its effects. After Effects renders them with the effects before them: SceneYard "Task
Preparation UI" applies Echo after CC Particle Systems II on black solids, and AE shows the
echoed fireworks over a white page where we echoed the black solids and blacked it out
(Posterize Time after Turbulent Displace, the usual stop-motion setup, depends on the same).
The host already bounds the count to the effects before the one rendering.

Mean abs error vs AE: Task Preparation UI 0.1855 -> 0.0071, Lightbulb Idea 0.0126 -> 0.0102;
the 15 other corpus projects with a Time effect after another effect are pixel-identical.

(cherry picked from commit 46870452db2656c72406f63245e8b1609ceb04d4)

(cherry picked from commit 334d102582e5a64db32e2f3e55836d55ac96bdaa)
(cherry picked from commit 734da3e35dc57c01b73823458bdd87611ac1bf99)

(cherry picked from commit 87a733e076)
2026-10-12 19:18:53 +01:00
vangel76andClaude Sonnet 5.5 4acea86d4f Merge upstream/main into gap-object-matte
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 19:03:42 +02:00
Matej Kozlovský 47dbd8dbd5 Cryptomatte: the rank planes are resolved once per evaluation and the GPU path reduces them on all cores, instead of formatting and looking up channel names for every pixel in a single-threaded loop (#473)
AuxChannels::crypto_rank_planes resolves a layer's (ID, coverage) planes once; crypto_ranks keeps its signature on top of it. The CPU effect and the GPU effect's aux image (effectcraft_effects::crypto_aux_image, rayon) index the planes per pixel. Output is bit-identical.
2026-10-12 13:29:03 +02:00
Brandon Thomas 6b69881826 Merge pull request #686 from vbasky/fix/659-text-effect-bounds
Effects: text and shape layers run their stack over the whole layer region (#659, #658)
2026-10-12 04:21:15 -04:00
vangel76andClaude Opus 5.5 953fbbc730 #783: Object Matte effect from a point / box prompt, tracked
Problem: After Effects 26.2's Object Matte (pick an object with a click,
optionally a box, get a matte that follows it) had no counterpart; only
stroke-driven Roto Brush existed.

Fix: an Object Matte effect (Effect > Matte, ec.matte.objectmatte) turns
the Object Point (and optional box) on a Reference Frame into Roto Brush
strokes and segments them with Roto Brush's engine, content-keyed cache
and the model chosen in Settings > Roto Brush (MobileSAM, classical graph
cut without it). Track Object carries the matte frame by frame up to 600
frames each way; otherwise every frame is segmented on its own. Feather,
Contrast, Shift Edge, Invert and a Matte view. The engine keeps its Input
Key current so upstream edits recompute it. objectMatte.apply creates one
from agents / the CLI. Unusable prompts pass the layer through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:32:10 +02:00
Vikram Bhaskaran 9ce7d12721 Pixel Motion Blur: synthesize the blur from motion vectors and honour Vector Detail (#535).
Time > Pixel Motion Blur fetched the layer at its shutter times and averaged the decoded
frames as they were, so fast motion rendered as doubled copies and the accepted Vector
Detail parameter changed nothing. Build a TimewarpPlan instead: every shutter instant is
decoded between the two source frames it falls between along a measured motion field — the
same block-matching and in-between synthesis Time > Timewarp uses, with a search radius
wide enough for full-frame displacements and the vector grid tightened from Vector Detail
0 (one vector per 32 px) to 100 (one per 2 px) — and the instants average into the blur.

CPU and GPU share the plan (fx_time renders it with its Timewarp step loop, declining to
the CPU when there is no host), so the existing GPU parity cases for the effect compare
the new path. Falling back to the old average is kept for layers without a time host.

A render integration test moves a textured block 12 px between source frames, renders
the straddling shutter, and asserts the output is neither the still nor the plain frame
overlay, lays trail coverage along the block's path, and changes with Vector Detail;
without the synthesis it fails at the overlay assertion.
2026-10-11 13:32:13 +11:00
Vikram Bhaskaran fa733bfb04 Effects: text and shape layers run their stack over the whole layer region (#659).
An authored layer's effect buffer spanned only its painted content — the glyph
bounding box for text, the drawn bounds for shapes — while the size the stack
measures from (effect_bounds) is the whole comp-sized layer rectangle. Tiling
and displacement effects computed correct output for pixels outside the painted
rectangle and then wrote them into the buffer that was too small, so CC Tiler
repeated only within the text box and CC Smear lost its liquid trail at the
glyph edge (#658). Buf::cover now grows the stack's input to the layer's
rectangle with transparent margins — what a sized source's buffer already is —
applied by run_effect_stack whenever an enabled video effect will run, and
natively on the GPU by reallocating and copying the texture. The placement and
the sized-source paths are unchanged; two regression tests (CC Tiler spans the
region, CC Smear trails past the text bounds) fail without the growth.
2026-10-11 12:25:29 +11:00
Claude 2aefe3e644 Effects: CC Smear and CC Tiler on text and shape layers reach past the content's box (#659)
A text or shape layer's effect bounds are comp-sized (as in After Effects),
but its buffer surrounds only its content, and each effect grows the buffer
itself when it needs room. CC Smear and CC Tiler never did, so a smear was
cut off where the glyphs end and tiles filled only the text's box.

CC Tiler now covers the whole layer rectangle; CC Smear grows by how far it
moves pixels (Reach x From->To), at most to the layer bounds, so footage
layers, whose buffer is their bounds already, are unchanged. CPU and GPU
alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 23:35:47 +00:00
Claude 3ba43b20c7 Effect Controls: file parameters get Choose…, Apply Color LUT asks for its LUT on apply, and text parameters get a field (#514, #473)
Cause: Apply Color LUT's only parameter, `lut`, was ParamUi::Hidden (set only
through commands), so the effect could not be used from the UI. Effect
Controls also had no widget for any ParamUi::Text parameter (only EXtractoR's
channels had one), so Cryptomatte's Selection, the OCIO effects' config file /
file / config names and Colorama's Output Cycle could not be set either.

Fix: a new ParamUi::File { filters } for paths. Effect Controls shows the
file's name (None when empty) and a Choose... button that opens the host's
file dialog with those extensions and sets the path (prop.set, so agents use
the same command). As in After Effects, applying an effect with an empty file
parameter from the UI (Effect menu, Last Effect, drops, Effects & Presets)
opens the file dialog straight away; cancelling keeps the effect without a
file. Apply Color LUT's LUT (.cube) and the OCIO Config File (.ocio) and File
(.cube/.3dl/.csp/.cc/.ccc/.cdl/.spi1d/.spi3d/.spimtx) use it. Text
parameters get a single-line field that sets the value when it loses the
focus (Esc keeps the old text). Saved projects are upgraded on open: a
Hidden or Text parameter whose spec is now a File parameter takes the File
UI and keeps its path (effects::migrate). docs/effects.md regenerated.

This covers #473's first part (the Selection field); picking mattes in the
viewer, the layer popup from the file's metadata, the GPU path and quoted
commas are separate.

Tests: effects hidden_and_text_paths_become_file_parameters (migration); ui
file_and_text_parameters_have_widgets (the LUT is asked for on apply and
inverts red to cyan, the Choose... button picks another LUT, typing into
Cryptomatte's Selection sets it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
(cherry picked from commit 403863f703999d19d3a83d9976f76bdc7577e682)
2026-10-10 19:48:25 +00:00
Claude c5af482bce QA: frame scores refuse NaN samples and mismatched buffers (#427 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
2026-10-10 12:53:09 +00:00
Claude 37cff1f547 Merge pull request #427 from vbasky/pr/measured-ae-fidelity 2026-10-10 12:50:18 +00:00
Claude 91d37dd449 Merge pull request #423 from cdinhoo/codex/effect-input-control-fixes 2026-10-10 12:50:18 +00:00
bflatasticandClaude Opus 5.5 923d978119 Effects: CC Particle World follows the comp camera (#397)
Cause: CC Particle World projected its particles through a fixed camera
(distance 2 layer widths, looking at the layer centre) and never asked the
host for the comp camera, so moving or orbiting a camera layer left the
particles flat on the layer. It also had no Extras > Effect Camera. Separately,
the layer cache key ignored the comp camera and lights, so the effects that
read them (Card Dance / Shatter / Card Wipe Comp Camera, First Comp Light,
Caustics) kept a stale frame when the camera, a light or the layer moved.

Fix: as the Cycore manual describes, Particle World sees its particles through
the comp's active camera layer when there is one (EffectHost::comp_scene now
says whether its camera is a camera layer's, CompScene::camera_layer), through
card3d's comp-camera projection (factored out of Card Dance as comp_proj, with
Proj::view for depth, size scale and screen velocity). Otherwise it uses the
new Extras > Effect Camera: Distance (2), Rotation X / Y / Z (0) and FOV (50)
orbit the world origin; FOV moves the camera so the origin's plane keeps the
scale Distance gives it, so the defaults reproduce the old fixed camera bit for
bit (golden hashes unchanged) and old projects render as before. The Effect
Camera is left out of the simulation cache key, so animating it doesn't
restart the simulation. The plan is shared, so the GPU compositor follows too.
The layer cache folds the comp scene into the key of layers running an effect
in effects::READS_COMP_SCENE (render::comp_scene is shared with the host).

Tests: sim particle_world_follows_the_comp_camera (panning a camera layer moves
the particles, zooming spreads them, the Effect Camera is ignored then, the
default view keeps the Effect Camera, cameras facing away / zero / NaN draw
nothing) and particle_world_effect_camera (missing params = defaults, rotation,
distance and FOV change the view, extremes stay finite, the sim key ignores the
camera); render particle_world_follows_the_comp_camera (a real camera layer
panned 40 px moves the particles left, and the cached render after the move
equals an uncached one: it failed with the old cache key); gpu
particle_world_through_the_comp_camera (CPU vs GPU at 8 / 32 bpc, full and
half resolution). particle_golden / sim_golden unchanged. effects.md
regenerated; parity.md and gaps.md describe the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:35:25 +07:00