Commit Graph
140 Commits
Author SHA1 Message Date
pravdomilandClaude Opus 5.5 07639b5f4b Settings: Save Compact Project Files writes .ecproj as one-line JSON
Settings ▸ Project ▸ Project Files ▸ Save Compact Project Files (off by
default) saves .ecproj files as compact JSON: smaller, faster to save and
open. Off, they stay indented, one value per line, so they read and diff
cleanly in version control. XML copies are not affected; auto-saves were
already compact. The project.ecproj inside exported template and Motion
Graphics template packages is written compact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 20:06:02 +01:00
BFlat eccb1883e2 Merge pull request #1092 from vangel76/fix-1083-beat-marker-audio-pause
Markers: adding one during playback no longer stops the audio (#1083, #1085)
2026-10-30 01:23:37 +07:00
BFlat 7099fe0bae Merge pull request #1874 from vangel76/rfx-masks-shapes
Mask feather, Repeater opacity and Trim Paths on rectangles render as in After Effects
2026-10-30 01:21:47 +07:00
BFlat b669e4a951 Merge pull request #2476 from ashokram-si/feat/targa-sequence-output
Export: Targa (.tga) sequence output, 24-bit RGB or 32-bit RGB + Alpha
2026-10-30 01:19:27 +07:00
bflatasticandClaude Opus 5.5 3d45065add Project: reads_markers stops at the first expression and allocates nothing
It runs for every frame identity and disk-cache key; walk() formatted a path per node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:21:33 +07:00
bflatastic 1fde74b5f2 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-29 23:19:56 +07:00
dotCooCoo 32f058ea05 Merge main into feat/wiggle-paths-spatial-phase 2026-10-28 05:39:33 -07:00
Dylan M. Taylor d171d6cf88 Refactor project for strict Clippy checks 2026-10-28 06:09:21 +00:00
vangel76andClaude Sonnet 5.5 9fc04e8fe3 Merge upstream/main into fix-1083-beat-marker-audio-pause
Conflicts came from upstream splitting crates/project/src/lib.rs and moving the
frames tests. Re-applied the branch's change in the new places:
- Layer::same_pixels_with now in crates/project/src/layer.rs
- Comp::same_pixels_with, same_settings(o, with_markers), pixel_form_with and the
  timing-marker helpers in crates/project/src/comp.rs (same_pixels_ignoring_markers
  stays removed)
- Project::reads_markers in crates/project/src/item.rs
- crates/ui-egui/src/frames.rs uses reads_markers/same_pixels_with; the branch's
  two marker tests replace the old ones in crates/ui-egui/src/frames/tests.rs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 17:09:17 +01:00
vangel76andClaude Sonnet 5.5 ca00da2f55 Merge upstream/main into rfx-masks-shapes
render: upstream moved the tests_*.rs modules into crates/render/src/tests/; the branch's
tests_mask_feather.rs now lives at tests/mask_feather.rs and is registered in tests.rs.
path: rect() keeps the branch's explicit-outline version with an added NaN guard on the
roundness clamp bound (main's fix for the same panic).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 16:57:13 +01:00
ashokram-si de27e1ea0a Merge main into feat/targa-sequence-output 2026-10-27 10:46:47 +05:30
aiongg 55ab7b556c refactor(project): move settings, comp, layer, footage and item types out of lib.rs 2026-10-26 21:49:11 +00:00
ashokram-si c51a08ceb4 Export: Targa (.tga) sequence output, 24-bit RGB or 32-bit RGB + Alpha
After Effects lists Targa among its still-image output formats; EffectCraft
imported Targa (#745) but could not render to it.

- OutputFormat::TargaSequence ("Targa Sequence", `.tga`, numbered like the
  other sequences; `tga` / `targa` as a format name or output extension).
- Written by the `image` crate's Targa encoder (its `tga` feature, already
  enabled in effectcraft-media; no new crates, Cargo.lock unchanged), with
  run-length compression off: an 18-byte header, top-left origin, 24-bit BGR
  for RGB, 32-bit BGRA with 8 alpha bits in the image descriptor for
  RGB + Alpha (straight or premultiplied), 8-bit grey for Alpha.
- The render queue's size estimate counts the uncompressed bytes.
- CLI usage, the renderQueue command specs, README, architecture.md and
  file-format-parity.md name the new format.

Tests: each Targa frame holds exactly the pixels of the PNG sequence of the
same job, with the expected header, numbering and size; the frames read back
through the Targa importer as the same picture (straight, and premultiplied
when interpreted so); the image-sequence round trip, the format parser and
the renderQueue commands cover it too.
2026-10-26 15:34:12 +05:30
dotCooCoo 372af0f21e Shapes: upgrading a saved project walks nested shape groups without recursion
upgrade_shape_contents now visits nested groups with a work list, so a
project with very deeply nested shape groups does not run out of stack
while Wiggle Paths gains Spatial Phase. The new test nests a Wiggle
Paths 100,000 groups deep and upgrades it on a 2 MiB stack.
2026-10-25 20:13:34 -07:00
dotCooCoo 8ebacda419 Shapes: Wiggle Paths has Spatial Phase, which slides its wiggle along the path
Wiggle Paths had Size, Detail, Points, Wiggles/Second, Correlation,
Temporal Phase and Random Seed, but no Spatial Phase, which After
Effects lists among its properties (ADBE Vector Spatial Phase). Spatial
Phase now moves each point's own displacement along its subpath: 360
degrees is once around, and a fraction of a point is interpolated
between neighbors. At 0 degrees a path draws as before.

A project saved without the property gets it at 0 degrees, after
Temporal Phase, when it opens. Scripts can read and set every Wiggle
Paths property by its match name.
2026-10-25 16:06:28 -07:00
Dylan M. Taylor e821feeec4 Merge pull request #2274 from storytold/sonar/rust-s4325-s1612
sonar: method paths instead of closures (rust:S1612)
2026-10-25 16:36:06 -04:00
vangel76andClaude Sonnet 5.5 0de5887fe7 Format the merge
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 10:33:59 +01:00
vangel76andClaude Sonnet 5.5 4c8b89a6be Merge upstream/main into fix-1083-beat-marker-audio-pause
Conflicts in project/src/lib.rs and ui-egui/src/frames.rs: Comp::same_settings
takes with_markers and is shared by same_pixels_with and changed_span (#1641);
frames identity uses same_pixels_with/reads_markers and drops
same_pixels_ignoring_markers and project_has_expressions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 10:33:46 +01:00
aiongg 991904888a Merge pull request #1643 from arnav-hiwarkar/fix/1641-keep-cache-outside-edit
Preview: an edit keeps the cached frames outside the layers it changes (#1641)
2026-10-24 16:27:16 -04:00
vangel76andClaude Opus 5.5 5289b2e853 Merge upstream/main into fix-1083-beat-marker-audio-pause
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:02:37 +02:00
Dylan M. TaylorandClaude Sonnet 5.5 4e9fa51f12 sonar: method paths instead of closures (rust:S1612)
Poison recovery, OsStr::to_str and char predicates only; S4325 casts are
f64 to f32 and stay.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 08:17:16 +00:00
aiongg d88a604e8d Merge #2041: Save: check the file's values without reparsing it in release builds (@arnav-hiwarkar) 2026-10-24 01:12:11 +00:00
skylar taylor-barrick b55cb5bc15 Merge pull request #1363 from DWAA1660/fix/1237-comp-contains-cycles
Project: comp_contains walks precomps with a visited set, not recursion (#1237)
2026-10-22 07:52:56 -06:00
arnav-hiwarkarandClaude Opus 5.5 c9323afe1d Save: check the file's values without reparsing it in release builds
- New `crates/project/src/file_check.rs`: a no-output `Serializer` that walks the value once. It refuses non-finite numbers and nesting past 127 levels, and it allocates only on error.
- `to_file_json_with_view` runs that check, then writes the file. It also refuses a project schema newer than this build, the remaining thing `from_json` would reject in a file we wrote.
- **Debug builds still reparse the text as well**, so a new kind of mismatch still shows up in tests.

The bytes written are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:34:59 +05:30
vangel76andClaude Opus 5.5 25de452804 Masks: feather falloff as After Effects
The Smooth feather is three separable box passes of 2 round(0.37 F) + 1
pixels (a quadratic B-spline reaching ~1.11 F either side), fitted to
After Effects 2026 renders of feather 100, 300 and 1000 (rows, columns,
corner diagonal, case 398): max error under 1/255, where a Gaussian of
any width misses by 3/255 or more (it was sigma = F / 2). The coarse
grid for wide feathers picks the step whose three windows add up
closest to the full-resolution ones. Linear is unchanged (unmeasured).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:33:44 +02:00
arnav-hiwarkarandClaude Opus 5.5 c28daa479b #1714: test several comps, negative and malformed saved times
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:35:12 +05:30
arnav-hiwarkarandClaude Opus 5.5 ea67c46880 #1714: doc comments name the saved current times
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:40:44 +05:30
arnav-hiwarkarandClaude Opus 5.5 b9172fec2f #1714: reopen each comp at its saved current time
Layer times were already saved and read back as exact ticks; the values in
the issue are 34f, 315f and 3033f at 59.94 fps. The viewer was black because
the playhead wasn't saved: a reopened comp started at 0, before its layers.
The project file's view (`activeComp`, `openComps`) now also records each
comp's current time (`compTimes`), restored on open within the comp's frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:32:14 +05:30
arnav-hiwarkarandClaude Opus 5.5 6c3a08e780 Preview: an edit keeps frames only outside Solo changes and the shutter (#1641 review)
Turning Solo on or off hides or shows every other layer at every frame, so it
now recaches the whole comp. The kept span is widened by the comp's shutter
(phase and angle), since a frame's motion blur samples cameras, lights and
transforms up to three frames away. Comp::any_solo is shared with the
renderer so both read Solo the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 02:50:48 +05:30
arnav-hiwarkar 4fff7c624a Merge main into #1643 2026-10-17 02:38:53 +05:30
arnav-hiwarkarandClaude Opus 5.5 17edbbffff Preview: an edit keeps the cached frames outside the layers it changes (#1641)
The RAM preview keyed every frame by one identity of the whole comp, so any
edit (a cut, a trim, a nudge) made every cached frame a miss and the whole
comp recached. When only some of a comp's layers change, frames outside their
In-Out span now move to the new identity. Settings changes, reorders,
expressions, and edits that reach other layers (parents, track mattes, effect
layer pickers) still recache everything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 16:43:57 +05:30
midasmatterhorn 643dcf55c6 project: a hostile ROI cannot overflow the crop math
The roi quad is stored raw from the control channel (crop.useRoi passes
serde_json's [f64;4] straight through, unlike the sibling edge fields,
which are clamped to plus/minus 20 000) and nothing repairs it on load.
Crop::edges converted it with round() as i32 and subtracted raw: an x of
-1e12 saturates to i32::MIN and w as i32 - l overflows in debug builds,
wrapping to a wrong-but-bounded crop in release.

Clamp each converted edge to the same plus/minus 20 000 the sibling
fields use and subtract with saturating_sub in edges() and frame_size().
Regression test: hostile_roi_cannot_overflow_crop_math — panics at
crates/project/src/render_queue.rs:774 on main; with the fix the hostile
quad degrades to a 1x1 frame and the sane-ROI behavior is unchanged.
2026-10-15 16:18:10 +03:00
DWAA1660andClaude Opus 5.5 b26a340a7e Project: comp_contains walks precomps with a visited set, not recursion (#1237)
Project::comp_contains recursed through precomp layers with no visited
set or depth limit. A damaged project with a precomp cycle that doesn't
pass the queried comp (A and B nesting each other, C unrelated) recursed
until the stack ran out, which aborts the process ("stack overflow,
aborting"): it can't be caught as a command error, so unsaved work is
lost. layer.addItem checks it before adding a precomp, and the renderer
calls it for every nested comp and its audio. A long chain used the
stack the same way, and comps shared by many paths were walked once per
path (exponential for nested diamonds).

It is now an explicit stack with a visited set: same answers (a comp
contains itself; a target reached through a cycle is found), each comp
visited once, no recursion. The renderer already skips a precomp that
contains the comp being rendered, so a cyclic project now renders
instead of crashing; nothing is repaired on load.

Tests: project comp_contains_ends_on_cycles_long_chains_and_shared_
precomps (cycle with an unrelated target, self-nesting, a target through
a cycle, 40 levels of diamonds, a 20 000-comp chain); engine
a_project_with_a_precomp_cycle_opens_adds_and_renders_without_running_
out_of_stack (file.open, layer.addItem both ways, render). On main the
engine test aborts with a stack overflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:26:42 -06:00
vangel76andClaude Sonnet 5.5 6bf9c87a1a Markers: the disk key keeps protected markers with a duration and drops a lone plain comp marker (#1083)
pixel_form_with borrowed the comp unchanged when it held only comp markers and no layer
markers, so a lone plain comp marker still reached the disk key. Tests: a lone comp marker
keeps the key; a protected marker with a duration (and its duration) on a stretched precomp's
source changes the key of the comp that nests it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 19:07:25 +02:00
vangel76andClaude Sonnet 5.5 46aa69482d Merge upstream/main into fix-1083-beat-marker-audio-pause
Upstream's #1167 fix and this branch both stop marker edits from invalidating
cached frames. Kept this branch's shared Project::reads_markers/same_pixels_with/
pixel_form_with (viewer frames, disk cache, nested comps) and folded upstream's
rules into them: any expression counts as reading markers, protected time
markers with a duration still change the frames.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 14:17:47 +02:00
Brandon Thomas 430c968e52 Merge pull request #933 from Themolx/replace-with-precomp
# Conflicts:
#	crates/engine/src/tests_project_items.rs
2026-10-13 04:31:42 -04:00
Brandon Thomas a985dfb36b Merge pull request #1098 from vangel76/feat-gap21-percent-guides-effect-labels
# Conflicts:
#	crates/engine/src/tests_menu_cmds.rs
#	docs/effects-parity.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-13 04:29:06 -04:00
Brandon Thomas a43cf2002a Merge pull request #1075 from vangel76/fix-1061-rename 2026-10-13 04:29:04 -04:00
Brandon Thomas 37b681ac12 Merge pull request #1104 from eliasx45/upstream/import-project 2026-10-13 04:28:59 -04:00
Brandon Thomas 15d2030d73 Merge pull request #1051 from marcoarnulfo/fix/interpret-loop-video-duration 2026-10-13 04:28:37 -04:00
Brandon Thomas c5d2a54507 Merge pull request #1013 from Themolx/unique-new-comp-names 2026-10-13 04:28:36 -04:00
Kristian MyramandClaude Haiku 5.5 863a6aff6c #1167: frames: keep the viewer frames when only plain markers change
Adding a marker replaced the comp, so every cached frame stopped matching and the preview had to
re-render before its audio could carry on. Plain markers change no pixels; protected time markers
with a duration still do (Responsive Design). Markers count in full again when a project uses
expressions, which can read them.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-13 02:20:31 +02:00
vangel76andClaude Sonnet 5.5 f11792827e Gap 21: percentage guides and effect colour labels (#1095)
Guides can store a percent of the comp so they follow comp-size changes
(View > Add Percentage Guide, Convert Guides to Percentage / Pixels); an
effect instance has an optional colour label (effect.setLabel, Effect Controls
header bar and Label menu). Both are serde-defaulted, so old projects load.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:07:36 +02:00
vangel76andClaude Sonnet 5.5 cdd6d83abb Markers: the disk cache key ignores them too unless an expression reads them (#1083)
Project::reads_markers and Comp::pixel_form_with are shared by the RAM and disk frame caches.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:52:29 +02:00
vangel76andClaude Sonnet 5.5 6a8166491c Markers: adding one during playback keeps the cached frames and the sound (#1083, #1085)
Markers counted as pixels in a comp's frame identity, so every marker edit
dropped the cached frames; the playing preview then hit an uncached frame
and stopped its sound. They now count only when an expression mentions markers.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:47:37 +02:00
Elias 376f5757fc file.importProject: import an EffectCraft project into the open project
File > Import > EffectCraft Project (and .ecproj/.ecprojx given to File > Import)
merges the project into a folder named after the file, with every id shifted past
the open project's, in one undo step. Relative footage resolves next to the file;
the result lists comps, root comps, primaryComp and missing media.

offset_ids now shifts tracker Motion Targets and saturates instead of overflowing;
fix_next_id saturates too, so a damaged file can't overflow on open or import.
Menu entry translated in all ten menu catalogues.

Rebased onto current upstream main for the split of #858 (test module list in
engine lib.rs; es/pt-br/ru/fr/de/cs menu translations).

(cherry picked from commit 19c653fccc95133fb35e978a7f7f20a18c0e8c89)
(cherry picked from commit 4e751ad2)

(cherry picked from commit ceed706cc3b5f3755769738a1ddb3d965a94ab6b)
2026-10-12 19:19:21 +01:00
vangel76andClaude Sonnet 5.5 42c7a7141e #1061: Enter renames the selected effect, text animator, mask or shape group
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 19:40:37 +02:00
Marco Arnulfo a50e49df73 Interpret Footage: looped footage lasts its loops on new and re-timed layers (fixes #1045) 2026-10-12 17:13:33 +02:00
ThemolxandClaude Opus 5.5 fcf56415dc Project panel: Replace with Precomposition (used N times)
Right-clicking a footage item or solid in the Project panel offers
"Replace with Precomposition (used N times)": the item goes into a new
composition of its size, pixel aspect and frame rate, in the same folder, and
every layer in every composition that used it switches to that composition,
keeping timing, transforms, effects and masks. One undo step. Agents run it as
`file.replaceWithPrecomp`.

The Project panel's details header also shows how often the selected item is
used ("Name ▾, used 3 times"); the ▾ lists and opens the comps that use it.
`Project::item_users` and the `project.usedIn` query back both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:14:43 +02:00
ThemolxandClaude Opus 5.5 9e66e0d2ec New comps from footage get a unique name, as Duplicate does
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:22:59 +02:00