Files
dev-null-undefinedandClaude 14e0d12813 LR-IMP-PREVIEW-READ: stand-ins and raw thumbnails decode the smallest embedded JPEG that covers them
`embedded_preview_srgb` (the loupe stand-in and the grid thumbnail of an unedited raw) decoded the
largest embedded JPEG whatever the size asked for: a 512 px thumbnail of a Sony A7R VI ARW decoded
its 9984 x 6656 JPEG (5.6 MB) although the file holds a 1616 x 1080 one (0.3 MB).

`lightcraft_raw::embedded_preview_covering(bytes, min_edge)` returns the smallest of a TIFF-based
raw's JPEGs whose long edge is at least `min_edge` and that is a copy of the largest one: aspect
ratio within 1 % (not a letterboxed thumbnail of another shape) and the same number of components
(not the grey semantic masks of an iPhone ProRAW DNG); else the largest, as `embedded_preview`.
The colour-space handling stays in `decode_raw_preview` (a `covering` argument); an unsupported
raw's editing source and the camera-look fit keep the largest JPEG, so a source never depends on
its size level.

On 120 raws of every make in the corpus at 256 / 512 / 1600 px: wherever the largest JPEG is still
chosen the output is identical byte for byte; where a smaller one is (Sony, Nikon, Panasonic,
Adobe DNG) orientation matches, the size is equal or 1-2 px off, mean difference <= 4.7 / 255.

- Rebased onto #1020: its plan route (`preview_window` -> `planned_preview`) now takes the box size and picks
  with `embedded_preview_covering` too, so a TIFF raw read through the sparse plan gets the whole file's JPEG.

Co-Authored-By: Claude <[email protected]>
2026-10-13 14:33:52 +02:00

50 lines
2.1 KiB
Rust

//! Opt-in test of the embedded-preview read path on a **real file the user has** — typically one on a
//! network share, which is where reading only the header instead of the whole raw matters:
//!
//! LC_PREVIEW_RAW="/run/user/1000/gvfs/smb-share:.../DSCF0001.RAF" \
//! cargo test --release -p lightcraft-engine --test preview_read -- --ignored --nocapture
//!
//! It reports what the preview cost and fails when the read is not the small part of the file the
//! header path promises. Skipped without `LC_PREVIEW_RAW` (there is no committed raw fixture: media
//! is never committed, see AGENTS.md).
use std::time::Instant;
#[test]
#[ignore = "needs LC_PREVIEW_RAW pointing at a real raw file"]
fn a_preview_reads_only_the_header_and_the_preview() {
let Ok(path) = std::env::var("LC_PREVIEW_RAW") else {
eprintln!("skipped: set LC_PREVIEW_RAW=<path to a raw file>");
return;
};
let file_len = std::fs::metadata(&path).expect("the file exists").len();
assert!(file_len > 0, "empty file");
let started = Instant::now();
let window = lightcraft_engine::files::preview_window(&path, usize::MAX)
.expect("the file reads")
.expect("it carries an embedded preview the header can point at");
let elapsed = started.elapsed();
let (_, preview, read) = window;
let frac = read.fraction();
println!(
"preview of {path}\n read {} of {} bytes ({:.1} %) in {:.2} s{}",
read.read,
read.total,
frac * 100.0,
elapsed.as_secs_f64(),
if read.planned {
" [plan]"
} else if read.header_path {
" [header path]"
} else {
" [whole file]"
}
);
assert!(read.header_path, "the header or the plan must answer for a raw whose preview is not the whole file");
assert_eq!(read.total, file_len, "the report covers the whole file");
assert!(frac < 0.5, "a preview must not cost most of the file: read {} of {} bytes", read.read, read.total);
assert!(preview.starts_with(&[0xff, 0xd8]), "the preview bytes are a JPEG: {:?}", preview.get(..4));
}