The inverse transform computed its bit-reversal permutations and loop
bounds at run time and went through one generic path for every size.
inverse_dct, the ADST permutations and inverse_2d are now const generic
over the size and inlined, so the permutations fold at compile time.
forward_2d zeroed a 4 KiB tmp buffer on every call, even for 4x4. It now
dispatches to a fixed-size version for 4, 8 and 16 (tmp is exactly n*n
floats); 32 keeps the run-time version, which measured faster than a
const 32 variant. The float operation order is unchanged.
Output is bit-identical.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
encode_pulses rebuilt the full V(n, k) table with saturating u64 adds for
every coded band and summed the prefixes through a closure. Build the table
once (n <= 176, k <= 128, saturated at u32::MAX, 91 KB) and sum the prefixes
over a contiguous row slice. Shapes outside that range, which the band
layout never produces, fall back to a local table. The unused scratch Vec
is gone from BandEnc.
The bitstream is unchanged; a test keeps the old per-call encoder and
compares the coded bytes on random codewords over the whole (n, k) range.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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>
- Default's right column is a short Preview frame (fixed 76 pt: tab strip and a 46 pt body)
over a Properties frame with Align, Audio and Effects & Presets as tabs. Essential Graphics'
right column is one Properties / Effects & Presets frame. No built-in workspace uses
accordions, so a frame there takes tabs and Maximize acts on one frame.
- Workspace files of version 1: a column of stacked frames becomes tab groups split top to
bottom. Each open frame keeps all its tabs and its height as a fixed size; a closed frame's
tabs join the open frame above it.
- Version 1 stored a native panel window's outer position; craft-ui places windows by their
content. The migrated rect moves by the main window's frame offset once the main window
reports it, and workspaces are not saved before.
- Focus and traversal tests use DockArea::hit_id for tabs and dividers; the
focus outline is checked as craft-ui draws it (inset 2 pt, accent colour)
while the tab has keyboard focus from traversal.
- Floating resize grips are checked under craft-ui's accessibility label.
- Stack resizes compare rendered heights: craft-ui shares a stack by the
entries' heights as proportions, so the saved heights are checked as ratios.
- Drops into a split use the side zone depth (SIDE_EDGE_ZONE_DEPTH).
- Panels that need their whole area (scroll audit, automation audit) are
docked alone: maximizing a stacked panel maximizes its whole stack.
- ui_tabs: dragging by the panel menu button is gone (craft-ui drags by tab,
drag bar or border); the test docks by the tab. A stacked header's -/+
button collapses and expands, a click on a closed header opens it; a
click on an open header no longer collapses it.
- ui_tabs (#302): a panel dropped on a stacked panel joins the stack as its
own entry below it; craft-ui stack entries hold one panel each.
- ui_panel_focus: a reopened Composition goes back to its own index
([Composition, Layer]), craft-ui's restore order.
- ui_menus probes the Effect menu with Remove All, since Effect Controls
also names a tab.
Tab and stacked-header automation labels name what the panel shows
("Composition A"), as the tabs do. The top border above a group's tabs,
a craft-ui drag source, registers panel.border.<Panel>, which the agent
audit found without an id.
craft-ui's native-windows feature is on for every non-wasm build. Keys and
dropped files from a floating window reach the main window's next frame, and
while a modal dialog is open the dock area blocks input in floating windows.
Move Panel to Main Window docks the panel again. The web, Wayland and embedded
viewports keep floating windows in the main window.
A version 1 file (craft-libs 5c874d51) and the unversioned named-workspace record
load with every panel in place, floating groups and their native window places
kept. The first save writes the current format and keeps the earlier file as
workspaces.old.json. The fixture is a file written by upstream main.
A panel never laid out opens at EffectCraft's default place for it; a
closed panel reopens at its recorded place. The panel menu keeps Move
Panel to New Window, which records the window's place.
Edit ▸ Keyboard Shortcuts now lists the scripts File ▸ Scripts lists, under
File ▸ Scripts, and a key given to one runs it as its menu entry does. The
key is bound to the script's file name (file.runScript {"name": ...}) and is
saved with the preset. File ▸ Scripts and the macOS menu show it.
The shortcut table lists the Scripts folder again when a script is installed
or uninstalled, when the editor opens and when shortcuts.list runs.
shortcuts.set and shortcuts.reset look a script up in File ▸ Scripts' list
as it is now, and a script it doesn't list is an error instead of a key for
Run Script File....
Every craft-libs crate and the hayro-syntax and hayro-jbig2 patches are
pinned to storytold/craft-libs 2869b8cc. The workspace is a craft-ui
Workspace<PanelKind>: craft-ui draws the frames, tab strips, stacked
panels, floating windows and drop previews and owns the gestures.
EffectCraft keeps PanelKind, its built-in workspaces, the panel bodies,
the Timeline's comp tabs, viewer locks and the panel menu in each group
header, and the rule that no move or float empties the docked area.
window.dockAction applies craft-ui Actions; craft-ui's window.panel.*
commands, ui.dock and ui.dock.reset parse as DockCommand. Saved
workspaces are version 2 snapshots of craft-ui Workspaces; an earlier
file loads as the default layout and is left untouched.
A shape layer's Contents > Add menu had every After Effects path operation
except Wiggle Transform. It is now a shape item (layer.addShapeItem kind
wiggler, "ADBE Vector Filter - Wiggler" for scripts) with Wiggles/Second,
Correlation, Temporal Phase, Spatial Phase, Random Seed and a Transform
group whose Anchor Point, Position, Scale and Rotation set how far each
transformation wiggles.
The values come from the Wiggly selector's noise, with each Repeater copy
as one unit: the copies of a Repeater above the operation, in its group or
a nested one, wiggle independently as far as Correlation allows, and the
copies of a Repeater below it wiggle the same way. A paint above it whose
paths all move by one matrix moves its gradient and stroke scale with them.
The layer cache keys a layer with a Wiggle Transform by time.
Reconcile main 991904888a and preserve the reviewed lifecycle, IME, rendering and lockfile changes.
Formatting, strict lint, layers, assets and WASM passed. Full CI retained 3,961 passes, 14 baseline-matching GPU failures and 31 ignored executions. The local review commit has explicit approval to retain these documented failures; native/browser acceptance and paired app performance remain open.
The fine stage searched a 5x5 neighbourhood per pyramid level and seed.
When the best coarse match is reliable (NCC score >= 0.8) and lies within
one top-level pixel of the motion prediction, a 3x3 neighbourhood is
enough; otherwise the 5x5 search stays.
Fixes#2349
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reuse consecutive caller-owned pyramids for regular Warp motion and trajectory tracking. Preserve the original three-level motion search by setting aside the trajectory-only extra level, without cloning planes. Keep detailed analysis at its existing distinct resolutions and test bitwise motion and complete JSON equality against separate pyramids.
Fixes#2322
Near the borders a bubble's refraction (liquid) or reflection (metal) sample
lands outside the layer. sample_bilinear returns transparent there, so the
pale stand-in colour for transparent pixels showed through, a band of washed-out
bubbles along every edge. Sample with edge pixels repeated instead, on the CPU
and in the GPU kernel; the pale colour stays for genuinely transparent pixels.
The CC Bubbles golden hashes are re-pinned (the half-resolution cases pad the
buffer with transparent pixels and are unchanged).
Keep parsed analyses, their FNV key and source text in a bounded shared cache. Compare source contents on hits instead of rehashing the JSON, so cloned parameter strings remain fast while in-place edits invalidate the cache. Cover analysis replacement, key equality, same-allocation edits and retention bounds.
Part of #2322
For a precomp layer whose composition had a proxy, the renderer
decoded the proxy file with frame_with and drew it as decoded.
Interpret Footage > Proxy's Invert Alpha, Interpret As Linear Light
and Separate Fields had no effect on the render, and neither did the
layer's Frame Blending. A footage item's proxy already goes through
footage_frame and gets all of them.
The composition proxy now goes through footage_frame too, so it is
decoded the way a footage proxy is.
Prepare the default plan through the effects cache after analysis instead of discarding it. Plan from the serialized analysis so the first rendered frame retains identical floating-point inputs and output. Test bitwise render equality and cache reuse.
Part of #2322
Solve expensive camera systems with matrix-free Schur products and a block-preconditioned iterative solver. Check the true linear residual and fall back to the direct solve when convergence fails. Keep short systems on the existing path and compare reprojections, camera parameters and outlier decisions against direct solves.
Fixes#2328
The footage page said the 10 Bit White Point becomes the Converted White
Point. That holds with Highlight Rolloff at 0; at the default 20 the top of
the converted range is bent into a curve, as in the Cineon Converter effect,
and the white point converts to a lower level.
Share the unchanged Jacobi factors between full SVD and null-vector callers. Select the same right singular vector directly instead of normalizing U and sorting copies that the caller discards.
Fixes#2329
Detect narrow lower-triangle bandwidth and skip structural zeros while preserving dot-product lanes and signed-zero arithmetic. Keep a dense fallback for wide systems and compare factors and solutions against the original algorithm.
Fixes#2325
Unfiltered, list_commands returns ~134 KB and list_effects ~480 KB, more
than MCP clients accept in one tool result. With `compact: true`,
list_commands (and its command_list alias) returns only each command's
id and label, and list_effects only each effect's id, name and category
(~40 KB and ~23 KB). Default output is unchanged; filter and
enabled_only still apply.
Return errors for invalid bundle observations and oversized workspaces, reject non-finite projections and mismatched correspondence slices, and prevent track frame overflow. Cover the reported failures with synthetic regression tests.
Fixes#2330
Use a bounded inline array for the current camera parameters and preserve the existing accumulation order. Extra parameters spill safely if the parameter set grows.
Fixes#2327
The normalized cross-correlation only had a fast path for an exactly
identity shape, which tracked footage never has after the first frame, so
every candidate went through Plane::sample. All candidates of a search are
the reference centre plus integer offsets, so each template pixel has the
same fractional offsets for all of them. Build one table of taps per
(level, seed, shape) and score each candidate with integer-indexed lerps,
with one bounding-box check up front (outside the plane the old path is
used).
lk_refine only reads the level-0 gradients around the match, so compute
them for that window (with an interior fast path in Plane::gradients_in)
and fall back to the full gradients if the iteration leaves it.
The results are unchanged: same lerp form and accumulation order.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
step() cloned the whole RGBA frame to remember it (33 MB at 1080p,
133 MB at 4K) and then cropped that clone, although only the region
around the outline is ever read. The tracker now stores the previous
frame already cropped to the region the next step will use, computed
from the updated outline with the same margin and rounding as before,
so the crops, and therefore the tracking result, are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Store the two four-element DLT rows in arrays while preserving arithmetic and accumulation order. This removes two heap allocations for every triangulated view.
Fixes#2326
The feature mask tested every non-skin window pixel against the 48-edge
face outline (about 25 M edge tests per frame). A scanline hits the same
edges for every pixel of a row, so the crossings are now computed once
per row with the same expression and comparisons as inside_polygon, and
a pixel's parity follows from how many lie to its right. The face-frame
rotation is also worked out once per frame instead of once per blob
pixel.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every pixel of a (2r+1)^2 window shares one fractional offset per level
and iteration, but lk_point called Plane::sample (with a libm floor, a
bounds test and the clamp path) up to three times per pixel. The offset
and the clamped column/row index tables are now computed once per
window and the pixels run the same lerps on integer indices.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Opening a project runs migrate::upgrade_instance on every effect, so an
instance saved before a parameter was renamed, moved into a twirl-down
group, added or had its popup reordered takes the current shape.
Apply Animation Preset added a preset's effects as saved, without that
upgrade. A Change to Color saved with its single Tolerance applied
with that old property and without the Tolerance group, so the effect
did not read the saved tolerance.
apply_preset now upgrades each preset effect before adding it, sized by
the target layer's effect bounds as upgrade_effects does. A lone effect
property saved at a renamed or moved parameter id
(effects/ec.color.changetocolor/tolerance) is rewritten to the current
id through migrate::current_param_id, so it is applied instead of being
listed in skipped.
The Footage panel, the Project panel thumbnail and the native-size source
frames that tracking, mask tracking, scene detection and auto-trace read took
DPX and Cineon frames straight from the file, so they showed the log code
values while a composition showed the converted ones. Each now applies the
footage's Cineon Settings, as the composition renderer does.
execute_checked refuses any key a command's params doc does not list, so
MCP, the CLI and the control channel could not pass preserveRgb,
overrideStart, startTimecode or guessAlpha to file.interpretProxy. The
command reads all four, and the shared Interpret Footage form sends
preserveRgb for every proxy, and overrideStart and startTimecode for a
movie or sequence proxy. file.interpretProxy's doc now lists them. The
docs of file.interpretProxy and file.interpretFootage list premulColor
beside matteColor, which both commands read as the matte color. The
docs of file.interpretFootage, file.applyInterpretation and
file.reloadFootage list `item` beside `items`, which the three commands
already read.