Thumbnail a composite during the merge, as develop first shows it

The DNG a merge writes has no embedded preview, and an embedded preview
is all the grid's thumbnail path reads, so a composite stood in the grid
as a blank cell until it was opened. Reading an 800 MB file back to make
one would cost what the merge already has in hand.

The bands are box-reduced as they are written, after the border fill,
into a copy 4096 pixels long. That copy is written as a linear DNG in
memory with the composite's own profile, header and crop, and opened
through open_session, the function develop opens every file with: the
same decode, the default graph and view transform, the as-shot white
balance and the conversion to the display's space. The grid and large
thumbnails are rendered from that session, staged beside the payload
before the rename releases it to a drain, and put in the store under the
file id once the upload has learned it. Until then the grid draws them
from memory, so the cell is not blank while the file is uploading.

A test develops a synthetic composite both ways, the whole file as
develop opens it and the merge's reduced copy, and holds the thumbnail's
mean, 95th and 99.5th luma percentiles to within 3-4 levels of develop's
render; the naive balanced-and-gamma picture the merge's preview draws
misses by 13.
This commit is contained in:
2026-09-28 19:57:37 -04:00
parent 98a67393d9
commit ce5b7d72e3
6 changed files with 527 additions and 7 deletions
+11
View File
@@ -113,6 +113,16 @@ pub struct LibraryController {
/// two states apart — without it a carried thumbnail either blocks the
/// sharper fetch forever or is re-fetched on every scroll.
pub(super) thumb_class: RefCell<Vec<Option<dr_thumbs::ThumbSize>>>,
/// TRACES: FR-MRG-6
/// Thumbnails a merge made for a composite it has just catalogued, by
/// image, decoded and at each class it made.
///
/// The grid's own path keys the store on the server's file id, and a
/// composite has none until its upload lands — minutes, on Nextcloud. The
/// merge's pixels are drawn from here until then, so the cell is not
/// blank in the meantime; after, the store holds the same ones.
pub(super) merged_thumbs:
RefCell<std::collections::HashMap<i64, Vec<(dr_thumbs::ThumbSize, slint::Image)>>>,
/// Where in the catalog the current window starts. Scrubbing moves this.
pub(super) offset: RefCell<usize>,
/// The first visible ordinal, kept so returning from the develop view lands
@@ -449,6 +459,7 @@ impl LibraryController {
needs_metadata: RefCell::new(Vec::new()),
captured_at: RefCell::new(Vec::new()),
thumb_class: RefCell::new(Vec::new()),
merged_thumbs: RefCell::new(Default::default()),
offset: RefCell::new(0),
resume_at: std::cell::Cell::new(0),
roll_open: std::cell::Cell::new(None),