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:
+30
-10
@@ -92,6 +92,9 @@ pub struct Request {
|
||||
/// Where the photographs are going. An import without one has nowhere to
|
||||
/// put anything, and the page refuses to start.
|
||||
pub upload: Upload,
|
||||
/// TRACES: FR-RAW-2
|
||||
/// What reads each candidate's header and cuts its thumbnail.
|
||||
pub decoder: &'static dyn dr_decode::Decoder,
|
||||
}
|
||||
|
||||
/// TRACES: FR-NC-7a | FR-NC-7b
|
||||
@@ -283,7 +286,7 @@ fn run(request: Request, cancel: &Cancel, tx: &Sender<Message>) -> Result<(), St
|
||||
|
||||
let report = ingest.run(
|
||||
&candidates,
|
||||
&|c| probe(&card, c),
|
||||
&|c| probe(&card, request.decoder, c),
|
||||
&|key| is_duplicate(catalog.connection(), key),
|
||||
&|| cancel.load(Ordering::Relaxed),
|
||||
&mut |p| {
|
||||
@@ -303,7 +306,14 @@ fn run(request: Request, cancel: &Cancel, tx: &Sender<Message>) -> Result<(), St
|
||||
// photograph — only a transfer.
|
||||
{
|
||||
let upload = &request.upload;
|
||||
let (sent, thumbnails) = upload_all(upload, &library, &report.imported, cancel, tx);
|
||||
let (sent, thumbnails) = upload_all(
|
||||
upload,
|
||||
request.decoder,
|
||||
&library,
|
||||
&report.imported,
|
||||
cancel,
|
||||
tx,
|
||||
);
|
||||
outcome.thumbnails = thumbnails;
|
||||
outcome.uploaded = sent.iter().filter(|s| s.transferred).count();
|
||||
outcome.already_on_server = sent.len() - outcome.uploaded;
|
||||
@@ -370,6 +380,7 @@ fn run(request: Request, cancel: &Cancel, tx: &Sender<Message>) -> Result<(), St
|
||||
/// kept and a card emptied.
|
||||
fn upload_all(
|
||||
upload: &Upload,
|
||||
decoder: &dyn dr_decode::Decoder,
|
||||
library: &LocalStorage,
|
||||
imported: &[Imported],
|
||||
cancel: &Cancel,
|
||||
@@ -418,7 +429,7 @@ fn upload_all(
|
||||
// Before the body is handed over: `upload_original` takes it by
|
||||
// value, and re-reading the file to make a thumbnail afterwards
|
||||
// would be a second full read of an 80 MB original.
|
||||
let thumbnail = make_thumbnail(&bytes);
|
||||
let thumbnail = make_thumbnail(decoder, &bytes);
|
||||
|
||||
// The same folder segments the local copy went into, so the two
|
||||
// libraries have the same shape (FR-NC-7a).
|
||||
@@ -473,10 +484,15 @@ struct Sent {
|
||||
/// than the full preview's. An embedded preview is written in the sensor's
|
||||
/// orientation, so without this every portrait frame lies on its side — and
|
||||
/// the store is keyed by file and size alone, so it would stay that way.
|
||||
fn make_thumbnail(bytes: &[u8]) -> Option<dr_thumbs::Thumbnail> {
|
||||
let mut preview = dr_decode::extract_preview(bytes, dr_decode::PreviewSize::Thumbnail).ok()?;
|
||||
pub(crate) fn make_thumbnail(
|
||||
decoder: &dyn dr_decode::Decoder,
|
||||
bytes: &[u8],
|
||||
) -> Option<dr_thumbs::Thumbnail> {
|
||||
let mut preview = decoder
|
||||
.preview(bytes, dr_decode::PreviewSize::Thumbnail)
|
||||
.ok()?;
|
||||
preview.downscale_to(dr_thumbs::ThumbSize::Grid.edge());
|
||||
preview.apply_orientation(dr_decode::orientation(bytes).unwrap_or_default());
|
||||
preview.apply_orientation(decoder.orientation(bytes).unwrap_or_default());
|
||||
let encoded = dr_thumbs::encode_rgba(preview.width, preview.height, &preview.rgba).ok()?;
|
||||
Some(dr_thumbs::Thumbnail {
|
||||
width: preview.width,
|
||||
@@ -676,14 +692,18 @@ pub fn survey(
|
||||
|
||||
/// Read one candidate's capture metadata.
|
||||
///
|
||||
/// A header read rather than the whole file: `dr_decode::HEADER_BYTES` is
|
||||
/// A header read rather than the whole file: the decoder's `header_bytes` is
|
||||
/// enough for EXIF on every format in the tree, and reading 80 MB per file to
|
||||
/// learn a date would take longer than the import itself.
|
||||
fn probe(card: &dyn Storage, candidate: &Candidate) -> Option<Shot> {
|
||||
fn probe(
|
||||
card: &dyn Storage,
|
||||
decoder: &dyn dr_decode::Decoder,
|
||||
candidate: &Candidate,
|
||||
) -> Option<Shot> {
|
||||
let header = card
|
||||
.read_range(&candidate.source, 0..dr_decode::HEADER_BYTES)
|
||||
.read_range(&candidate.source, 0..decoder.header_bytes())
|
||||
.ok()?;
|
||||
let md = dr_decode::metadata(&header).ok()?;
|
||||
let md = decoder.metadata(&header).ok()?;
|
||||
Some(Shot {
|
||||
captured_at: md.captured_at,
|
||||
captured_offset: md.captured_offset,
|
||||
|
||||
Reference in New Issue
Block a user