Commit Graph
327 Commits
Author SHA1 Message Date
ash b9da77c63d Merge pull request #3571 from arnav-hiwarkar/fix/effect-ranges-easy-batch
Effects: 18 parameters take After Effects' valid ranges (Levels IC, 4-Color Gradient, Change Color, Noise HLS, Grid, Venetian Blinds, Radial Wipe, Scatter, Beam, Transform, Turbulent Displace)
2026-11-12 17:19:57 +05:30
ash bde8bea2bb Merge pull request #3564 from arnav-hiwarkar/fix/3535-analysis-inputs
Warp Stabilizer, 3D Camera Tracker: analysis keys cover masks, effects above and precomp contents (#3535)
2026-11-12 17:19:35 +05:30
ash 6ea9476ee4 Merge pull request #3560 from arnav-hiwarkar/fix/3538-ocio-file-reload
OCIO File Transform and custom OCIO configs follow their files on disk (#3538)
2026-11-12 17:08:11 +05:30
Dylan M. Taylor 4d6036265b Convert Expression to Keyframes: refuse layers of more than 2^18 frames
* fix(engine): refuse to convert an expression on a layer of more than 2^18 frames

* address review: refuse a non-positive frame duration; count a partial last frame; check every selected layer before converting

* Convert Expression to Keyframes: cap at 2^20 frames (a 3-hour 60 fps comp is 648,000)
2026-11-12 06:29:38 -05:00
Dylan M. Taylor 6e1dd08147 Import: a 3D model's left-out textures reach the user as an import warning
* Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting)

* Import: a model's left-out parts reach the user as an import warning
2026-11-12 06:29:14 -05:00
arnav-hiwarkar 28c63ce728 #3535: the analysis key leaves out the effects above while the Effects switch is off
The renderer skips the whole effect stack then, so editing those effects
doesn't change the analysed frames. Roto Brush Input Keys keep their text.
2026-11-12 16:30:24 +05:30
arnav-hiwarkar 22e66e3d97 Merge remote-tracking branch 'upstream/main' into fix/effect-ranges-easy-batch 2026-11-12 16:15:01 +05:30
arnav-hiwarkar 64c28b56d7 #3535: docs: which key covers what, why markers and audio are or aren't keyed 2026-11-12 15:47:50 +05:30
arnav-hiwarkar 98d82ae761 #3470: Turbulent Displace: Random Seed takes After Effects' range
Random Seed takes 0..100000, After Effects' recorded valid range (and what
the other effects' Random Seed already takes). The CPU and GPU hash it the
same way with wrapping u32 arithmetic, so any seed costs the same.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar 74bcb30540 #3471: Transform: Scale Height and Scale Width take After Effects' range
Scale Height and Scale Width take -30000..30000 %, After Effects' recorded
valid range in the same units. The CPU and GPU share the matrix
(transform_matrix_px) and the buffer growth, which is capped at 16 Mpx
(transform_grow; the CPU test already renders 100000 %), so larger scales
stay bounded.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar 3bd7de6ad8 #3475: Beam: Starting and Ending Thickness take After Effects' range
Starting and Ending Thickness take 0..4000, After Effects' recorded valid
range in the same units (pixels). The CPU and GPU renderers draw the beam
per pixel from its distance to the segment, within the layer's buffer, so
a thicker beam costs the same.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar c4cd9f1c2e #3477: Scatter: Scatter Amount takes After Effects' range
Scatter Amount takes 0..1000, After Effects' recorded valid range in the
same units (pixels); the slider stays 0..127. The CPU and GPU renderers move
each pixel by one random offset of up to the amount, so a larger amount
costs the same; samples outside the layer are transparent.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar a08240155c #3474: Radial Wipe: Feather takes After Effects' range
Feather takes 0..32000, After Effects' recorded valid range in the same
units. The CPU and GPU renderers evaluate the feather per pixel in closed
form (a Gaussian of the distance to the wedge, radial_wipe_alpha), so a
wide feather costs the same and stays finite.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar 29c76bb6b4 #3472, #3473: Venetian Blinds: Width and Feather take After Effects' ranges
Width takes 1..32000 and Feather 0..32000, After Effects' recorded valid
ranges in the same units (pixels). The work stays bounded: the renderers
floor the width at 1 buffer pixel, sum at most 40 Gaussian terms per
pixel, and above 8 widths the feather is the even veil
(venetian_cover; the GPU hands that case to the CPU, as before).
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar 85f47ec072 #3476: Grid: Border takes After Effects' range
Border takes 0..2000, After Effects' recorded valid range in the same units
(pixels). The CPU and GPU renderers compute the border per pixel in closed
form, so a wider border costs the same; one wider than the cells covers
them.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar ce4db78896 #3480: Change Color: Hue Transform takes After Effects' range
Hue Transform takes -1800..1800 degrees, After Effects' recorded valid range
in the same units. The CPU and GPU renderers wrap the hue (rem_euclid /
fract_euclid), so five turns render like the hue they end on, at the same
cost.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar 244f9bd846 #3483, #3484: Noise HLS: amounts and Grain Size take After Effects' ranges
Hue, Lightness and Saturation go up to 1000 % and Grain Size down to 0 (it
keeps its wider maximum, 100), After Effects' recorded valid ranges in the
same units. Noise HLS Auto shares the controls and widens with them.
Defaults and sliders are unchanged.

The renderers need no change: hue wraps, lightness and saturation clamp per
pixel after the noise is added, and Grain Size is floored at 0.1 px on the
CPU and the GPU, so the new values cost the same and stay finite.

The amounts were written as "percent" in keyframe text because their range
was 0..100, and Grain Size would become "percent" at 0..100; UNITS rows keep
the units they had.
2026-11-12 15:46:48 +05:30
arnav-hiwarkar 363a800d3f #3478, #3479: 4-Color Gradient: Blend and Jitter take After Effects' ranges
Blend goes up to 10000 (it keeps its minimum of 1, below After Effects' 5)
and Jitter up to 500 %, After Effects' recorded valid ranges in the same
units. Defaults and sliders are unchanged.

The CPU and GPU renderers capped Jitter's dither at 100 %, so higher values
would be stored but render as 100. Both now take it from one function,
four_color_jitter, which keeps the dither linear up to 500 %. Blend 10000
is a finite softening (Blend squared) at the same cost.

Jitter was written as "percent" in keyframe text because its range was
0..100; a UNITS row keeps that unit.
2026-11-12 15:14:34 +05:30
arnav-hiwarkar 39a3b565d8 #3487, #3488, #3489, #3490, #3491: Levels (Individual Controls): levels and gamma take After Effects' ranges
Input/Output Black and White of every channel take -10000..10000 and Gamma
goes down to 0 (it keeps its wider maximum, 10), After Effects' recorded
valid ranges in the same units (the .aep mapping is Conv::Plain). Defaults
and the 0..1 / 0.1..3 sliders are unchanged.

Gamma 0 renders as the existing 0.01 floor, an exponent of 100, so an HDR
value a few times Input White would overflow f32 and an infinite curve
times an empty output range is NaN. The curve is now bounded at +-1e6 on
the CPU (Lv::apply) and the GPU (fxt_lv), so the output stays finite over
the whole range; values below the bound are unchanged.

The engine regression (tests/effect_ranges.rs) sets both ends of each row
through prop.set, checks undo/redo, save and reopen, and that a project
saved with the narrower ranges opens with the new ones and its values.
2026-11-12 15:11:21 +05:30
arnav-hiwarkar 4d24233505 #3538: a custom config not read yet is read for its LUT files 2026-11-12 15:07:48 +05:30
arnav-hiwarkar 637d33f796 Merge #3550's branch: the frames key writes names; precomp layer names are keyed too (#3535) 2026-11-12 15:04:14 +05:30
arnav-hiwarkar 385b9530a1 #3535: Warp Stabilizer and 3D Camera Tracker keys cover a precomp's contents
On a precomp layer the key covered only the nested comp's size, rate and
duration, so an edit inside the precomp kept a stale analysis. The key now
writes the precomp layer's Essential Properties and what its comp draws, field
by field and recursively. Each comp is written once per key and nesting is
capped, so shared, deep or cyclic precomps are keyed in bounded time. Docs say
what clears an analysis.
2026-11-12 15:00:14 +05:30
arnav-hiwarkar 57990d9a47 #3535: Warp Stabilizer and 3D Camera Tracker keys cover the layer's masks and the effects above them
Their analyses are made from the layer's input to the effect (masks, then the
effects above it), but the key covered only the layer's frames, so a mask or
effect edit kept a stale analysis. frames_key::analysis_key adds the masks,
the effects above the instance and the Effects switch. Keys saved before
(frames only) stay valid while each edit keeps the input.
2026-11-12 14:48:30 +05:30
arnav-hiwarkar 94ab5d77cf #3536: pin the key text of non-default footage, source layer, Cineon, colour space and solid values; every mask mode keys apart 2026-11-12 14:47:05 +05:30
arnav-hiwarkar a700c5c8db #3538: layer keys and render reads include OCIO files and a config's LUTs 2026-11-12 14:43:54 +05:30
arnav-hiwarkar e04ae226a9 #3536: the frames key writes group and property names, and the uid of a property with an expression
Expressions find effects by name and read names (thisProperty.name,
propertyGroup(1).name), and seed random/wiggle with the property uid, so
both can change the frames. The Debug-based key had them.
2026-11-12 14:37:02 +05:30
arnav-hiwarkar 1c8d20a12f #3534: test a key 0.24.1 saved while the footage was offline; the frames key doc says what is read in full 2026-11-12 14:19:24 +05:30
arnav-hiwarkar 54cf31f841 #3534: footage going offline no longer drops saved analyses
Whether a footage file is there is an observation (footage.check sets
Footage::missing without an edit), not part of the footage. The
signature leaves it out, and keys saved by 0.22-0.24, which hashed it,
are recognised with the file either online or offline. Warp Stabilizer,
3D Camera Tracker and Roto Brush analyses are kept through the next edit
while the footage is offline and after it comes back.
2026-11-12 14:10:23 +05:30
arnav-hiwarkar 54d53fff76 #3536: one explicit analysis signature for Warp Stabilizer, 3D Camera Tracker and Roto Brush
analysis::frames_key writes what a layer's frames are made of field by
field: every struct and enum it writes is destructured or matched in full
and each variant is a fixed word, so a new field doesn't compile until it
is decided and a rename doesn't change keys. Names, labels, UI hints and
markers are left out. Roto Brush's Input Key writes masks and the effects
above it the same way, instead of their Debug text.

Keys saved by 0.22-0.24 are recognised by the old code, moved unchanged
to frames_key::legacy, and kept rather than rewritten (frozen mattes hash
them).
2026-11-12 14:08:57 +05:30
arnav-hiwarkar d361a6cb4e #3534, #3536: pin analysis keys saved by 0.24.1
A fixture keyed by the current release: Warp Stabilizer, 3D Camera Tracker
and Roto Brush keys keep matching through edits that keep the frames, undo
and save/reopen, and real changes still clear them.
2026-11-12 13:56:48 +05:30
ash 103760584a Merge pull request #3533 from arnav-hiwarkar/fix/3493-lut-cache
Apply Color LUT and Lumetri follow a LUT file that changes on disk (#3493)
2026-11-12 12:00:00 +05:30
arnav-hiwarkar d036127bf4 #3493: key a layer reading a precomp on the LUT file its override names
A precomp instance whose Essential Property names another LUT file renders
with that file, but a layer reading the instance through a layer parameter
was keyed on the nested comp's own file, so editing the overriding file left
that layer cached.
2026-11-12 11:29:57 +05:30
arnav-hiwarkar 58fcfeb54d #3456: test that placed-page footage doesn't match a 0.22 key
0.22 keys are hashed without placed_page, so only footage that isn't
placed as a page may match them.
2026-11-12 11:04:21 +05:30
arnav-hiwarkar e36e25512d #3456: solve again from 3D Camera Tracker tracks saved before 0.23
Analysing a tracker reused its saved tracks only when their key was
today's, so solving a 0.22 project again (after a Solve Method change)
tracked the footage again, and failed when it was offline.
2026-11-12 10:56:40 +05:30
arnav-hiwarkar cd08781aab #3456: keep Warp Stabilizer and 3D Camera Tracker analyses saved before 0.23
Their keys were compared only with today's signature, so the first edit
of a project saved by 0.22 (even adding it to the render queue, as
effectcraft-cli render does) cleared both analyses and queued them again,
and lost them when the footage was offline.

One check, warp::key_matches, now accepts a key made from today's
signature or from the one 0.22 wrote for the same frames, and keeps it.
Warp Stabilizer, the 3D Camera Tracker and Roto Brush all use it.

The regression test opens a project saved by the released 0.22.0 CLI.
2026-11-12 10:56:39 +05:30
arnav-hiwarkar 6a78d94a5b #3493: key cached layers and frames on the LUT files they read
A layer graded with Apply Color LUT or Lumetri's Input LUT and Look was
cached by the parameter text alone, so the same frame kept showing the old
LUT from memory and from the disk cache after the file changed. A layer's
cache key (and the key of a layer reading it, through a precomp too) now
holds each LUT file's size and modification time, and a render lists the
LUT files it read, so a frame kept on disk made with an older file is not
used.
2026-11-12 10:51:27 +05:30
arnav-hiwarkar 6780c65671 #3456: write the footage in analysis signatures field by field
The Warp Stabilizer, 3D Camera Tracker and Roto Brush signatures hashed
the footage item's derived Debug text, so every new Footage field changed
every signature and dropped every saved analysis (placed_page did in 0.23).
Write the same text from a named list of fields instead: a new field no
longer compiles until it is decided there. Pin the text in a test.
2026-11-12 10:44:57 +05:30
Dylan M. Taylor 004dcda1c7 fix(engine): name allocators resume each base's suffix instead of retrying from the start (#2996) 2026-11-11 14:37:43 -05:00
Dylan M. Taylor 20ed10da5f Shortcuts: list each binding once and cap the conflict list (#2997)
* fix(shortcuts): list each binding once and cap the conflict list

* address review: compute the shortcut bindings once per keyboard draw
2026-11-11 14:37:29 -05:00
Dylan M. Taylor 82ea2e7c3e Project panel: draw vector footage thumbnails at the thumbnail's size (#2998)
* fix(engine): draw vector footage thumbnails at the thumbnail's size

* address review: shrink a one-pixel-over vector thumbnail to the requested size
2026-11-11 14:37:14 -05:00
ash be5c98506f Merge pull request #3431 from lisn0/codex/frozen-object-matte-3430
Preserve frozen Object Mattes with pre-0.23 footage keys
2026-11-11 17:55:11 +05:30
arnav-hiwarkarandClaude Opus 5.5 5922dabd3a Time entry: a negative displayed timecode goes to the frame it shows (#3373)
In a comp whose Start Timecode is negative, the current-time display shows
the first frames as `-0:00:00:12`, but typing that back (the Go to Time
dialog opens with it since #3419, and the Timeline's time field takes it)
read the leading `-` as "move back", so OK on an unedited dialog jumped to
another frame. There, a `-` before a full timecode is now the time's sign,
as it already is before Feet + Frames; a bare `-n` still steps back, and
comps that start at 0 or later are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 16:19:46 +05:30
nazar-gavrylyk 5f1c5ad404 Time: synced comp times reach the final partial frame too
Synchronize Time of All Related Items clamped each related comp's time
with its own duration-minus-one-frame rule, so a nested comp whose
duration is off the frame grid could not reach its final partial frame.
Use comp_frame_at, as direct moves and restored times do.
2026-11-11 10:19:45 +01:00
nazar-gavrylyk c344ff8c23 Time: keep a composition's final partial frame reachable
A frame-rate change can leave the duration off the frame grid. The
current-time clamp used duration minus one frame, which put the last
reachable frame before a final partial frame, so a layer fitted to a
work area inside it never got the playhead. Clamp to the last frame
that starts before the end instead; on-grid durations are unchanged.
2026-11-11 09:55:54 +01:00
nazar-gavrylyk 1991ad5d56 Settings: fit tools move the playhead to the first frame inside the span
The work area need not sit on frames (a typed start, a frame-rate
change), so rounding its start to the nearest frame could land before
the new layer's in-point. show_span now rounds up to the first frame in
the span and moves nothing when the span holds no frame.
2026-11-11 09:15:58 +01:00
nazar-gavrylyk 34086899ad Settings: fit tools move the target comp's playhead, not the active one
show_span now checks and moves the playhead of the comp the layer was
made in, through a new Session::set_time_of that set_time uses for the
active comp, so time sync and the text editor boundary stay the same.
2026-11-11 08:47:25 +01:00
nazar-gavrylyk abdf4c369a Settings: move the playhead onto layers the tools fit to the work area
With Fit New Layers to Work Area on and the playhead outside the work
area, the Type tool, shape tools and Pen now move the playhead to the
work area start so the new layer and its controls show. Layer timing is
unchanged; Layer ▸ New commands leave the playhead.
2026-11-11 08:34:05 +01:00
nazar-gavrylyk 34f62efbca Settings: Fit New Layers to Work Area
Settings ▸ General ▸ Fit New Layers to Work Area (off by default) makes
new text, solid, shape, null and adjustment layers start at the work
area start and end at its end. It applies to Layer ▸ New, the shape
tools and the Pen tool, and wins over Create Layers at Composition
Start Time. Footage and compositions keep their own length.
2026-11-11 08:11:39 +01:00
lisn0 b8be50ea4b Use a released 0.22 project in the frozen matte regression (#3430) 2026-11-11 01:25:00 +00:00
lisn0 1f04ea571a Format frozen matte regression test (#3430) 2026-11-11 00:39:12 +00:00