use crate::{
error::Error,
protocol::{data::Rect, decode::Samples},
scan::{
autoexpose::Exposures,
clean::clean_frame,
focus::Focus,
pass::{Pass, Progress},
window::Recipe,
},
session::Session,
};
use std::{ops::ControlFlow, time::Duration};
const SCAN_TIMEOUT: Duration = Duration::from_secs(1800);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Phase {
Meter(usize),
Scan,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Options<'a> {
pub exposures: Option<&'a Exposures>,
pub lock_white_balance: bool,
pub clean: bool,
}
pub struct Scanned {
pub pass: Pass,
pub exposures: Exposures,
pub cleaned: Option<usize>,
}
pub fn scan_frame(
session: &mut Session,
recipe: &Recipe,
frame: Rect,
options: Options,
samples: &mut Samples,
) -> Result<Scanned, Error> {
scan_frame_with(session, recipe, frame, options, samples, |_, _| {
ControlFlow::Continue(())
})
}
pub fn scan_frame_with(
session: &mut Session,
recipe: &Recipe,
frame: Rect,
options: Options,
samples: &mut Samples,
mut on: impl FnMut(Phase, Progress) -> ControlFlow<()>,
) -> Result<Scanned, Error> {
let mut windows = recipe.windows(session.capabilities(), frame)?;
session.focus_frame(frame, Focus::default())?;
let exposures = match options.exposures {
Some(locked) => locked.clone(),
None => {
let lock = options.lock_white_balance;
session
.autoexpose_frame_with(frame, recipe, lock, |pass, p| on(Phase::Meter(pass), p))?
}
};
exposures.apply(&mut windows);
let pass = session.scan_pass_with(&windows, SCAN_TIMEOUT, samples, |p| on(Phase::Scan, p))?;
samples.to_full_scale(pass.layout.bits_per_sample);
let cleaned = options
.clean
.then(|| clean_frame(samples, &pass, session.capabilities().identity.model()))
.transpose()?;
Ok(Scanned {
pass,
exposures,
cleaned,
})
}