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:
+51
-6
@@ -266,6 +266,10 @@ pub struct Composite {
|
||||
pub lens: Option<String>,
|
||||
pub iso: Option<u32>,
|
||||
pub file_size: u64,
|
||||
/// Made from the final image as develop would first show it
|
||||
/// ([`crate::merge_thumbs`]); empty if that failed, and the grid then
|
||||
/// thumbnails it the ordinary way.
|
||||
pub thumbnails: Vec<(dr_thumbs::ThumbSize, dr_thumbs::Thumbnail)>,
|
||||
}
|
||||
|
||||
impl MergeEvent {
|
||||
@@ -662,6 +666,7 @@ fn run_inner(
|
||||
let _ = std::fs::remove_file(part_name(path));
|
||||
if staged {
|
||||
let _ = std::fs::remove_file(crate::export::destination_record(path));
|
||||
let _ = std::fs::remove_file(crate::export::thumbnails_record(path));
|
||||
}
|
||||
};
|
||||
let (out_w, out_h) = (report_size(&bounds).0, report_size(&bounds).1);
|
||||
@@ -671,6 +676,8 @@ fn run_inner(
|
||||
let profile = dng_profile(first, white_level);
|
||||
let balance = profile.as_shot_neutral.map(|n| 1.0 / n.max(1e-3));
|
||||
let (_, mut carried) = crate::export::header_for_file(&first.meta);
|
||||
// The thumbnails' copy of the profile; the writer takes the original.
|
||||
let thumb_profile = profile.clone();
|
||||
// The composite is dated at the middle of its sweep — the mean of the
|
||||
// frames' capture times — so it sorts among the frames it was made
|
||||
// from, not at the first of them and not at the moment of the merge.
|
||||
@@ -683,6 +690,7 @@ fn run_inner(
|
||||
|
||||
// What the catalog is told about the file (FR-MRG-6), taken before the
|
||||
// header moves into the writer.
|
||||
let thumb_header = carried.clone();
|
||||
let described = (
|
||||
carried.captured_at,
|
||||
carried.captured_offset,
|
||||
@@ -881,6 +889,9 @@ fn run_inner(
|
||||
|
||||
let total_bands = out_h.div_ceil(rows_per_strip) as usize;
|
||||
let mut bands_done = 0usize;
|
||||
// The final image, reduced as it goes by, for the thumbnails (FR-MRG-6).
|
||||
let mut reduced =
|
||||
crate::merge_thumbs::Reduced::new(out_w, out_h, crate::merge_thumbs::SOURCE_EDGE);
|
||||
progress("Merging", 0, total_bands);
|
||||
let merged = pass.merge(
|
||||
&mut adjust,
|
||||
@@ -935,6 +946,7 @@ fn run_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
reduced.push(band.first_row, band.rows, &rgb);
|
||||
band_tx
|
||||
.send(rgb)
|
||||
.map_err(|_| dr_gpu::GpuError::Readback("the writer stopped".into()))
|
||||
@@ -961,10 +973,6 @@ fn run_inner(
|
||||
cleanup(&out_path);
|
||||
return Err(e);
|
||||
}
|
||||
if let Err(e) = std::fs::rename(&part_path, &out_path) {
|
||||
cleanup(&out_path);
|
||||
return Err(format!("{}: {e}", out_path.display()));
|
||||
}
|
||||
// The rectangle the file opens on, as the writer recorded it.
|
||||
let picture = if fill_cam.is_some() {
|
||||
None
|
||||
@@ -975,6 +983,42 @@ fn run_inner(
|
||||
.map(|i| clamp_crop(i.best(), out_w, out_h))
|
||||
.filter(|r| r.width > 0 && r.height > 0)
|
||||
};
|
||||
|
||||
// TRACES: FR-MRG-6
|
||||
// The thumbnails, from the final image, developed as develop will first
|
||||
// show the file. Before the rename, and staged beside the payload before
|
||||
// it: the rename is what lets a drain take the file, and the drain is
|
||||
// what puts these in the store once the server has named it.
|
||||
// The merge's GPU memory back before develop asks for its own.
|
||||
drop(resident);
|
||||
drop(pass);
|
||||
progress("Making thumbnails", 0, 1);
|
||||
let (pw, ph) = picture.map_or((out_w, out_h), |r| (r.width, r.height));
|
||||
let thumbnails = match crate::merge_thumbs::render(
|
||||
ctx,
|
||||
reduced,
|
||||
&thumb_profile,
|
||||
&thumb_header,
|
||||
picture,
|
||||
request.decoder,
|
||||
&crate::merge_thumbs::classes(pw, ph),
|
||||
) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
log::warn!("merge: the thumbnails could not be made: {e}");
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
if staged && !thumbnails.is_empty() {
|
||||
if let Err(e) = crate::export::write_thumbnails(&out_path, &thumbnails) {
|
||||
log::warn!("merge: the thumbnails could not be staged: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = std::fs::rename(&part_path, &out_path) {
|
||||
cleanup(&out_path);
|
||||
return Err(format!("{}: {e}", out_path.display()));
|
||||
}
|
||||
log::info!(
|
||||
"merge: {}×{} written to {} in {:?}",
|
||||
out_w,
|
||||
@@ -1029,14 +1073,15 @@ fn run_inner(
|
||||
.file_name()
|
||||
.map(|n| n.to_string_lossy().into_owned())
|
||||
.unwrap_or_default(),
|
||||
width: picture.map_or(out_w, |r| r.width),
|
||||
height: picture.map_or(out_h, |r| r.height),
|
||||
width: pw,
|
||||
height: ph,
|
||||
captured_at,
|
||||
captured_offset,
|
||||
camera,
|
||||
lens,
|
||||
iso,
|
||||
file_size: std::fs::metadata(&out_path).map(|m| m.len()).unwrap_or(0),
|
||||
thumbnails,
|
||||
};
|
||||
|
||||
Ok(Some(MergeEvent::Done {
|
||||
|
||||
Reference in New Issue
Block a user