Commit Graph
4836 Commits
Author SHA1 Message Date
.CooCoo d6c9ad6643 Cineon Converter: Gamma 1 gives linear output and saved projects keep their render (#2312)
## Behavior

A new Cineon Converter opens with Gamma 1.7. Its Version parameter is 2 by default. A converter saved before Version existed opens as version 1, retaining its Gamma value, keyframes, and expression text and rendering on its original scale. Reset keeps the instance's Version.

In version 2:

- Log to Linear raises the exposure to `1 / gamma` before subtracting the black offset and normalizing: `(E^(1/g) - O^(1/g)) / (1 - O^(1/g))`, with `E = 10^((code - white) * 0.002 / 0.6)` and `O` the exposure at the black point. Gamma 1 gives linear output, and a larger Gamma lightens the midtones. Highlight Rolloff then applies as before, and Linear to Log is the inverse.
- Log to Log returns code values unchanged at Gamma 1 and raises the normalized code value to `1 / gamma` at other values. With custom black or white points, the code values first go through the Gamma 1 conversion.

The CPU and GPU paths match. Version 1 instances render as before, and Interpret Footage ▸ Cineon Settings keeps its existing formula, which agrees with the version 2 Converter at Gamma 1.

The public Cineon Converter documentation (https://helpx.adobe.com/after-effects/using/utility-effects.html) describes Gamma as a midtone control and gives no defaults or formula. The default, the Log to Linear curve and the Log to Log behavior come from running and scripting After Effects 26.5 on my own machine and reading back the rendered pixels. In After Effects, choosing Log to Log also sets Gamma to 1 and Highlight Rolloff to 0; EffectCraft does not do that yet, because no command sets one parameter from another.

Animation preset format 3 records `cineonVersion` for a saved Gamma property and stores effect key times relative to the first key. Presets in formats 1 and 2 without that tag contain version 1 Gamma values; format 2 already has relative effect key times. Applying a whole old Cineon Converter also gives it version 1. Exposed Essential Properties Gamma values convert to the target converter version where their target is unambiguous (the conversion keeps the linear point; other Gamma values follow each version's own curve); enabled expressions on another scale are skipped instead of rewriting their source.

A saved Gamma records the Version its converter has at the current time. Applied to a converter whose Version is keyframed, a Gamma preset keeps those keys: the Gamma is converted to the one version they hold, or skipped when they hold several or an expression sets the Version. A preset that also holds the Version sets it from that property.

The project schema remains 2. `docs/architecture.md` describes the behavior above.

## Validation

Effect tests check version 2 Log to Linear at Gamma 1.7 (codes 95 to 685) and Gamma 2.2 (codes 180 to 620), and Log to Log at Gamma 1 (Rolloff 0 and 20) and Gamma 1.7, against the measured 8-bit values within one or two codes, plus round trips, version 1 rendering and the Interpret Footage agreement at Gamma 1. GPU tests compare the version 2 paths with the CPU. Engine tests cover old projects, Reset, presets, keyframes, and Essential Properties.

Commands run on `74d398c2`:

- `cargo fmt --all -- --check`
- `cargo clippy -p effectcraft-effects -p effectcraft-gpu -p effectcraft-project -p effectcraft-engine --all-targets -- -D warnings`
- `cargo test -p effectcraft-effects`: 546 passed (including `effects_doc_is_current`, so `docs/effects.md` needs no change), 1 ignored, plus 6
- `cargo test -p effectcraft-project`: 47 passed
- `cargo test -p effectcraft-gpu`: 253 passed, 1 ignored
- `cargo test -p effectcraft-engine`: 1,007 passed, 5 ignored, plus 27
2026-11-15 12:11:07 +01:00
.CooCoo 7cbefaf70f Import: VectorCraft documents import through the PDF they carry (#2901)
* Import: VectorCraft documents import through the PDF they carry

File > Import reads a .vectorcraft file through the PDF that VectorCraft
saves in it with Create PDF-Compatible File, one page per artboard, as
footage or as a composition with one layer per PDF layer. The PDF is read
with craft-doc from craft-libs (plain or compressed files, the PDF inline
or after the JSON). A PDF after the JSON may be as large as a PDF file
EffectCraft imports (512 MiB). A document saved without that option gives
an import error that names it.

craft-doc, craft-vectorcraft-format (tests only) and craft-ecproj-format
are pinned to craft-libs fa1a6574; craft-ecproj-format is unchanged
between ca1b5e2e and that commit.

* Import: show file errors returned by the import dialog

* Keep dropped-file import errors alongside local drop errors

* Import: preserve every file error across background jobs and UI notices

* Import: queue pending errors with bounded notification details

* Import: a damaged VectorCraft preview no longer stops the carried PDF

The importer read every rendition with craft_doc::renditions and then
picked the PDF, so a preview whose base64 was invalid or whose data lay
past the end of the file made the whole import fail. It now reads only
the PDF with craft_doc::rendition, from storytold/craft-libs#546, and
never decodes the preview. A document without a PDF still reports
NoCarriedPdf.

* Import: dismissing a picked import's error status acknowledges its details

A picked or menu import queued its error details with the status
report_import_errors set ("first (and 1 more)"), and show_result_errors
then replaced that status with "first; second". The two never matched,
so clicking the status left the details queued and they showed again.
Import commands now move the queued details to the status that replaces
it, also when After Effects project errors lead it. Other commands'
statuses never take over an import's details.
2026-11-15 03:45:56 -05:00
Dylan M. Taylor dc2380b070 fix(ci): stabilize macOS dialog and EXR cache tests (#3704)
* fix(ci): stabilize macOS dialog and EXR cache tests

* style: format file dialog test helpers
2026-11-14 20:08:47 -05:00
FredApps e8a68671ae Merge pull request #3693 from storytold/fix/import-dialog-tests-paths-result
Tests: import dialog tests answer with a Result of paths (main does not compile its tests)
2026-11-14 21:22:41 +01:00
FredApps c6b9311f98 3084: own diagnostic text and retain pre-logging errors 2026-11-14 19:15:07 +01:00
FredApps f159ddf823 3084: guard the diagnostic-report message dialog 2026-11-14 18:54:50 +01:00
FredApps 4bb4c71583 Wrap the line rustfmt would wrap 2026-11-14 18:20:47 +01:00
FredApps 3968433f75 Tests: import dialog tests answer with a Result of paths
DialogAnswer.paths became a Result in #3304; three import tests merged
since still passed a plain Vec, so the test build of effectcraft-ui-egui
did not compile on main.
2026-11-14 18:20:27 +01:00
FredApps f029bbadb2 Merge pull request #3251 from vangel76/tests-pin-english-3244
Tests: Match System resolves to English in ui-egui test builds
2026-11-14 17:30:28 +01:00
FredApps afaa9cced3 Merge pull request #3304 from vangel76/macos-open-panel-3084
File dialogs: a dialog that can't be created is an error, not a crash (#3084)
2026-11-14 17:30:08 +01:00
FredApps 38cd374bae Merge pull request #3236 from vangel76/number-field-math-3195
Number fields: typed arithmetic (960/2, (3+4)*2) and a leading *, / or + applied to the value
2026-11-14 17:29:47 +01:00
FredApps 829706de6f Merge pull request #3232 from vangel76/project-drag-new-folder-3196
Project panel: items dropped on New Folder go into a new folder, as After Effects
2026-11-14 17:29:26 +01:00
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
vangel76 fcef7db85a Merge remote-tracking branch 'upstream/main' into tests-pin-english-3244
# Conflicts:
#	Cargo.lock
2026-11-14 00:11:28 +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