LR-IMP-PREVIEW-READ: a raw whose embedded preview can't be read still gets a thumbnail (fixes #1448)

The grid queues no render behind an unedited raw's quick thumbnail, since the embedded JPEG is
meant to be the thumbnail. But whether that JPEG can be read is only known when the quick job
runs, and `quick_thumb_job` had no fallback: when the preview loader returned nothing the job
failed with "no quick preview", the failure was remembered for that key, and the cell stayed
empty until an edit changed it. The quick job now carries the real thumbnail job as its last
resort, as `quick_view_job` already does for the loupe.
This commit is contained in:
Marco Arnulfo
2026-10-13 09:01:48 +02:00
parent b3d392b499
commit c10ca4e958
3 changed files with 29 additions and 3 deletions
+21
View File
@@ -2296,4 +2296,25 @@ mod tests {
assert_eq!(s.quick_view_job(id, 1600, true).unwrap().run().quick, Some(QuickSource::Small));
let _ = std::fs::remove_dir_all(&dir);
}
/// Issue #1448: the grid renders nothing behind an unedited raw's quick thumbnail, so when the
/// embedded preview can't be read (and nothing is cached) the quick job itself must render the
/// raw, or the cell stays empty for good ("no quick preview").
#[test]
fn a_quick_thumbnail_without_an_embedded_preview_renders_the_raw() {
use crate::media::QuickSource;
let dir = std::env::temp_dir().join(format!("lc-quick-none-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("a.cr3");
std::fs::write(&path, cr3_with_preview(96, 64)).unwrap();
let mut s = crate::Session::new().with_fs();
let r = s.execute("library.import", &serde_json::json!({"paths": [path.to_string_lossy()]})).unwrap();
let id = lightcraft_catalog::PhotoId(r["imported"][0].as_u64().unwrap());
s.media.preview_loader = Some(std::sync::Arc::new(|_: &str, _| None));
let job = s.thumb_job(id, 128).unwrap();
let q = s.quick_thumb_job(&job).unwrap().run();
assert_eq!((q.quick, q.key), (Some(QuickSource::Small), job.key));
assert!(q.rendered.is_ok(), "{:?}", q.rendered.err());
let _ = std::fs::remove_dir_all(&dir);
}
}
+5 -3
View File
@@ -1427,8 +1427,10 @@ impl crate::Session {
/// A first grid thumbnail for an unedited raw: the cached render for `job` (the real
/// thumbnail job, from [`Self::thumb_job`]) if there is one — then the result carries
/// [`QuickSource::Cached`] and `job.key` and is final — else its embedded preview. `None` for
/// other photos (render them directly).
/// [`QuickSource::Cached`] and `job.key` and is final — else its embedded preview, else `job`
/// itself: the grid queues no render behind an unedited raw's quick thumbnail
/// ([`Self::embedded_thumb_is_final`]), so a preview that can't be read must not leave the
/// cell empty (issue #1448). `None` for other photos (render them directly).
pub fn quick_thumb_job(&mut self, job: &RenderJob) -> Option<QuickJob> {
let p = self.catalog.photo(job.photo)?.clone();
let (path, l) = self.embedded_of(&p)?;
@@ -1439,7 +1441,7 @@ impl crate::Session {
key: job.key,
cached: job.cache.clone().into_iter().collect(),
embedded: Some((path, l, edge)),
small: None,
small: Some(Box::new(job.clone())),
})
}
+3
View File
@@ -101,6 +101,9 @@ loupe render of a cold photo finished 174 s after the click, most of it spent be
- `grid::skips_the_real_render(embedded_is_final, from_embedded)` is the decision, unit-tested:
a **cached render** as the stand-in, an **edited photo** and a **raw with no embedded JPEG** all
still render, because their thumbnail must show the pipeline's output rather than the camera's.
- Whether the embedded JPEG can actually be read is only known when the quick job runs, so
`Session::quick_thumb_job` carries the real thumbnail job as its last resort: a raw whose preview
the loader can't read renders the raw instead of leaving the cell empty (issue #1448).
- The thumbnail key still changes with the settings hash, so editing a photo re-renders its
thumbnail through the ordinary background path.