mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-19 02:10:25 -05:00
View > Export Slideshow... (and Export PDF / JPEG / Video in Settings > Slideshow) writes the photos in view as their composed slides, between the intro and ending screens that are on: - PDF: a page per slide (1024/1920/3840 px long side, JPEG quality), /Dur and /Trans fades per page, optional /PageMode /FullScreen. The contact sheet's writer moves to engine/src/pdf.rs and streams to disk. - JPEG: <name>-001.jpg... in a folder. - Video: AV1 (rav1e, speed 10, low latency) in MP4 or WebM written by our own muxers (codecs/src/video.rs); fades rendered as blended frames, held slides one key-frame sample with a long duration; 720p/1080p, 24/30 fps; no audio; capped at 2 hours. slideshow.export runs in the background with a row in the activity stack (activity.cancel stops it); outputs are written as .part and renamed when complete, so cancel or failure leaves nothing behind. A photo that can't be rendered is skipped with a warning. slideshow.exportStatus reports the result.
65 lines
5.5 KiB
Markdown
65 lines
5.5 KiB
Markdown
# Slideshow
|
||
|
||
LightCraft's slideshow follows Lightroom Classic's Slideshow module (no Adobe assets, templates or designs): the photos
|
||
in view played full screen with a layout, overlays, intro and ending screens and fades, and exported as PDF, JPEG or
|
||
video. There is no Slideshow module view yet: the settings live in Settings ▸ Slideshow (View ▸ Slideshow Options…)
|
||
next to a live preview of the selected photo's slide, and View ▸ Slideshow (⌥⌘↩) plays.
|
||
|
||
| Part | Commands | Code |
|
||
|---|---|---|
|
||
| Playback: slide and fade durations, fade colour, random order, repeat, preparing slides ahead | `slideshow.playback`, `view.slideshow` | `crates/engine/src/slideshow.rs` (the timeline), `crates/ui-egui/src/panels/slideshow.rs` |
|
||
| Layout, options, backdrop: margins, aspect preview, zoom to fill, stroke, shadow, colour, colour wash, background photo | `slideshow.layout` | `crates/slideshow` (the composer, no UI), `crates/engine/src/slide.rs` |
|
||
| Overlays: identity plate, rating stars, text with tokens, watermark | `slideshow.overlays` | `crates/engine/src/slide_overlay.rs` |
|
||
| Intro and ending screens | `slideshow.titles` | `crates/engine/src/slide_title.rs` |
|
||
| Templates: built-ins of our own and the user's (`<settings>/slideshow-templates/*.json`) | `slideshow.templates.*` | `crates/engine/src/slide_template.rs` |
|
||
| One slide or screen as a PNG | `slideshow.preview` | `crates/engine/src/cmd/slideshow.rs` |
|
||
| Export: PDF, JPEG sequence, AV1 video | `slideshow.export`, `slideshow.exportStatus` | `crates/engine/src/slideshow_export.rs`, `crates/codecs/src/video.rs` |
|
||
|
||
All settings are library preferences (`prefs.json`); sizes and offsets are fractions of the slide's shorter side, so a
|
||
slide looks the same on any screen and at any export size. Playback, the preview, `slideshow.preview` and export draw
|
||
the same pixels through the same composer.
|
||
|
||
## Export
|
||
|
||
View ▸ Export Slideshow… (or Export PDF / JPEG / Video at the end of Settings ▸ Slideshow) chooses a format and size,
|
||
then asks where to write. The slides are the photos in view, in view order, between the intro and ending screens that
|
||
are on; random order and repeat are playback choices and don't apply. The slide aspect is the Layout's aspect preview
|
||
(Screen exports 16:9).
|
||
|
||
- **PDF** — one page per slide, its picture a JPEG at the chosen quality, the page as many points as the slide has
|
||
pixels (1024 / 1920 / 3840 on the long side; 64–8192 by command). Each page has the slide duration (`/Dur`) and,
|
||
after the first, a fade transition (`/Trans`), which PDF viewers use in presentation mode; "Automatically show full
|
||
screen" sets `/PageMode /FullScreen`. Written by `crates/engine/src/pdf.rs` (shared with the contact sheet), streamed
|
||
to disk.
|
||
- **JPEG** — `<name>-001.jpg`, `-002.jpg`… in the chosen folder (created; `name` defaults to the folder's name).
|
||
- **Video** — AV1 in MP4 (default) or WebM, 720p or 1080p (64–2160 by command), 24 or 30 fps (25 / 60 by command), no
|
||
audio (music is a later item, LRC-SS-MUSIC). The timeline is sampled at the frame rate: a slide's fade-in is rendered
|
||
frame by frame (a crossfade, or out to the fade colour and in from it), then the slide is held for the rest of its
|
||
duration as **one** sample lasting that long (variable frame rate), so a still slide costs one encoded frame. That
|
||
frame is a key frame: seeking lands on slides. Encoded by `rav1e` (pure Rust, BSD-2-Clause, already used for AVIF) at
|
||
speed 10, 8-bit 4:2:0 BT.709 limited range, in order (low latency, no frame reordering); at most 2 hours.
|
||
|
||
The muxers are our own (`crates/codecs/src/video.rs`, from the public ISO/IEC 14496-12 + AV1-ISOBMFF and
|
||
Matroska/WebM specifications): MP4 is `ftyp`, one `mdat` (64-bit size patched at the end) and `moov` with an `av01`
|
||
sample entry (`av1C` holding the sequence header OBU, `colr nclx`, `pasp`), `stts` durations, `stss` key frames and a
|
||
single chunk; WebM is the EBML header and a Segment with SeekHead, Info (duration), Tracks (`V_AV1`, `CodecPrivate` =
|
||
`av1C`), Clusters of SimpleBlocks (the last frame a BlockGroup with its duration) and Cues. Samples are temporal units
|
||
without temporal delimiters. No AV1 decoder is in the product, so the tests check the container structure, sample
|
||
counts and durations; ffmpeg / browsers play the files (checked by hand when the muxers change).
|
||
|
||
The export runs on a worker thread with a row in the activity stack ("Exporting slideshow": slides done, or frames for
|
||
video, and the slide being rendered; ✕ or `activity.cancel {id: taskId}` stops it). Every output is written as
|
||
`<file>.part` and renamed when complete, so a cancelled, failed or interrupted export leaves nothing behind (for a JPEG
|
||
sequence, all files are renamed at the end). A photo that can't be rendered (its file gone or unreadable) is skipped
|
||
and named in the result's `warnings`; a full disk or an unwritable folder is an error. An original in the library is
|
||
never overwritten (`check_write_target` before and after).
|
||
|
||
Encoding speed (rav1e speed 10, measured 2026-10-11 on a 32-thread Linux machine shared with other work): one minute of
|
||
slideshow (15 slides of 4 s with 1 s fades, 30 fps: 435 encoded frames) took 35 s at 720p and 74 s at 1080p, slide
|
||
rendering included. The cost is in the fades (one encoded frame per frame of fade); held slides cost one frame each.
|
||
|
||
## Not yet
|
||
|
||
The Slideshow module view (left-panel template browser, slide editor with draggable guides and overlays), music and
|
||
sync to music, pan & zoom, H.264 video, saving a slideshow with an album, export presets.
|