Commit Graph
4816 Commits
Author SHA1 Message Date
FredApps 00a4903b5f Merge pull request #3230 from vangel76/script-expression-error-3219
Scripting: expressionError reports evaluation errors, as After Effects
2026-11-14 17:29:06 +01:00
FredApps 7cda51d7e5 Merge pull request #3222 from vangel76/expr-fn-decl-minus-3212
Expressions: a function declaration ends at its brace, so a next line starting with '-' is a new statement
2026-11-14 17:28:45 +01:00
FredApps 5c8e66aa1e Merge pull request #3242 from vangel76/prop-get-footage-3231
Expressions: prop.get and Reveal Expression Errors evaluate with the footage source
2026-11-14 17:28:24 +01:00
FredApps da611b2540 Merge pull request #3226 from vangel76/waveform-peaks-1722
Timeline waveform: read keyframed Audio Levels at layer time
2026-11-14 17:28:05 +01:00
FredApps 93bdc1f2f8 Merge pull request #3252 from vangel76/reversed-layer-audio-3239
Audio: a time-reversed layer plays its audio backwards
2026-11-14 17:27:44 +01:00
FredApps f89dd51bcf Merge pull request #3235 from vangel76/effect-controls-lock-3193
Effect Controls: lock the panel to the layer it shows
2026-11-14 17:27:24 +01:00
FredApps 94a8e5cabf Merge pull request #3214 from vangel76/effect-rename-select-3198
Effect Controls: renaming an effect opens with the whole name selected
2026-11-14 17:26:51 +01:00
FredApps ea2af8e677 Merge pull request #3228 from vangel76/shape-path-snap-2681
Viewer snapping: a point beats a line pair through it unless the pair is clearly nearer
2026-11-14 17:26:19 +01:00
FredApps c3cdaecdc6 Merge pull request #3240 from vangel76/shift-snap-axis-3229
Viewer: a Shift-constrained drag snaps only along its axis
2026-11-14 17:25:59 +01:00
FredApps ee31dd170d Merge pull request #3225 from vangel76/graph-theme-3202
Graph Editor: follow the theme (plot, bar buttons, grid, keys, handles, curves)
2026-11-14 17:25:26 +01:00
FredApps 987be06ec3 Merge pull request #3243 from vangel76/timeline-convert-bezier-menu-3237
Timeline: Convert To Bezier Path in the context menu of a parametric shape path
2026-11-14 17:25:06 +01:00
FredAppsandFredApps 5382b5ad01 Tests: the footage reload tests save a file in one step, so the watcher can't see it half written (#3689)
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-14 17:10:36 +01:00
FredApps 4579f91c94 Merge pull request #3211 from vangel76/frame-blend-interpret-3180
Frame blending averages the source frames a comp frame skips over (fast-conformed footage)
2026-11-14 17:04:22 +01:00
FredApps d438fdc2b2 Merge pull request #3217 from vangel76/pdf-continuous-raster-2895
Vector footage: faster, budget-safe clipping in the SVG/PDF rasteriser (refs #2895)
2026-11-14 17:02:30 +01:00
FredApps 05801ee111 Merge pull request #3210 from vangel76/grid-mask-3121
Effects: Grid, Checkerboard and Circle keep the layer's mask with Blending Mode None
2026-11-14 17:00:32 +01:00
FredApps 88bb97b839 Merge pull request #3258 from vangel76/cryptomatte-select-tool-2839
Cryptomatte: the Selection tool picks IDs while the effect is selected
2026-11-14 16:58:44 +01:00
FredApps a7c6d6a8fb Merge pull request #3249 from vangel76/stopwatch-expression-alt-3192
Timeline: Add Expression from the stopwatch's right-click menu
2026-11-14 16:55:14 +01:00
FredApps 0b0b3cdf9b Merge pull request #3308 from vangel76/colstab-sample-size-3125
Color Stabilizer: Sample Size is a square of that width centred on the pixel, partial pixels weighted, as in After Effects (#3125)
2026-11-14 16:49:32 +01:00
FredApps 4603c3873e Merge pull request #3319 from vangel76/aep-audit-f
.aep: huge or hostile files no longer import in quadratic time, silent losses now reported
2026-11-14 16:48:05 +01:00
vangel76 7bc7a61651 Merge upstream/main into pdf-continuous-raster-2895 2026-11-14 00:11:59 +01:00
Dylan M. Taylor 53576d2232 Handle CRLF Cargo manifest in packaging scripts (#3656) 2026-11-13 15:59:57 -05:00
FredAppsandFredApps 05fc7f2835 Tests: give the nested knockout draw-budget test two minutes, not twenty seconds, on a loaded runner (#3650)
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-13 20:27:01 +01:00
vangel76 1241a00a53 Pattern tiles: analyse the backdrop afresh inside a plain clip group
paint_pattern draws the tile content as a tree of its own on a fresh tile, so the
plain-clip state inherited from an enclosing group must not apply there: an inner
clip that blends against the tile was drawn as a plain clip and lost its backdrop.
Save and clear SurfaceBudget::plain around the tile draw, as apply_soft_mask does.
2026-11-13 20:14:33 +01:00
vangel76 0b312d82ab Merge upstream/main into pdf-continuous-raster-2895
Upstream gained region-limited coverage, clip regions, a group nesting cap
in the PDF builder and a render budget. The clipping fast paths (canvas-covering
clips drawn without a surface, plain clip groups on region-sized surfaces) are
ported onto those; the crop-based shape/image/paint changes and the PDF
builder depth cap are superseded by upstream's.
2026-11-13 19:35:53 +01:00
vangel76 fb7ce6928c Merge upstream/main into colstab-sample-size-3125 2026-11-13 19:06:14 +01:00
vangel76 2aefe27278 Merge upstream/main into reversed-layer-audio-3239 2026-11-13 19:06:02 +01:00
vangel76 b53b18366d Merge upstream/main into effect-controls-lock-3193 2026-11-13 19:05:57 +01:00
vangel76 20c4faf37c AEP import: precomp loop search counts only same-component users; only eligible layers are candidate links; popup keys lower whole strings (final sigma) 2026-11-13 18:42:33 +01:00
Andrea Zorzi a70eb03fa6 #3593: render_frame and render_preview tile times into one contact sheet (#3594)
* #3593: render_frame and render_preview tile `times` into one contact sheet

* contributors: credit andreaaazo by their GitHub profile name

* #3593: contact-sheet cells default to 320 px, sheets stay under 8000 px

With `times`, `max_side` now defaults to 320 per cell instead of 960, so
64 frames of a 16:9 comp make a 2560 x 1440 sheet instead of 7680 x 4320.
`sheet::MAX_SIDE` drops from 8192 to 7680. `labels` that isn't true or
false is a bad-arguments error, and a bridged cell keeps the time asked
for when the app's reply leaves `time` out.
2026-11-13 19:40:11 +05:30
vangel76 421769311a Merge upstream/main into aep-audit-f 2026-11-13 12:40:01 +01:00
Dylan M. Taylor 69bce0ec66 Merge pull request #3531 from storytold/codex/craft-libs-formats
Formats: move remaining file formats to craft-libs
2026-11-13 06:09:18 -05:00
Tyler JenningsandFredApps a5ddc93257 Animation presets: preserve text animators and selectors when saving and applying (#3168)
* Text animators: fit one animator to a Per-character 3D setting

* Animation presets: save text animators whole and add them to the target layer (#2530)

* Animation presets: a preset that fits no layer is an error, not silence

* Tests: text animators in animation presets (#2530)

* Fix text preset review edge cases

* Keep existing animators consistent when presets turn 3D off

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-13 09:13:02 +01:00
Dylan M. Taylor 0ef1637d5a Update EffectCraft pdf pin validator after mesh merge 2026-11-13 07:50:24 +00:00
Dylan M. Taylor dc9bf62870 Pin EffectCraft to merged craft-libs mesh 2026-11-13 07:18:50 +00:00
Sean Hawkey 0a6d264554 Import: a static shape path an expression draws is kept, empty, with its expression (#3606)
After Effects stores a static shape path that an expression draws (a text-box rig writes its
outline from the text's size) with no vertices. The importer dropped the whole path ('its path
has no vertices'), expression and all, so such shapes drew nothing. A path without vertices that
has an expression and no keys now arrives as an empty path with its expression; one without an
expression is still reported.
2026-11-13 07:41:03 +01:00
Sean Hawkey cf1898df4f Expressions: a text style's justification and direction use After Effects' names (#3607) (#3609)
* Expressions: a text style's justification and direction use After Effects' names

After Effects reads a paragraph's justification as alignLeft, alignCenter,
alignRight, justifyLastLeft, justifyLastCenter, justifyLastRight or
justifyFull, and its direction as left-to-right or right-to-left; its
setters take those names. EffectCraft returned its own keys (left, ltr,
justifyAll...), and the document refused alignLeft/alignCenter/alignRight
and justifyFull. The getters now return After Effects' names and the
parser accepts them; EffectCraft's own names are still accepted.

* Expressions: every accepted justification spelling reads back as After Effects' name

setJustification('CENTER_JUSTIFY').justification returned CENTER_JUSTIFY.
The setter and getter now share one table of the spellings Justify::parse
accepts, looked up as own properties only, so an alias reads back as
alignCenter and an inherited name such as toString is never matched.
2026-11-13 07:26:36 +01:00
Sean Hawkey 78eb99583a Scripting: ParagraphJustification has After Effects' numbers (#3610)
After Effects 26.3 numbers MULTIPLE_JUSTIFICATIONS 7412, LEFT_JUSTIFY 7413,
RIGHT_JUSTIFY 7414, CENTER_JUSTIFY 7415, and the justified variants with
the last line left, right, centred and full 7416-7419. EffectCraft had
centre before right (and last-line centre before last-line right), so a
script setting 7414 got a centred paragraph instead of a right-aligned
one, silently.
2026-11-13 07:06:04 +01:00
Dylan M. Taylor 6bd20d6082 Merge latest main with GPU test scheduling fix 2026-11-13 04:14:15 +00:00
8f21de0a0c ci: limit concurrent GPU tests in nextest (#3323)
Co-authored-by: skylartaylor <skylartaylor@users.noreply.github.com>
Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
2026-11-12 23:06:17 -05:00
Dylan M. Taylor 092a6136d3 Merge latest main with archived xtask path fix 2026-11-13 04:05:16 +00:00
skylar taylor-barrickandskylartaylor 547368febf test: resolve xtask from relocated nextest archives (#3322)
Co-authored-by: skylartaylor <skylartaylor@users.noreply.github.com>
2026-11-12 22:51:00 -05:00
Dylan M. Taylor 8dca7d4e8a Merge remote-tracking branch 'origin/main' into codex/778-effect-migration
# Conflicts:
#	crates/aep/src/container.rs
#	crates/aep/src/container/aepx.rs
#	crates/aep/src/container/arbitrary.rs
#	crates/aep/src/container/camera.rs
#	crates/aep/src/container/effects.rs
#	crates/aep/src/container/footage.rs
#	crates/aep/src/container/header.rs
#	crates/aep/src/container/keys.rs
#	crates/aep/src/container/masks.rs
#	crates/aep/src/container/read.rs
#	crates/aep/src/container/settings.rs
#	crates/aep/src/container/shapes.rs
#	crates/aep/src/container/textdoc.rs
#	crates/aep/src/container/typed.rs
#	crates/aep/src/container/values.rs
#	crates/aep/src/container/version.rs
#	crates/aep/src/container/write.rs
#	crates/aep/src/import/markers.rs
#	crates/aep/tests/it/container.rs
#	crates/aep/tests/it/container/batch3_core.rs
#	crates/aep/tests/it/container/batch3_objects.rs
#	crates/aep/tests/it/container/cases_600.rs
#	crates/aep/tests/it/container/header.rs
#	crates/aep/tests/it/container/helpers.rs
#	crates/aep/tests/it/container/keyframes.rs
#	crates/aep/tests/it/container/layer_switches.rs
#	crates/aep/tests/it/container/matte.rs
#	crates/aep/tests/it/container/robustness.rs
#	crates/aep/tests/it/container/text_markers_footage.rs
#	crates/aep/tests/it/container/typed.rs
#	crates/aep/tests/it/container/values.rs
2026-11-13 02:53:56 +00:00
Dylan M. Taylor 92274ec914 Merge remote-tracking branch 'origin/main' into codex/778-effect-migration
# Conflicts:
#	crates/aep/src/container.rs
#	crates/aep/src/container/footage.rs
#	crates/aep/tests/it/container/robustness.rs
#	crates/model/src/fbx.rs
#	crates/model/src/gltf.rs
#	crates/model/src/obj.rs
2026-11-13 02:26:44 +00:00
Codex 2042ee13fe AEP #3319: remove duplicate integrated report test module 2026-11-13 01:22:39 +01:00
FredApps 49e8c51d62 AEP #3319: integrate reviewed parser and expression changes 2026-11-13 00:57:49 +01:00
Valentin KleinandFredApps a4bde72bca .aep: import twice as fast, faster export (#3302)
* aep: a layer's markers and Source Text are read where they are stored, not searched for (#3276)

A layer's marker list is a direct child of its root tdgp, and its Source Text record
btds a direct child of the ADBE Text Properties group. Markers walked the whole layer
subtree twice per layer (most layers have no marker list, so the walk always ran to the
end), and the three text-document accessors walked it once each.

* aep export: build the prototype template once, sort names with cached keys (#3277)

The template every export clones from is immutable; it is now built by the
first export of the process and kept (about 0.93 MB). The name sort builds
each natural key once instead of on every comparison. Root solids move into
the Solids folder instead of being copied. The two key-list rewrites share one
emit-local helper and match names compare as bytes instead of a decoded String.
Output is byte-identical.

* aep container: size sibling lists, hex and write output once; borrow plain .aepx attributes (#3275)

- read.rs: count the chunk headers of a sequence first and allocate the
  children Vec once (bounded by the input and by the chunk limit).
- aepx.rs: decode_hex sizes its output once; attribute values without
  an entity are borrowed instead of copied through decode_entities.
- typed.rs: PropGroup::get compares match names without building a
  String (same result as the lossy compare, invalid UTF-8 included).
- write.rs: reserve the exact output size up front (try_reserve, so a
  hand-built tree cannot abort on the allocation).
- read.rs, settings.rs, values.rs: use the shared bytes.rs readers.

Output is identical on the corpus (tree, written bytes, import).

* aep container: review fixes (#3275)

- read.rs: reserve with try_reserve_exact, so a raised chunk limit on a
  small target cannot abort before parsing; the capped reservation and a
  deep, retry-heavy nest of broken lists are pinned by tests.
- write.rs: a tree past the 32-bit container size fails with TooLarge
  before anything is serialised.
- aepx.rs: drop an unneeded local and lifetime annotation.

* aep export: note what the concurrency test covers and why a failed template build is not kept (#3277)

* aep: text documents are read without a String per token, defaults built once (#3276)

TextDocument::parse reads a borrowed tree: names, bare words and strings without
escapes point into the body, keys are borrowed when they are /names, numbers parse from
the borrowed bytes, and dictionaries and arrays are moved into vectors of their exact size
when they close. The style and paragraph defaults are built once instead of per document.
parse_text_dict keeps its public TextValue result (the borrowed tree turned owned).

The old owning parser stays as a test-only oracle: a differential test compares both on
40 000 generated bodies (every key form the owned keys canonicalise, non-UTF-8 names,
escapes, unclosed strings, non-finite numbers), every cut of a realistic document and
nesting around the depth limit.

* aep: text-dict test builds its prefix without repeat(1) (#3276)

* aep: an escaped text string drops its byte order mark without a range drain (#3276)

* aep export: one key-time loop, key-list rewrite, tdmn decoder and byte writer for the listed modules (#3278)

In masks, shapes, styles, synth, fxsynth, base, camlight, skeleton,
textanim and tree (effects.rs, plan.rs and emit.rs keep their copies for
the follow-up work):
- New export/keys.rs: the key-time loop (timed_keys), the key-list rewrite
  (fill_key_list, set_key_list, replace_cdat) and the spatial key record,
  shared by masks, shapes and styles.
- One tdmn decoder (synth::tdmn_name, lossy UTF-8 up to the first NUL, as the
  importer reads it) and one encoder (synth::tdmn) instead of the copies in
  masks, styles, shapes, textanim, camlight and skeleton.
- One byte writer: tree::put, which errors. base::bytes, synth::tdb4,
  synth::key_list, fxsynth::pard, masks::shph and the keyed path records write
  through it; their fixed layouts keep the infallible signatures behind
  tree::fixed, which logs, and a test builds every layout and requires no error.
- camlight::put_group uses the shared group split (tree::split_group), which
  errors on a match name without a node instead of stopping quietly.

Export output is byte-identical on the corpus and the synthetic fixtures.

* aep export: pin the key-time loop's report, name the layout in the base::bytes log (#3278)

* aep export: review fixes for #3278

- tree::Fixed replaces tree::fixed: a fixed layout writes every field that
  fits (as the old code did) and keeps the first error; finish() logs it
  once with the layout's description and counts it per thread, so the
  every-layout test cannot race with other tests.
- base::bytes logs one line naming its length and fields.
- masks::build_atom errors when the prototype has no Mask Shape instead of
  dropping the path of every mask quietly.
- log sorted into the dependency list.

* aep: review fixes for #3276 text documents and markers

text_document reads the first btdk in the Source Text record, the documents
text_document_count counts. The old slot checks (same, is_neutral, undecoded_fields)
join the test oracle and are compared on 20 000 generated slot dictionaries; mixed
dictionary/array chains and a document left open at the depth limit join the parser
differential. An escaped string's byte order mark is drained without a copy.

* aep: remove test-only container accessors, share report and time-base code (#3281)

- Remove 35 public container accessors that only tests called (header,
  camera/light, keys, footage, masks, shapes, version) and the dead
  StyleMap::switch_name. Tests now check the importer's own reading of the
  same bytes, or the decoder it uses; three import tests gain the values
  the removed checks covered.
- Import and export reports share one loss entry (Loss<C>) with record,
  total, count and summary; the JSON shape is pinned for both.
- Collector::lose_with builds a detail only below the detail cap (callers
  switch later).
- The export uses the container's TimeBase; one NTSC table serves import
  and export; TimeBase::frames saturates like units instead of returning 0.

* aep: review fixes for #3281: NTSC table beside TimeBase, real-path NTSC round trip, light and camera defaults on import, doc wording

* aep: drop a local check tool committed by mistake (#3281)

* aep export: sort keys as one compact byte string, bounded by the name (#3277)

The cached natural keys were Vecs of (bool, String, u128) segments: a 255-byte
name of alternating letters and digits took about 14 KB, held for every item at
once. Each key is now one byte string whose byte order is the natural order,
at most 5 bytes per name byte. Order and output are unchanged.

* aep container: check bdata digits and size before reserving (#3275)

decode_hex reserved the decoded size before it knew the value decodes,
so a large bdata with a bad digit reserved half its length first. It now
refuses a value larger than a chunk's 32-bit size field (TooLarge), checks
every digit in one vectorisable pass, and only then reserves, with
try_reserve_exact so a value that does not fit in memory is an error
instead of an abort.

* aep export: the every-layout test counts from a baseline, not from zero (#3278)

* aep export: correct the order the natural sort key doc gives for equal numbers (#3277)

* aep: the format notes name the accessors that remain (#3281)

* aep: the format notes name the 1 x 1 mask scale of text and shape layers (#3281)

* aep: read imports through a flat tree index (#3300)

Import reads an .aep through TreeIndex, one depth-first array of chunks that
borrows the file's bytes, instead of building and freeing the owned Chunk tree
(one Vec per list). The parser mirrors read::Parser step for step: the same
chunk count order against max_chunks, the same depth limit and opaque
fallbacks, the same errors. The typed view reads through Node, a handle to a
chunk of either tree, so the accessors work unchanged on both; .aepx keeps its
owned tree. Chunk, parse and write stay for export and editing.

The array starts small and doubles, never past what the input can hold or the
chunk limit allows; a failed allocation is TooLarge, a tree past u32 indices
TooManyChunks.

* aep export: the time remap and Transform keys use the shared key-list and match-name helpers; doc comments for the audit's functions (#3300)

* #3300: import .aepx through the flat index

The .aepx reader no longer builds the owned chunk tree element by
element. It decodes every body (hex, text, alias JSON, numbers) into
one buffer and records the chunks as depth-first entries whose bodies
are ranges of that buffer; TreeIndex::from_drafts turns them into the
index the importer reads, which borrows the buffer. read_aepx converts
that index into the owned tree in one pass.

The tree and the errors are those of the previous reader, which the
tests keep as the reference: generated and damaged documents under
seven limit sets, invalid UTF-8, hostile input, the fixture directory,
and imports through both. Bound tests pin the chunk list to the input
and the limit, and check that dropped content leaves nothing behind.

* #3300: read_aepx keeps its own owned-tree reader

read_aepx, read_aepx_with_limits and parse_any build the owned chunk
tree element by element again, as before the .aepx index, instead of
converting the index (which held the decoded buffer and the owned tree
at the same time). Import keeps the index reader. The tests now
compare the two production readers.

* #3300: test keys that round to the same time

* aep: compare the destination projects in the index-equivalence tests

both() and both_aepx() kept only the ImportResult of each path; they now keep
both destination Projects and assert structural and serialized equality, so a
layer or property value regression cannot pass on equal ids and reports.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-13 00:54:29 +01:00
Valentin Klein 10a34b854f .aep import/export: text animator Stroke Opacity and Source Text expressions (#3170) (#3305)
* #3170: .aep import of the text animator Stroke Opacity and the Source Text expression

ADBE Text Stroke Opacity maps to the animator's strokeOpacity (percent, as
After Effects shows it). The Source Text expression is imported like any
property's (Utf8 text, tdb4 bytes 119/120); one whose flags give no state
is still reported. New committed fixture text-synthetic.aep, made in After
Effects 2026 with the working space None.

* #3170: .aep export of the Source Text expression and the text animator Stroke Opacity

The Source Text expression is written enabled or disabled as After Effects
stores it (tdb4 bytes 119/120, Utf8 after cdat); a disabled one is no longer
dropped without a report. strokeOpacity is written as ADBE Text Stroke
Opacity with the record After Effects wrote in text-synthetic.aep. A test
exports the fixture and imports it again.

* #3170: doc comments on the functions this change touches
2026-11-13 00:16:37 +01:00
Valentin Klein 8028f46808 .aep import: a test pins the Orientation expression import (#3087) (#3313)
* .aep import: Photoshop layers, placeholder effects, shape direction and Orientation expressions (#3087)

- A footage item showing one Photoshop layer shows that layer (record index, bounds and name
  from its opti) and is named <layer>/<file>; an adjustment layer shows white where it applies
  (opt-in through SourceLayer::adjustment_white, the direct Photoshop import is unchanged).
- Effects without a counterpart arrive as placeholder effects with their parameters, so
  expressions reading them keep working; pseudo effects are no loss, a Dropdown Menu Control
  becomes EffectCraft's. Slider ranges are checked, sizes are bounded.
- The loss prompt shows every detail line the report keeps and how many more there are.
- Shape direction and gradient highlight records in the default state are no loss.
- An expression on Orientation is imported with its value or keys.

* .aep import: report placeholder records and popup defaults that borrowed definitions leave open (#3087)

A value record the borrowed definitions do not name is reported instead of dropped, and a
borrowed popup without a record reports that its default item is not known (the first is used).

* Merge upstream/main into aep-import-bugs-3087
2026-11-13 00:15:49 +01:00
vangel76 9c36d34295 Export report: compute unlisted for losses beyond the detail cap 2026-11-12 23:57:25 +01:00
vangel76 33aa49a291 Merge remote-tracking branch 'upstream/main' into aep-audit-f 2026-11-12 22:52:40 +01:00