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:
2026-09-24 21:33:14 -04:00
parent d8fb382ce9
commit 414094bd38
10 changed files with 214 additions and 129 deletions
+18 -8
View File
@@ -61,7 +61,7 @@ use crate::library::{FaceModelPaths, MetadataFound};
/// native render serves every face handler and the metadata one besides.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Input {
/// The first `dr_decode::HEADER_BYTES` of the file: EXIF, and the
/// The first `Decoder::header_bytes` of the file: EXIF, and the
/// embedded preview's offset.
Header,
/// The whole original, undecoded.
@@ -165,15 +165,18 @@ pub struct Target {
pub struct Fetched {
bytes: Vec<u8>,
input: Input,
/// What opens [`Self::session`]; the job's, from its [`Toolkit`].
decoder: &'static dyn dr_decode::Decoder,
session: Option<crate::develop::DevelopSession>,
frame: Option<dr_export::Frame>,
}
impl Fetched {
fn new(bytes: Vec<u8>, input: Input) -> Self {
fn new(bytes: Vec<u8>, input: Input, decoder: &'static dyn dr_decode::Decoder) -> Self {
Self {
bytes,
input,
decoder,
session: None,
frame: None,
}
@@ -194,8 +197,10 @@ impl Fetched {
return Err(Failure::Other("only the header was fetched".into()));
}
if self.session.is_none() {
self.session =
Some(crate::library::open_native(gpu, &self.bytes).map_err(Failure::Unreadable)?);
self.session = Some(
crate::library::open_native(gpu, self.decoder, &self.bytes)
.map_err(Failure::Unreadable)?,
);
}
Ok(self.session.as_mut().expect("just opened"))
}
@@ -234,6 +239,9 @@ pub struct Toolkit {
pub model_id: String,
pub store: ThumbStore,
pub options: dr_face::DetectOptions,
/// TRACES: FR-RAW-2
/// What reads headers and opens originals for every handler.
pub decoder: &'static dyn dr_decode::Decoder,
}
impl Toolkit {
@@ -692,12 +700,12 @@ fn crop(
/// marked examined too, so it is not fetched again -- the state the sweep
/// records for the dateless, for the same reason.
fn metadata(
_tk: &mut Toolkit,
tk: &mut Toolkit,
catalog: &Catalog,
target: &Target,
fetched: &mut Fetched,
) -> Result<usize, Failure> {
let found = match dr_decode::metadata(fetched.bytes()) {
let found = match tk.decoder.metadata(fetched.bytes()) {
Ok(md) => MetadataFound {
image_id: target.image.0 as i64,
captured_at: md.captured_at,
@@ -1065,6 +1073,7 @@ pub fn spawn(
model_id: model_id.clone(),
store,
options,
decoder: dr_decode::default(),
};
let repairs = registry(scope, &model_id, detector, caps);
@@ -1144,6 +1153,7 @@ pub fn spawn(
// photograph. Every file borrowed is given back (ARCH §9.0a).
let pool = dr_sync_folder::BorrowPool::new();
let header_bytes = tk.decoder.header_bytes();
for chunk in planned.chunks(LANES) {
let fetched = crate::library::futures_join_all(chunk.iter().map(|p| {
let backend = &*backend;
@@ -1151,7 +1161,7 @@ pub fn spawn(
async move {
let id = RemoteId::Path(RemotePath::new(&p.target.path));
let got = if p.input == Input::Header {
backend.get(&id, Some(0..dr_decode::HEADER_BYTES)).await
backend.get(&id, Some(0..header_bytes)).await
} else {
let held = match pool
.borrow(backend, &RemotePath::new(&p.target.path))
@@ -1185,7 +1195,7 @@ pub fn spawn(
}
};
let mut fetched = Fetched::new(bytes, p.input);
let mut fetched = Fetched::new(bytes, p.input, tk.decoder);
let mut wrote = 0usize;
let mut broke = false;
for (repair, set) in repairs.iter().zip(sets.iter()) {