Files
Jason Baldwin b391307009 LRC-SS-EXPORT: slideshow export to PDF, JPEG sequence and AV1 video
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.
2026-10-11 10:35:09 +02:00

65 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.