* Monitors: a frame with partial alpha goes up through a table, not two powf per channel
The Transparency Grid's texture (#3233) converted each partly transparent pixel with two powf per channel on the UI thread. A 256 x 256 table made on first use gives the same values.
* docs: the RGBA channel display modes render on the CPU, and Alpha renders live
* Timeline: duplicate frame markers are worked out on a background thread
Show Duplicate Frame Markers (#3212) compared up to 1,000,000 frames on the UI thread after every edit, which a long sequence takes more than a frame for. A background thread works them out under catch_unwind, and the last markers of the same sequence show until the new ones are ready.
* Effect Controls: the cut line is also grabbed where it crosses clip A or B
The cut could only be grabbed over the transition, outside its ends' zones (#3115), so with Start or End at Cut, or a narrow transition, it could not be dragged at all. A test also checks that a drag past what the clips allow stops at timeline.roll's limit.
The monitors flattened every frame over black before showing it, so the
transparent areas of a title, graphic or clip with an alpha channel
showed as black, and Display Mode > Alpha showed white everywhere. The
frame cache now keeps each pixel's alpha, with the same color bytes as
before, and texture_for still uploads frames opaque.
Transparency Grid, in View and in each monitor's wrench menu, paints a
white and #cccccc checkerboard of 8 pt squares behind the picture and
uploads the frame with its alpha, so the grid shows through. It is off
by default, and each monitor keeps its own setting. A magnified picture
paints only the squares in view, and in Comparison View the reference
frame gets the grid too. The GPU compositor's output and the render
previews are opaque, so while the grid is on the Program monitor and
the reference frame render the sequence live on the CPU
(Target::SequenceLive). Display Mode > Alpha now shows the frame's
alpha, and the Program monitor renders the sequence live for it too,
also in ranges that have a render preview.
The command is view.transparencyGrid; like the other view toggles it
takes {"enabled": bool} and {"monitor": "program"|"source"}, and the
setting is transparency_grid in ui.inspect and ui.set.
* Tests: one integration-test binary per crate
Each crate with several tests/*.rs files keeps them as modules of tests/it/,
so cargo links one test executable per crate instead of one per file. Files
that change process-wide state or time a workload stay binaries of their own.
* Profiles: line tables for workspace crates, no debug info for dependencies
* Windows ARM64: nightly and on release pushes instead of every push to main
* xtask ci: clippy and tests in the dev profile
The release profile's thin LTO and single codegen unit made linking the test
binaries take most of the gate. The codec and audio crates stay at opt-level 3
in the dev profile, so their tests run at release speed.
* xtask ci: nextest, doctests, no examples, one wasm check
Tests run with cargo nextest when it is installed (cargo test otherwise) and
build with --lib --bins --tests, so examples aren't linked; doctests run
separately. The wasm check keeps going past the first failing crate. VP8
fixture generation locks across processes, since nextest runs each test in
its own.
* VP9 synthetic fixtures: write aside and rename
A test in another process could read a fixture ffmpeg was still writing.
* Fuzz tests: a third of each sweep by default, all of it with FILMCRAFT_FUZZ_ITERS
Every seed, mutation kind and thread count still runs. Header bytes each get
one overwrite value and two bit flips in rotation, so every value and bit
index still occurs across the region. FILMCRAFT_FUZZ_ITERS=N runs the whole
sweep plus N random rounds per seed, as before.
* Platform fallback tests: one failure point per hybrid path
The failure-anywhere test injected a failure at four points around every
restart point; the hybrid's paths differ only in the first GOP, around the
second and last restart points, at the end and never. The replay test needs
the first two GOPs.
* Export tests: shorter ranges with the same checks
The bitrate test measures 0.5–1.5 s, where each preset reaches its own
target (on 0–3 s the simple demo left two presets at the same rate). The
every-preset test exports three frames per preset.
* Audio tests: effects in parallel, a table-driven FFT for the Opus quality metric
The extreme-settings test keeps both settings and the 40 s tail for every
effect, one thread each. The Opus quality metric's FFT builds its twiddles
once and allocates nothing per transform (same algorithm and summation order).
* CI: dev-profile gate with nextest
xtask ci no longer builds with --release, so the release-profile overrides
go; nextest is installed for the test step.
* CI: apt packages through the cached install-apt-packages action
* Review fixes: fuzz tests with a panic hook in their own binaries, loud fixture lock
The fuzz tests of av1, h264, hevc, mpeg2v, vp8 and vp9 count panics through
a process-wide hook, so a panic in another test of the same process would
fail them; they are binaries of their own again. The VP8 fixture lock fails
the test instead of skipping it silently, and the full-sweep doc comments say
how many truncation points the full run samples.
* Fuzz tests: rotate the default subset per run (FILMCRAFT_FUZZ_SEED replays one)
* platform tests: decode-switch tests in their own binaries, one NVENC lock
While the Program or Source monitor plays and its time ruler is zoomed
in, the zoom range follows the playhead the same way the Timeline does,
honouring Settings > Timeline > Timeline Playback Auto-Scrolling: page
scrolling moves the range so the playhead is near its left edge when it
runs off the right edge or jumps before the left one; smooth scrolling
keeps the playhead in the middle; no scrolling leaves the range alone.
The range keeps its width. Scrubbing, clicking and moving the playhead
while stopped never move it.
The Program and Source monitors get a zoom scroll bar under their time
ruler, drawn like the Timeline's: drag a handle at either end of the
thumb to zoom in or out (down to five frames), drag the middle to
scroll, or press beside the thumb to move it there. The ruler then
shows only that part: playhead, markers, In/Out handles and ticks are
mapped to it, clicks and scrubs set a time within it, and anything
outside it is not drawn. Each monitor keeps its own range in its view
state (time_zoom).
The transport buttons are now 28 pt square with larger icons, spaced
6 pt apart and centred; on a narrow monitor the spacing tightens before
buttons move to the overflow menu.
Zooming a monitor took the zoom dropdown, which zooms about the middle
of the picture, and then scrolling or the Hand tool to bring the part
you wanted into view; at Fit the wheel did nothing.
Now, on the Program and Source monitors:
- The mouse wheel (or a pinch) zooms about the pointer, from Fit too,
between the dropdown's 10% and 1600%: the point under the pointer
stays under it. It zooms as fast as the Timeline's wheel zoom, through
the same wheel_input.
- A middle-button drag that starts over the picture pans it, whatever
the tool; the Hand tool still pans with the left button.
- Fit resets zoom and pan, and each monitor keeps its own, as before.
Comparison View, which always fits both frames, ignores the wheel.
Tests: view_ui (wheel zoom about the pointer, middle-button pan, the 10%
floor, Fit, Source untouched).
Problem: with the Source monitor focused, L only ever played at normal
speed, and J and the slow shuttle commands returned "Source playback
currently supports normal forward speed": the Source clock always ran at 1x.
Fix: the Source clock runs at a signed speed. L/J play forward/backward
and pressing the same key again doubles the speed up to 8x; Shuttle Slow
Right/Left play at 1/4 speed. As in the Program monitor, speeds other than
normal forward play without sound, and backward playback stops on the
first frame. docs/monitors.md updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cancel the export draft when clicking outside the dialog, including top menus and mode tabs, while allowing the background control to receive the click. Keep the dialog's own format and depth dropdowns interactive.
Cover dismissal for both monitors, mode switching, dropdown selection, and background monitor clicks in a UI regression. No frame is exported when clicking away.
Assisted-by: Codex
Display the captured frame with the same frame rate and drop-frame timecode format as Source or Program, instead of decimal seconds. Keep the captured timecode fixed while the export dialog is open.
Add a UI regression comparing both monitor labels at standard and NTSC rates, with Program drop-frame formatting and playhead movement after capture.
Assisted-by: Codex
Wire both camera buttons to captured-monitor frame export with original media at full resolution. Add a movable settings dialog with format/depth selection, a visible destination folder, Browse, overwrite confirmation, and optional project import.
Reuse existing 8-bit still encoding; add JPEG and direct float-to-16-bit PNG/TIFF encoding. Cover monitor routing, captured frame time, proxy bypass, hostile parameters, depth, import, cancellation, overwrite, and fractional header movement with synthetic regressions.
Assisted-by: Codex
Play source video and audio, drag marked ranges as video, audio, or a linked pair, and route Source commands to clip marks and markers. Add shared draggable In/Out handles and range movement, preserving scrubber priority and explicit-only Program marks.
Include original code-drawn controls, documentation, attribution, and regression coverage for playback, placement, routing, undo, cancellation, and hostile parameters.
Assisted-by: Codex
MonitorView state is serde and settable with ui.set {program|source}; view.* UI commands with
menu checkmarks; wrench and zoom menus with automation ids; headless kittest tests (view_ui.rs).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>