Route dr-ui's decoding through the Decoder trait
With the trait in place the claim still meant nothing while every caller named dr_decode's free functions: a second decoder would have had to be threaded through the scan, the thumbnail ladder, import, the viewer, export, merge and repairs at the moment it arrived. Each of those now takes a &dyn Decoder and reads headers, previews, orientation and sensor data through it, including the header budget a remote fetch asks for (header_bytes) and where it finds the embedded preview (locate_preview). Only the places that start a job name dr_decode::default(): the thumbnail, sweep and thumbnail-sweep threads, the viewer's open handlers, and the request structs a job is handed (BatchRequest, MergeRequest, the import Request, the repairs Toolkit), so a caller can be given another decoder by changing what it is handed. The default is rawler through the same free functions as before, so nothing a user sees changes. The trait gains Debug as a supertrait so request structs that derive Debug can carry one.
This commit is contained in:
@@ -97,6 +97,9 @@ pub fn spawn_thumbnails(
|
||||
catalog_path: PathBuf,
|
||||
) -> Receiver<ThumbnailMessage> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
// TRACES: FR-RAW-2
|
||||
// The one place this job names a decoder; everything below takes it.
|
||||
let decoder = dr_decode::default();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut store = match ThumbStore::open(&store_dir) {
|
||||
@@ -222,7 +225,7 @@ pub fn spawn_thumbnails(
|
||||
let mut offline = false;
|
||||
|
||||
for req in to_fetch {
|
||||
let msg = fetch_one(&*backend, store.as_mut(), &req, &mut found).await;
|
||||
let msg = fetch_one(&*backend, decoder, store.as_mut(), &req, &mut found).await;
|
||||
offline = matches!(msg, ThumbnailMessage::Offline { .. });
|
||||
// A closed channel means the window went away mid-fetch.
|
||||
if tx.send(msg).is_err() || offline {
|
||||
@@ -247,7 +250,7 @@ pub fn spawn_thumbnails(
|
||||
if tx.send(ThumbnailMessage::DateProgress).is_err() {
|
||||
break;
|
||||
}
|
||||
read_metadata_only(&*backend, &req, &mut found).await;
|
||||
read_metadata_only(&*backend, decoder, &req, &mut found).await;
|
||||
|
||||
if found.len() >= FLUSH_EVERY {
|
||||
flush_metadata(&catalog_path, &mut found, &tx);
|
||||
@@ -267,11 +270,12 @@ pub fn spawn_thumbnails(
|
||||
|
||||
pub(super) async fn fetch_one(
|
||||
backend: &dyn RemoteBackend,
|
||||
decoder: &dyn dr_decode::Decoder,
|
||||
store: Option<&mut ThumbStore>,
|
||||
req: &ThumbnailRequest,
|
||||
found_metadata: &mut Vec<MetadataFound>,
|
||||
) -> ThumbnailMessage {
|
||||
let preview = match fetch_preview(backend, req, found_metadata).await {
|
||||
let preview = match fetch_preview(backend, decoder, req, found_metadata).await {
|
||||
PreviewOutcome::Ready(p) => p,
|
||||
PreviewOutcome::Unavailable(reason) => {
|
||||
return ThumbnailMessage::Unavailable {
|
||||
@@ -308,7 +312,7 @@ pub(super) async fn fetch_one(
|
||||
/// ([`spawn_thumbnail_sweep`]) fetches on several lanes at once and stores the
|
||||
/// results on the one thread that owns the store, so it needs the pixels
|
||||
/// *before* anything is written or addressed to a cell.
|
||||
pub(super) enum PreviewOutcome {
|
||||
pub(crate) enum PreviewOutcome {
|
||||
Ready(dr_decode::Preview),
|
||||
/// This image has no usable preview. The batch continues past it.
|
||||
Unavailable(String),
|
||||
@@ -322,8 +326,9 @@ pub(super) enum PreviewOutcome {
|
||||
/// was wrong in a way that looked like corruption: fetching a fixed prefix cut
|
||||
/// the embedded JPEG partway through, and decoders render a truncated JPEG as
|
||||
/// the top fraction of the frame rather than reporting an error.
|
||||
pub(super) async fn fetch_preview(
|
||||
pub(crate) async fn fetch_preview(
|
||||
backend: &dyn RemoteBackend,
|
||||
decoder: &dyn dr_decode::Decoder,
|
||||
req: &ThumbnailRequest,
|
||||
found_metadata: &mut Vec<MetadataFound>,
|
||||
) -> PreviewOutcome {
|
||||
@@ -341,7 +346,7 @@ pub(super) async fn fetch_preview(
|
||||
};
|
||||
|
||||
// Stage one: the header, enough to parse the container's IFDs.
|
||||
let header = match backend.get(&id, Some(0..dr_decode::HEADER_BYTES)).await {
|
||||
let header = match backend.get(&id, Some(0..decoder.header_bytes())).await {
|
||||
Ok(b) => b,
|
||||
Err(e) => return classify(e),
|
||||
};
|
||||
@@ -349,15 +354,15 @@ pub(super) async fn fetch_preview(
|
||||
// The same bytes carry EXIF. Reading it here is free — the alternative is
|
||||
// a second 256 KB fetch per image over the whole library.
|
||||
if req.needs_metadata {
|
||||
collect_metadata(backend, &id, &header, req, found_metadata).await;
|
||||
collect_metadata(backend, decoder, &id, &header, req, found_metadata).await;
|
||||
}
|
||||
|
||||
// Read unconditionally, unlike the rest of the EXIF above: `needs_metadata`
|
||||
// is false once an image has been catalogued, but a thumbnail can still be
|
||||
// regenerated long after that — a cleared cache, a new size — and a
|
||||
// thumbnail that came out upright the first time must come out upright
|
||||
// every time. This is a header walk, not a decode; see `dr_decode::orientation`.
|
||||
let orientation = dr_decode::orientation(&header).unwrap_or_default();
|
||||
// every time. This is a header walk, not a decode; see `Decoder::orientation`.
|
||||
let orientation = decoder.orientation(&header).unwrap_or_default();
|
||||
|
||||
// A plain JPEG is its own preview; anything else needs locating.
|
||||
let bytes = if header.starts_with(&[0xFF, 0xD8, 0xFF]) {
|
||||
@@ -366,7 +371,7 @@ pub(super) async fn fetch_preview(
|
||||
Err(e) => return classify(e),
|
||||
}
|
||||
} else {
|
||||
let Some(loc) = dr_decode::locate_preview(&header, req.size) else {
|
||||
let Some(loc) = decoder.locate_preview(&header, req.size) else {
|
||||
// No locatable preview. Declining beats fetching the whole file:
|
||||
// that is the 370 GB path FR-NC-3 exists to avoid.
|
||||
return PreviewOutcome::Unavailable("no locatable embedded preview".into());
|
||||
@@ -474,12 +479,13 @@ pub(crate) fn store_thumbnail(
|
||||
/// and the header-only pass for images whose pixels were already cached.
|
||||
pub(super) async fn collect_metadata(
|
||||
backend: &dyn RemoteBackend,
|
||||
decoder: &dyn dr_decode::Decoder,
|
||||
id: &RemoteId,
|
||||
header: &[u8],
|
||||
req: &ThumbnailRequest,
|
||||
out: &mut Vec<MetadataFound>,
|
||||
) {
|
||||
let md = match dr_decode::metadata(header) {
|
||||
let md = match decoder.metadata(header) {
|
||||
Ok(md) => md,
|
||||
Err(first) => {
|
||||
// A file whose IFDs follow its pixels — the linear DNG a merge
|
||||
|
||||
Reference in New Issue
Block a user