The Linux, macOS, web and Windows packaging still carried PhotoCraft's
image-editor metadata.
- nfpm.yaml (#94): deb/rpm summary and description describe the video
editor; filmcraft-cli is a command runner, MCP server and exporter;
Debian section video instead of graphics.
- AppStream (#100): AudioVideo/Video/AudioVideoEditing categories, matching
the desktop entry; video keywords; the screenshot is the README hero image
(the old filmcraft-demo.jpg URL was a 404).
- macOS Info.plist.in (#100): export the .fcproj project type (JSON) instead
of PhotoCraft's .pcraft; app category video; drop the PSB and QOI imported
types, which FilmCraft does not read.
- filmcraft.wxs header comment: the MSI declares no file associations.
- web hosting guide: iframe title "FilmCraft video editor".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Español next to English and 日本語 (Edit ▸ Language ▸ Español, also in the
native menu bar). SPANISH covers every menu, submenu and menu item label
(347 entries, Latin American Spanish); names, formats and percentages stay
as they are. A new test checks that every entry is a current menu label, so
a renamed command cannot leave a dead entry behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel menu mark (≡) on the active tab was three 1.5 pt lines placed
at fractional positions, so at 1x they straddled pixels and anti-aliasing
made the gaps look uneven (3 px then 4 px). Every line icon had the same
half-pixel blur.
The icon pen now draws strokes a whole number of device pixels wide and
snaps every horizontal or vertical segment (and any that runs less than
a pixel sideways) onto the pixel grid, rounding from the icon's centre
the same way on both sides, so equal steps on the 16 grid stay equal on
screen at 1x and 2x. The tab's menu mark is now the Hamburger icon drawn
by the pen instead of hand-drawn lines.
Tests: every icon at 1x and 2x at four sizes off the pixel grid has only
whole-pixel straight lines; the menu mark's two gaps are equal at 1x,
1.5x and 2x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three problems with scrolling the Timeline:
1. Cmd + wheel zoomed, like Option + wheel. egui turns Cmd + wheel into a
zoom before the panel sees it, and the panel only knew "zoom" and "scroll
sideways".
2. The plain wheel scrolled sideways; the tracks could only be scrolled up
and down with Shift held.
3. The view could be scrolled without limit past the end of the sequence (39
minutes past a 26 second one). There the zoom scroll bar's thumb sat at
the right end and stayed there when dragged: the bar's range grew with the
scroll position, so the view moved but the thumb did not.
Now, as Premiere Pro 26.5.2 does on macOS (checked in the app):
- wheel: the tracks under the pointer, up and down (video tracks above the
divider, audio tracks below);
- Cmd + wheel: the Timeline, sideways;
- Option + wheel: zoom about the pointer.
The existing setting Timeline Mouse Scrolling ("Vertical" by default) is
wired up: "Horizontal" swaps the first two. A sideways gesture (trackpad
swipe, tilt wheel, Shift + wheel) always scrolls sideways and a pinch zooms.
The wheel is read from its events, with their own modifiers.
The Timeline ends ten minutes past the end of the sequence, where Premiere's
view stops too, and the view is kept inside it however it is moved (wheel,
scroll bar, zoom, a script; not-a-number positions are reset).
The zoom scroll bar spans that extent. Its thumb is as wide as the share in
view, never narrower than 34 points, and its travel covers the whole scroll
range: it reaches both ends of the bar and stays under the pointer while
dragged (the drag follows the pointer's position, not a sum of steps). A
click or press beside the thumb brings the thumb there. The handles at its
ends zoom as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The probe guessed the pixel format from the codec profile alone: HEVC
reported nothing, H.264 High 4:2:2 had no bit depth, and ProRes 4444
claimed alpha because of its fourcc.
- HEVC: chroma format and bit depth from hvcC.
- H.264: from the avcC High-profile extension (ISO/IEC 14496-15 §5.3.3),
else from the first SPS; unknown (empty) if neither can be read.
- ProRes: the first frame header gives the chroma format and alpha
(alpha_channel_type), as the first DNx frame header already does.
Checked against ffprobe on ffmpeg-made HEVC 4:2:0/4:2:2 8/10-bit,
H.264 4:2:0/4:2:2 8/10-bit and ProRes 422 HQ / 4444 with and without
alpha. Unit tests cover hvcC, avcC (extension and SPS fallback) and
ProRes 4444 with and without alpha.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The GPU compositor uploaded only the Y, Cb and Cr planes of a Yuv8/Yuv16 frame and
the shader returned alpha 1.0 for them, so a ProRes 4444 overlay (banner, lower third,
outro) was drawn opaque in the Program monitor and covered the footage under it.
Exports were correct because they use the CPU compositor.
Frames with an alpha plane now upload it as a fourth texture (R8Unorm, or R16Float
converted like the other planes), bound at binding 5 of the layer and blend layouts.
A uniform slot (p3.y) carries the alpha scale (0 = no alpha plane); the shader reads
the alpha at full resolution and premultiplies the linear colour by it.
Adds gpu_draws_yuv_alpha_plane_like_the_cpu: an alpha ramp over an opaque layer at
8, 10 and 12 bits, GPU against the CPU compositor. It fails without the change
(p99 diff 190/255) and passes with it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The clip duration came from the media duration (`mdhd`), which runs on
the decode timeline. ffmpeg / x264 MP4s with B-frames skip the reorder
delay with an edit, so every one imported a frame too long (a 120-frame
file became 121 frames, the last a held repeat). A video track delayed
by a leading empty edit was a frame short, and its last frame could not
be reached.
Use the sum of the track's edits (movie timescale, what `tkhd` records)
when there is an edit list. Fragmented files, and edit lists that add up
to nothing or overflow a Tick, keep the media duration.
Tests: a unit test with crafted edit lists (skip, empty edit, hostile
sizes) and an ffmpeg oracle test (libx264 with B-frames: 60 frames,
was 61).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An overwrite move shortens, splits or removes whatever already sits where a
clip lands, as Premiere's drag does, but the reply only named the clips it
moved: a script moving clips one at a time never learned that an earlier move
had cut one it had not reached yet. The reply now lists each covered clip
under `overwritten` with its length before and after and the ids what is
left of it lives on as. The edit itself is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ffmpeg prints "corrupt decoded frame" as a warning, so the oracle's
`-v error -ec 0` decode let a stream with a whole slice missing pass
silently. Switching that decode to `-v warning` is not enough: with
`-ec 0`, ffmpeg 7.1/8.1 flags every picture that has more than one
slice, libx264's multi-slice streams included, while the pixels are
unchanged. That is the 119/120 in #74; the encoder's bitstream is not
at fault (VideoToolbox decodes the same streams bit-identically, and
ffmpeg with strict error detection reports nothing).
The oracle now decodes with ffmpeg's default concealment, `-v warning`
and `-err_detect +crccheck+bitstream+buffer+explode`, fails on any
output or a non-zero exit, and still compares every pixel with the
encoder's reconstruction. A negative control drops and truncates
slices (CAVLC and CABAC, 3 slices) and checks the oracle reports each
one; it fails with the old flags.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem (#73): a 30 fps WebM played and exported with every third frame
missing and the frame before it shown twice (20 unique frames a second).
The same happens at 29.97, 60, 59.94, 24 and 23.976 fps, and with MP4
files whose video track has a 1 ms timescale. 25 and 50 fps were fine.
Cause: Matroska stores block timestamps in TimestampScale units (usually
1 ms), rounded to the nearest unit, so at 30 fps frame 2 (66.67 ms) is
stored as 67. MkvSource::video_frame converted the requested time to track
units with a floor (66), and the lookup (last sample with pts <= target)
returned frame 1. Every frame whose timestamp was rounded up was skipped.
Mp4Source::video_at, and the web build's WebCodecs MP4 path, floored the
same way against the track timescale; normal MP4 timescales hold frame
times exactly, so only coarse (1000) timescales were hit.
Fix: new Tick::to_rational_round (nearest unit, halves up, total for zero
and negative timebases), used for the video lookup target and the
catch-up threshold in mkv.rs, mp4.rs and webcodecs.rs. A requested time
now matches a frame whose stored timestamp is within half a unit of it,
the same precision the timestamps have. Truncating muxers (stamps rounded
down) still work. Times on exact timebases (90 kHz, 15360, 30000/1001…)
convert exactly as before.
Test: crates/codecs/src/rounded_pts_tests.rs builds synthetic Motion-JPEG
streams in the test (each picture shows its index as black/white blocks),
muxed as WebM with 1 ms timestamps and as MOV with a 1000 timescale, at
30, 29.97, 60, 59.94, 24, 23.976 and 25 fps with nearest and truncated
stamps. Requesting every frame's start over 3 s must show every frame
exactly once, and every frame's midpoint must show that frame. Before the
fix both tests failed ("30 fps, Nearest ms stamps: 30 of 90 frames never
shown [2, 5, 8, 11]…, 30 shown twice [1, 4, 7, 10]…"). The time crate
tests cover the rounding, ties and hostile timebases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merged Japanese interface (#38) looked only for a system font. When
FilmCraft is built with craft-fonts, theme::install already appends the
Japanese faces to every font family, so switching to Japanese uses them
(no system scan) and works on the web too; otherwise it falls back to an
installed system font as before. Docs use an absolute CRAFT_FONTS_DIR
(build.rs runs in crates/text, so a relative path resolves from there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A corner handle scales both axes alike with the opposite corner held in place;
the edge handles (top, right, bottom, left) are now grabbable and stretch only
their own axis with the opposite edge held in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging a corner handle of a graphic layer in the Program monitor scaled about
the layer's anchor, so a text layer (anchor at its bottom-left) grew up and to
the right whichever corner was dragged. The opposite corner now stays in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A short export can finish inside one 100 ms poll, which left a single progress
notification and no time to answer the ping. The test now tries longer exports
until one outlasts a few polls, and cancels a longer one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/text/build.rs embeds the fonts of a storytold/craft-fonts checkout
named by CRAFT_FONTS_DIR as filmcraft_text::fonts::CRAFT_FONTS (native:
all of them; wasm32: only BIZ UDPGothic Regular). Unset, CRAFT_FONTS is
empty and nothing changes. A bad CRAFT_FONTS_DIR is a warning, or an
error with CRAFT_FONTS_REQUIRED=1. Never a Cargo dependency, no network.
- UI: the Japanese faces are the last fallback of every egui font family
(BIZ UDPGothic first, Bold first in the semibold/medium families).
- Text engine: registered after the bundled faces; fallback_for picks
them after the bundled faces and before system faces (Mincho for serif
text, Gothic otherwise, nearest weight).
- Tests: Japanese glyphs (no tofu) in the UI and title paths, skipped
without craft-fonts; the theme works with CRAFT_FONTS empty.
- Release: every build job checks out craft-fonts at a pinned commit and
builds with CRAFT_FONTS_DIR + CRAFT_FONTS_REQUIRED; packages carry
OFL-<family>.txt. NOTICE, ATTRIBUTION.md (optional build input), AGENTS.md,
CLAUDE.md, README and docs/contributing.md describe it.
FilmCraft main bundles no Japanese font, so no font files move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>