Commit Graph
4536 Commits
Author SHA1 Message Date
arnav-hiwarkar 14d04536aa #3087: report a Photoshop layer item as big as neither its layer nor the document
Such an item is shown on the document, which the size does not confirm; it is now reported in unsupported_items, when the opti stores the document's size.
2026-11-11 22:02:46 +05:30
arnav-hiwarkar a6fba29c3e #3087: report a Photoshop layer whose framing the item's size cannot tell
An item as big as its layer's bounds was taken for Retain Layer Sizes. When
the layer is also as big as the document but not at its corner, a
document-framed item has the same size, so it may show shifted. The opti
record holds the document's size at bytes 32 and 36 (1000, 1000 in every
record of the issue's project, the .psd's size); with it, that case is
reported. The layer still shows at its own bounds.
2026-11-11 21:42:54 +05:30
arnav-hiwarkar 0e52ca784c Merge remote-tracking branch 'upstream/main' into fix/3087-aep-psd-layers
# Conflicts:
#	crates/aep/tests/it/import/synthetic.rs
#	docs/aep-import.md
2026-11-11 21:38:03 +05:30
ash 2e3c7f05d3 Merge pull request #3448 from arnav-hiwarkar/fix/3365-aep-loss-report
.aep import: every loss detail in the prompt; no false shape loss; Orientation expressions (#3365, #3087)
2026-11-11 21:33:09 +05:30
FredApps 230b3920de Release: EffectCraft v0.24.0 (#3494) 2026-11-11 16:57:18 +01:00
ash b03ebc4b83 Merge pull request #3455 from arnav-hiwarkar/fix/3454-lut-path-detection
Apply Color LUT loads files whose path contains LUT_ (#3454)
2026-11-11 19:18:06 +05:30
ash 4f00f11cab Merge pull request #3418 from nanox333/fix/3368-cylinder-radius
Cylinder Wrap: Radius goes up to 30000 % (#3368)
2026-11-11 19:06:30 +05:30
FredApps 006dcc962d #1615: Auto caps CPU frames in flight from free memory after a job switches to the CPU (#3458)
A GPU project whose Render Queue job measured the CPU faster (#3335) rendered
every frame of a batch at once on the CPU, one per thread, with no memory cap:
on a 16-thread machine at about 1.4 GB a frame the app ran out of RAM and
exited without an error (0.23.1, Windows).

While a job renders on the CPU after a GPU frame measured its working memory,
CPU frames in flight now fit half the memory the system has free at that size
per frame, 1 to one per thread, settled once per job and logged. The probe's
timed CPU frames use the same cap. EFFECTCRAFT_CPU_INFLIGHT still overrides
it; Software Rendering projects, with no measurement, are unchanged. The host
reads the free memory through the new JobOptions::free_memory.
2026-11-11 14:31:36 +01:00
Dylan M. Taylor 33a15e1d86 Scripting: export commands a script runs directly respect Allow Scripts to Write Files (#2921)
* Scripting: export commands a script runs directly respect Allow Scripts to Write Files

CompItem.saveFrameToPng and exportAsMotionGraphicsTemplate checked the
file-write preference only in their JavaScript wrappers. A script could call
comp.saveFrameAs or essential.exportTemplate itself (app.run, __exec) and the
native handlers wrote the file with the preference off.

Session::refuse_export and write_output_with now refuse when the running
command was issued by a script and the preference is off, so every command
that writes a path it is given is gated the same way. The wrappers check the
gate with a new side-effect-free __file("checkWrite") instead of writing an
empty file at the destination.

* address review: clear stale export test dir before use

* test: allow script writes in script_imports_footage_and_saves
2026-11-11 08:24:28 -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-hiwarkar 6540c9ab44 #3454: Apply Color LUT loads files whose path contains LUT_
load_lut took any source containing "LUT_" for inline .cube text, so a
path like /luts/LUT_Rec709.cube was parsed as text, failed, and the
effect passed the image through. A .cube can only parse with a size
header and data rows on separate lines, so a newline (after trimming,
as the path already is) is what tells text from a path.
2026-11-11 17:48:49 +05:30
ash 10607ff035 Merge pull request #3397 from JonRC/fix/3395-script-string-setvalue
Scripting: setValue stores string effect parameters, such as Apply Color LUT's LUT (#3395)
2026-11-11 17:41:58 +05:30
arnav-hiwarkar f6b4da0aeb Merge remote-tracking branch 'upstream/main' into fix/3365-aep-loss-report
# Conflicts:
#	crates/aep/tests/it/import/synthetic.rs
2026-11-11 17:19:01 +05:30
arnav-hiwarkar 6dbd751ae5 Merge remote-tracking branch 'upstream/main' into fix/3087-aep-psd-layers
# Conflicts:
#	crates/aep/tests/it/import/synthetic.rs
2026-11-11 17:18:38 +05:30
ash 284cec6e46 .aep: popup items match ignoring case outside ASCII too (#3432 follow-up) (#3447)
popup_key lowered only ASCII letters, so an item starting with a capital
outside ASCII (French "Écran") kept it and never matched the same word in
lower case. After Effects stores popup items in its UI language, and
#3432 starts matching such names through popup_alias, keyed by
popup_key. Use Unicode lower case; ASCII keys are unchanged.
2026-11-11 17:16:46 +05:30
sunkiddaandsunkidda 12260b288c .aep import: recognize French names for Levels' Channel and Clip popups (#3432)
* .aep import: recognize French names for Levels' Channel and Clip popups

After Effects stores a popup's items in the UI's own language. A project saved
by a French After Effects writes "RVB", "Activé" and "Désactivé pour la
couleur 32 bpc" for Levels' Channel and Clip to Output Black / White.
EffectCraft's own popup options are English only, so these never matched by
name and the parameter fell back to its default, reported as "has no
counterpart".

Add popup_alias, a small table of confirmed After Effects labels in other
languages, used as a fallback in popup_mapping after the exact name match
fails. A synthetic test reproduces the three losses from a real file and
checks they are gone, with the same values as the English original.

* Cover Levels' French Red, Green and Blue popup items too

popup_alias only mapped "rvb" (RGB), so a French Levels with Red, Green or
Blue selected still lost that selection the same way. Add "rouge", "vert"
and "bleu" (from the same real pdnm list, confirmed against it); "Alpha" is
already the same word in both languages. A direct test on popup_alias covers
every item of the Channel popup it claims to know.

---------

Co-authored-by: sunkidda <sunkidda@users.noreply.github.com>
2026-11-11 17:16:25 +05:30
arnav-hiwarkarandClaude Opus 5.5 6708964046 #3365: sort the unmapped effects with sort_by_key (clippy)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 17:15:43 +05:30
ash 367ef1f031 Merge pull request #3445 from arnav-hiwarkar/fix/negative-start-time-entry
Time entry: a negative displayed timecode goes to the frame it shows (#3373)
2026-11-11 17:15:14 +05:30
FredApps 3d1b6f4f0b Merge pull request #3446 from storytold/test/split-crypto-pick-matrix
test(ui-egui): split the crypto-pick viewer matrix into one test per window size
2026-11-11 12:27:20 +01:00
arnav-hiwarkar e2e3413011 #3087: an item made from one Photoshop layer shows that layer
Footage made from one layer of a layered .psd showed the whole document and
was named after the file. Its opti (tag 8BPS) stores the layer: the record
index (BE i32 at 14, -1 for the merged image), the bounds (LE i32 at 78-93)
and the name (UTF-8 at 344). The item now shows that layer as EffectCraft's
own Photoshop import does (at its own size with Retain Layer Sizes), named
<layer>/<file>. A layer without pixels (an adjustment layer) keeps the merged
image and is reported.
2026-11-11 16:56:00 +05:30
arnav-hiwarkarandClaude Opus 5.5 1f38431faf #3087: an expression on Orientation is imported with its value or keys
An Orientation with an expression was reported as "not decoded" and lost its
value and keys too. The expression is stored in the otst's tdbs like on any
property, so it now arrives with the stored value or keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 16:45:55 +05:30
arnav-hiwarkarandClaude Opus 5.5 b55226320b #3087: a shape property in the default state is no loss
Shape Direction, blend mode, composite order and the gradient highlight
values were reported when their record was in the default state (tdsb 3),
which holds After Effects' default (docs/aep-format.md 3.4), so a plain
guide rectangle reported "shape direction not imported". They are now read
only from written records, keys or an expression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 16:45:55 +05:30
arnav-hiwarkarandClaude Opus 5.5 6b7731d05a #3365: the losses prompt shows every detail line and how many have none
The prompt showed 3 detail lines per category under a bare count, which read
as the project's total. The headline now says how many occurrences are left
out or changed, every line the report keeps shows, and "… and N more" counts
the rest. The import report records that number as `unlisted` (an unmapped
effect's line covers all its instances); unmapped effects are listed most
used first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 16:45:55 +05:30
FredAppsandClaude Opus 5.5 47fc00f85d test(ui-egui): split the crypto-pick viewer matrix into one test per window size
picks_what_the_viewer_shows_across_sizes_scales_zoom_pan_resolution_and_proxies looped over four
independent window-size and display-scale cases, each with its own session. As one test it took
~90 s in the gate and ~590 s under coverage, where it alone set the slowest SonarCloud shard.

Each case is now its own test, running the same checks, so nextest runs them in parallel and
partitions them across shards. The "more than 100 hovers checked" floor becomes more than 25 per
case, which still adds up to more than 100.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 11:58:07 +01:00
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
ash 6aca5e473c Merge pull request #3419 from nanox333/fix/3373-viewer-time-click
Viewer: clicking the current-time display opens Go to Time (#3373)
2026-11-11 16:01:30 +05:30
ash 9287ddba3c Merge pull request #3420 from nanox333/fix/3365-group-lost-effects
AEP import: name every unmapped effect in the report, with its instance count (#3365)
2026-11-11 15:48:01 +05:30
FredApps c08a4cf672 Merge pull request #3435 from nazar-gavrylyk/feat/fit-new-layers-to-work-area
Settings: option to fit new layers to the work area
2026-11-11 11:11:54 +01:00
ash cc17fc64ff Merge pull request #3434 from arnav-hiwarkar/fix/macos-golden-hashes-2944
Effects: re-pin the macOS golden hashes #2944 changed by rounding
2026-11-11 15:34:10 +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
ash 4ac6cdb274 Merge pull request #3433 from arnav-hiwarkar/docs/render-queue-env-followup
Docs: scope EFFECTCRAFT_CPU_INFLIGHT to exports, document EFFECTCRAFT_RQ_RENDERER
2026-11-11 13:20:22 +05:30
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
arnav-hiwarkarandClaude Opus 5.5 a76bbf05f7 Effects: re-pin the macOS golden hashes #2944 changed by rounding
#2944 (497a056c) normalises each box-blur mean in f64 and rounds once
(it was rounded to f32 first, then scaled), and counts repeated edge pixels
with one multiply instead of adding them one by one. Its PR states results
change only by float rounding, and it added the focused regression test the
golden-test rule asks for (box_row matches a direct sum). The golden hashes
are only enforced on aarch64 macOS, so Linux PR CI passed, and every
completed macOS CI run on main since #2944 has failed (497a056c's own run
was cancelled by a newer push).

Bisected on an M5: the golden tests pass at ed085bd4 and fail at 497a056c
with the same 52 hashes macOS CI reports. All 52 are 32 bpc frames of
effects that blur through box_plane/gauss_plane (Lightning, Advanced
Lightning, Inner/Outer Key, Caustics, Paint Bucket, Vegas); every 8 bpc frame
is unchanged. All 52 frames differ by at most 4.8e-6 per channel, with no new
non-finite values.

Only the pinned hash values change; LIBM_VARIANTS is untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 12:41:14 +05:30
arnav-hiwarkar 861c65ab26 #3335: document EFFECTCRAFT_RQ_RENDERER and the Render Queue's per-job compositor choice
architecture.md and web.md still said every job renders on the project's
renderer (#414); since #3335 a GPU job times both compositors on its first
frames and keeps the faster.
2026-11-11 11:22:23 +05:30
arnav-hiwarkar f35392cd3e #1615: say EFFECTCRAFT_CPU_INFLIGHT caps Render Queue renders and exports, not previews 2026-11-11 11:22:23 +05:30
ash 4f601cbe1a Merge pull request #3423 from JonRC/docs/cpu-inflight-env
#1615: document EFFECTCRAFT_CPU_INFLIGHT in docs/preferences.md
2026-11-11 10:40:43 +05:30
ash 34dd0babed Merge pull request #3394 from MatejKozlovsky/gpu-box-blur-match
GPU: Fast Box Blur and Gaussian Blur rows round like the CPU
2026-11-11 09:51:54 +05:30
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
lisn0 3694f07600 Preserve frozen Object Mattes with pre-0.23 footage keys (#3430) 2026-11-11 00:37:34 +00:00
Dylan M. Taylor c30fb9aeed Sonar: [[ in the plugin-catalog check, Number.* in the boa runtimes, no Invoke-Expression (S7688, S7773, S8659) (#3427)
Clears all 29 open SonarCloud issues.

- `examples/plugin-catalog/scripts/validate.sh`: `[[ ]]` in place of `[ ]` for bash tests (SonarCloud shell:S7688). Every script has a bash shebang; no test uses `-a`/`-o`, escaped parentheses or an unquoted pattern on the right of `=`.
- `crates/expr/src/prelude.js`, `crates/script/src/prelude.js`, `crates/script/src/scriptui.js`: `Number.isFinite` / `Number.isNaN` / `Number.parseInt` (javascript:S7773). Every call already has a number (a `typeof` check or `Number()` before it), and `Number.parseInt === parseInt`, so nothing changes in boa.
- `packaging/windows/test-legacy-fixture.ps1`: dot-sources `[scriptblock]::Create($block)` in place of `Invoke-Expression $block` (powershelldre:S8659). It runs in the same scope, so `$LegacyWxs` is still set after it.
- `sonar-project.properties`: S7773 and S8786 are not raised on `*.jsx`. Those are After Effects ExtendScript (ES3), which also runs in After Effects and has no `Number.isFinite` or `String.prototype.trim`. The ignore covers 7 issues in `Ease Presets.jsx` and `Rename Layers.jsx`.
2026-11-10 19:04:34 -05:00
Dylan M. Taylor 27fa43fd52 text: caret and line lookups by bisection (#3029) 2026-11-10 18:00:52 -05:00
Dylan M. Taylor e2afb4b17c Timewarp: hold Block Size to its declared range and the matching window to a cap (#3024) 2026-11-10 18:00:27 -05:00
Dylan M. Taylor 8d46e1df7f Gradients: bound the stops sampled per pixel (#2950)
* Gradients: bound the stops sampled per pixel

Gradient sampling scans the stops linearly and shape painting samples once
per pixel, so a gradient with a huge stop list multiplied every frame's
work. Sampling now looks at the first 256 colour and opacity stops, and the
plain JSON form (coerce_json) refuses gradients with more than 256.

* address review: bound tagged gradients to the stop limit
2026-11-10 17:59:59 -05:00
Dylan M. Taylor e1ea71f540 Text: merge variable font axes by tag and cap their number (#2949)
* Text: merge variable font axes by tag and cap their number

Each tag in a variations object scanned the style's growing axis list, so a
large object cost quadratic time, twice (validation probe and apply). The
object is now merged in one keyed pass, refused when it names more than 64
axes, and a merge that would leave a style with more than 64 is refused
without changing the style.

* cargo fmt

* address review: refuse variation over every run before changing any

* address review: empty-document range cannot overflow in set_attr
2026-11-10 17:59:33 -05:00
Dylan M. Taylor 2c95408777 Auto-trace: bound the nesting, simplification and fitting work (#2945)
* Auto-trace: bound the nesting, simplification and fitting work

Many separate regions made the nesting depth test every pair of contours,
Douglas-Peucker could rescan a span once per point (and recurse as deep as
the loop is long), and the curve fit searched the raw contour from its start
for every simplified vertex.

- Depth: each loop's bounding box goes into a coarse grid; a loop's first
  point is tested only against loops whose box holds it. Same depths as
  testing every pair.
- Douglas-Peucker: spans wait on an explicit stack, with a budget of 64
  distance evaluations per point; spans still waiting when it runs out keep
  all their points, so the outline stays exact.
- Fitting: uses the raw indices the simplifier kept instead of searching.

* cargo fmt; clippy manual_is_multiple_of

* address review: keep wide contour boxes out of the nesting grid
2026-11-10 17:59:07 -05:00