CC Composite stays After Effects-pure: its Layer parameter is gone, so
parameter indices match AE again (scripts and expressions that address
parameters by index). The model reference lives in its own effect,
ec.3d.instance3d (3D Channel, CPU, one layer parameter), which replaces
the layer's pixels with the referenced layer's render — alpha included.
A referenced model's solo render is now hashed into the owner's cache key
instead of declining the entry (Bflat, review of #2410): the layer, its
animated values and transform, the mesh item, the comp camera and lights,
draft 3D, the renderer setting and the in/out gate. Covered by a render
test: the copy is cached, served on the second render, and re-rendered
when the camera pans or the model leaves its in/out range.
- add_environment takes the evaluated light (&EnvLight) instead of nine
loose parameters — strict clippy (#2496) denies too_many_arguments.
- The Environment light's shadow map moves into env_shadow_map so
shadow_maps stays under the cognitive-complexity limit.
- tests/it/ui_env_light_repro.rs deleted: it duplicated ui_env_light_gpu
and the engine tests, and polled with sleeps of up to 12 s.
Signed-off-by: Minher0 <minhero@outlook.fr>
lib.rs and menus.rs were split (#2491): the dialog_state accessors, the undimmed canvas while a
dialog previews, and Escape-as-Cancel for the Gradient Editor and Preview move to app/window.rs,
app/frame.rs and menus/shortcuts.rs. Layer Styles' rollback keeps main's text-editor handling inside
the shared restore(). The dialog preview integration test joins the tests/it binary.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDvxCS8xcR28VArjzn1x7U
effectcraft_project::cineon_log_to_lin does the log-to-linear conversion
for Interpret Footage > Cineon Settings and the Cineon Converter's Log to
Linear. Both raise the result to 1 / Gamma, so Gamma 1 gives linear light
and the same Gamma gives the same values in both. The converter's Linear
to Log, its Log to Log reference and the GPU shader use the same Gamma
scale, with a smallest Gamma of 0.01 / 1.7 (CINEON_MIN_GAMMA).
A test runs the converter at Gamma 1.7 (the default), 1 and 2.4 and
checks it against Cineon Settings at each code value.
The mixdown evaluated levels and effect parameters once per block, and export mixes WAV/AIFF
in one-second blocks, so a level fade became a staircase that changed once a second. Animated
Audio Levels are now evaluated on a 1 ms grid of comp samples and interpolated in dB between,
and animated per-sample effects (Stereo Mixer, Backwards) run in 1 ms pieces with their
parameters at each piece. The grid is aligned to comp sample indices, so the result still does
not depend on the block size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests now sit with the crate's other test modules under crates/script/src/tests/ and are declared in tests.rs. docs/scripting-parity.md points at the new path.
Dither can round a colour byte up while alpha rounds down (colour = alpha = 100.25/255 gives
alpha 100, colour 101), breaking the premultiplied invariant. Clamp each dithered colour byte
to the written alpha byte.
Test: dithered_premultiplied_colour_never_exceeds_alpha.
Resolve the export tests' module list (banding and h264_gpu_yuv both stay). #1631's
yuv420_frame checked Render Settings Color Depth itself; that depth is now the project's
depth for the whole render, so the GPU YUV path is taken only for 8 bpc renders. Above
8 bpc frames take the RGBA path, which dithers, so H.264 exports don't band either.
Test: renders_above_8_bpc_skip_the_planes.
Per review: as in After Effects, the region of interest lies inside the comp, so each converted
ROI edge is clamped to [0, w] / [0, h] and the far edge never passes the near one. A hostile ROI
reaching far outside the frame can no longer pad it into a huge crop, the wide-frame case keeps
its full width, and the i64 widening is no longer needed (frame_size is back to main's code).
Tests: hostile quads give edges >= 0 and a frame between 1 x 1 and the frame size, and an ROI
covering more than the frame is the whole frame.
wiggle_transform looks up its Transform group itself and returns when
the group is missing, so it takes seven parameters and no longer needs
the clippy::too_many_arguments allow.
Polish menus (every fixed menu entry, 630 rows) and panels (every row of
the Chinese panel catalogs, 695), the undo/redo, Reveal and Assign Shortcut
labels, the macOS Services item, Settings ▸ General ▸ Language and
Match System for pl-* locales. Tests cover the locale mapping, the
settings registry and the macOS application menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DukHNvUY45rjRnvtz91x33
The CJK render test skipped wherever no Japanese face was installed, which
is every CI runner, and on a Mac it drew glyphs with or without the fix, so
it caught nothing. Replace it with a fallback-order test: fixture faces for
`Hiragino Sans` and `Hiragino Sans GB` show that a zh-hans Han lookup tries
the Chinese family first, that ja tries the Japanese one, and that a layout
gets Hiragino Sans GB. Synthetic fonts in an isolated child process keep it
independent of the host's fonts; with the Simplified list reverted it fails
on `[7]` against `[8, 7]`.
Delete `TEST_CJK_LOCALE`: the locale-mutating tests moved to an isolated
child process, so nothing used the lock and the test-only static failed
clippy's `-D warnings` gate.
Trim the documentation edits to the single docs/gaps.md gap-11 row: revert
the header and changelog edits in ROADMAP.md, docs/roadmap.md,
docs/localization-parity.md and docs/target-app-parity.md, and the gaps.md
header and changelog row, which conflict with other PRs.
Tests: cargo clippy -p effectcraft-text -p effectcraft-render --all-targets
-- -D warnings, cargo clippy --workspace --all-targets -- -D warnings,
cargo test -p effectcraft-text (79 lib), cargo test -p effectcraft-render
(264 passed, 1 ignored perf test), cargo fmt --all -- --check.
A shape's painted bounds can be tiny enough (a 1e-305 px rectangle) that
the fit scale overflows to infinity. Non-finite bounds are skipped and a
non-finite scale returns an error before anything is written, instead of
storing infinity in the document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
layer.transform's fit / fitWidth / fitHeight ops took the layer's size
from render::source_size, which returns (0, 0) for layers without a
source item — shapes and text — so the ops matched nothing and silently
did nothing, for the shortcut, the menu item and the command alike.
When the source size is empty and a fit op runs, the size falls back to
the layer's painted content bounds at the current time, and the anchor
goes to the bounds' centre: content bounds live in layer space, where a
shape is centred on the origin and text sits above its baseline, so
(w/2, h/2) would fill the frame but land off-centre. Reset and the
other ops keep the source size, so their defaults are unchanged.
The regression test asserts what the viewer shows — the painted bounds
mapped to comp space span [0, 0, 640, 360] after fit, and fitWidth
spans the width centred vertically — for a rect shape and a text layer.
Co-Authored-By: GLM-5.3-Flash <noreply@z.ai>
Edit > Clear follows panel focus: with the Project panel focused it
redirects to project.delete, which removes the selection at once when
nothing uses it. With a text edit open, that deletes the composition
being typed into — the user is working in it — and undo restores the
item but not the active composition. During a text edit the redirect
stands down (Clear does nothing) and the Edit menu's Clear entry is
disabled, mirroring how Deselect All stands down in #2096.
Co-Authored-By: GLM-5.3-Flash <noreply@z.ai>
Edit > Duplicate replaces the layer selection with the copies, but an
open in-canvas text edit stayed on the original layer: typing kept
editing a layer that was no longer selected. When the duplicate leaves
the edited layer deselected, end the edit the way #2096's Deselect All
does (text_edit::end_edit), in the command every caller — menu, shortcut,
MCP — dispatches through.
Co-Authored-By: GLM-5.3-Flash <noreply@z.ai>