Resolve conflict in crates/ui-egui/src/i18n/tests.rs using switch_and_check
helper and add 22 new catalog entries to tr.tsv matching upstream additions.
* #3396: preserve binary transparency in animated GIF exports
Enable Include Alpha Channel for GIF with an explicit 128/255 threshold. Cover transparent, opaque and partial-alpha pixels, frame disposal and the Export UI. Document the behavior and add the requested username-only contributor credit.
Co-Authored-By: Codex <noreply@openai.com>
* #3396: clarify when the GIF alpha threshold applies
Begin the export hint with "When enabled," and wrap the longer label so its explanation remains visible. Alpha behavior and threshold wording remain unchanged.
Co-Authored-By: Codex <noreply@openai.com>
* #3396: cover GIF transparency during palette quantization
Round-trip two frames with more than 256 post-conversion RGBA colors, a moving isolated transparent pixel, and opaque black and near-black neighbors. Require exact alpha and bounded RGB error. Document commands for the GIF and export test suites; leave the encoder unchanged.
Co-Authored-By: Codex <noreply@openai.com>
---------
Co-authored-by: Codex <noreply@openai.com>
In a selected transition's own timeline in Effect Controls, the cut line
over the transition can now be dragged. While it is dragged, the clips
and the transition are drawn where the rolling edit would put them, with
the same limits as `timeline.roll`. On release one rolling edit moves
the cut, so the clips' In and Out points change and the sequence keeps
its length. The ends of the transition still resize it, and the rest of
it still slides it.
A rolling edit now stops where the transition on its cut would leave
one of its two clips. When a ripple trim, a slide or a ripple speed
change moves a cut far enough that the transition on it would leave its
two clips, the transition is moved back within them, shortened when the
clips are now shorter than it, and its stored alignment is updated.
* 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
- ru.tsv: all 3,592 strings of the reference catalog (was 355), translated
from the English sources with ordinary, Premiere-style Russian vocabulary.
Fixes the known mistranslations (Sequence -> Последовательность, Bin ->
Корзина, Lumetri Scopes -> Осциллограммы Lumetri, Wipe -> Вытеснение,
Normalize Mix Track, «мастер-дорожка») and applies one glossary throughout.
- Tests: russian_covers_the_whole_interface requires every Spanish entry and
no dead ones; a font test covers the Cyrillic alphabet including «ё»;
System Language maps ru-* to Russian.
- Docs: localization-parity, roadmap, gaps, ROADMAP, the ui-egui README and
the catalog's attribution sidecar.
#630 shipped German for the menus. This adds the other 3,213 strings, so
de.tsv has every entry of the Spanish catalog (3,590): panels, dialogs,
effects and their parameters, transitions, presets, Settings, shortcut
commands, export presets, tooltips and status messages. Same style as the
menus (du, infinitives, placeholders, ellipses and escapes kept); the
glossary in the header is extended. The 377 menu entries are unchanged.
german_entries_cover_the_original_menu_catalog required every entry to be
a menu label. It becomes german_covers_the_menus_and_has_no_dead_entries,
like the French and Italian tests: every entry needs a Spanish counterpart,
the core menus and the native macOS app menu are required, later strings
may stay English. german_renders_with_bundled_fonts checks the umlauts,
ß and German quotation marks against the bundled fonts.
Docs: ROADMAP.md, docs/localization-parity.md, docs/gaps.md, ui-egui README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KJhvSJ5odpWzFw9HhPzAix
Add crates/ui-egui/src/i18n/tr.tsv, clean-room Turkish translations matching every entry in the Spanish catalog plus the native macOS Settings menu (3,590 entries).
Language::Tr ("Türkçe") is persisted as `tr` in `general.interfaceLanguage`; it appears in Edit > Language (and native macOS menu), Settings > General, and System Language picks it for `tr-*` locales.
Tests cover the core menus, reject dead entries, verify bundled font rendering for Turkish characters (Ç, Ğ, İ, Ö, Ş, Ü, ç, ğ, ı, ö, ş, ü), switching/persistence across restarts, and locale matching. Attribution sidecar, ATTRIBUTION.md, ui-egui README, gaps.md, and localization-parity.md updated.
Portrait phone photos imported sideways: decode_raster decoded JPEG,
PNG, WebP and TIFF stills with image::load_from_memory, which never
applies the EXIF Orientation tag.
The orientation is now read from the headers alone (no pixels decoded,
the probe's copy of the file dropped before the real decode) and
applied with DynamicImage::apply_orientation before the RGBA
conversion, so MediaInfo, the frame, the auto anchor and Scale to
Frame Size all see the upright size, like rotated phone video. Pixels
still go through load_from_memory, keeping its allocation limit for
hostile headers. A missing, unknown or damaged tag means no transform,
never a failed import.
Tests build every orientation 1-8 in JPEG, PNG, WebP and TIFF and check
size and pixel placement against the EXIF definition, plus open_bytes,
image sequences (also with mixed orientations), invalid values and a
mutation fuzz under catch_unwind. Docs list what is left (YCbCr TIFF,
DNG, the export image overlay) and the effect on projects saved before.
A PC whose GPUs were installed from different driver packages of one vendor
(an ASUS G513QY: the integrated Radeon on the 2021 package, the RX 6800M on a
2026 one) gives the Vulkan loader two versions of that vendor's driver, and
the RX 6800M is listed once through each. wgpu's request_adapter takes the
first discrete entry, the 2021 driver's, and the window drawn through it dies
inside amdvlk64.dll (0xc0000005) before it paints: the main thread is in the
2021 driver, the fault on a thread of the 2026 one, no Rust frame anywhere.
Left to the #820 ladder, two crashed launches end in DirectX 12 only.
filmcraft_gpu::adapter::choose is wgpu's own rule (the adapters that can draw
to the surface, by device type for WGPU_POWER_PREF, then as enumerated) with
one addition: when the adapter that rule picks is listed again by the same
driver (same backend, device type, PCI vendor and device and driver name; the
same bus id when both report one), the entry exposing the most wgpu features
is used, i.e. the newer version (68 against 60 here, a strict superset).
Without such a duplicate the choice is wgpu's own, and entries of one GPU from
different drivers (RADV and AMDVLK, a vendor's driver and Dozen) are left
alone.
graphics::apply installs it as egui-wgpu's native_adapter_selector in the
default mode on Windows, as #609 was asked to be rebased onto the #827
start-up. DirectX 12 only, software mode and the other platforms keep their
adapter choice, and the ladder is untouched. The adapters and the choice go
to the log.
Tests: the laptop's listing (wgpu's rule picks the entry that crashed, choose
the other one), listings without two versions of one driver over every power
preference (choose equals wgpu's rule), surface, bus id and driver cases, and
one against the adapters of the machine running the tests (the rule is what
request_adapter returns there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
E-AC-3 (#788, closes#647), AVI import (#650, closes#598), Russian (#610),
French (#804), Indonesian (#878), pt-BR (#866) and German menus (#630) are
merged; roadmap.md Alpha gate row, B3, M9 and L10N still listed them as open.
G14 heading and summary row now say 4 of the 12 key languages missing (hi, ar,
ko, vi); counts re-measured from crates/ui-egui/src/i18n/*.tsv (union 3,576).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Verified against upstream/main: AVI import (#598, crates/avi), FLAC in MP4
(#603, mp4_flac_audio test), multicam paging beyond 16 angles
(render/multicam.rs page_layout), image-sequence downloads in Chrome (#378,
#624), streamed interchange import (#461, #664), deselect on empty space
(#683) and Project-panel marquee (#578). German (#630) and Russian (#610)
menus are listed. The FLAC-in-MP4 "silent" claim is removed from
codec-parity.md and target-app-parity.md as well.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Problem: Premiere reads Radiance HDR (.hdr) pictures; FilmCraft listed
the extension nowhere and could not open them.
Fix: clean-room RGBE decoder in filmcraft-media from the public format
description: header with FORMAT, all eight axis orders, flat, old
run-length and new per-channel run-length scan lines. Frames are linear
float (Rec.709), like EXR. Allocations are bounded by the data present;
hostile headers and truncated data return errors. "hdr" joins the
importable still extensions; stills and image sequences share the decoder.
Output equals ffmpeg's decode exactly for flat and RLE files; EXPOSURE and
PRIMARIES lines are ignored and XYZE is refused.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Problem: Shift snapped pen segments (#853) but dragging an existing mask
vertex or the whole mask still moved freely.
Fix: while Shift is held the movement since the press is constrained to
horizontal/vertical/diagonal steps (accumulated raw vs applied totals per
gesture, so each frame's delta stays a single undo step). Unit test for
the pure step function; gaps.md G18 updated.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Problem: OpenEXR files with PXR24 compression (a common lossy 24-bit
float format from renderers and compositors) were refused with "not
supported yet".
Fix: decode PXR24 chunks (16 lines, deflated byte planes of per-line
sample differences; floats keep their top 24 bits) into the existing
scan-line pipeline. Bounds-checked, no panics on damaged data. Tests:
hand-built chunks with half/float/uint channels and a partial last
chunk, damaged-file cases, and an OpenImageIO-written oracle comparing
our decode with OpenEXR's own (skips without oiiotool). Docs updated;
stale AVI / FLAC-in-MP4 entries removed from G10.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Problem: the Matroska demuxer mapped A_MPEG/L2 and A_MPEG/L3 only, so a
Layer I track was reported as an unsupported codec even though the MP1/MP2
decoder (#809) reads Layer I.
Fix: add Codec::Mp1 for A_MPEG/L1 and route it through the same packet
duration and decoder path as Mp2. Test remuxes synthetic Layer I frames
into Matroska with ffmpeg and compares our decode (max error 3e-5).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A tiny header naming ~1000 channels on a 64M-pixel line sized each chunk
buffer at hundreds of GB and Vec::with_capacity aborted the process.
Chunks over 256 MB are refused, and the RLE / zlib buffers reserve with
try_reserve_exact. Regression test: huge_scan_lines_are_refused_before_allocating.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>