use crate::config::DecoderConfig;
use crate::image::Image;
use crate::img_scanner::ImageScanner;
use crate::symbol::Symbol;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FinderRegion {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
pub struct ScanResult {
symbols: Vec<Symbol>,
finder_region: Option<FinderRegion>,
}
impl ScanResult {
pub(crate) fn new(symbols: Vec<Symbol>, finder_region: Option<FinderRegion>) -> Self {
Self {
symbols,
finder_region,
}
}
pub fn symbols(&self) -> &[Symbol] {
&self.symbols
}
pub fn into_symbols(self) -> Vec<Symbol> {
self.symbols
}
pub fn finder_region(&self) -> Option<&FinderRegion> {
self.finder_region.as_ref()
}
}
impl IntoIterator for ScanResult {
type Item = Symbol;
type IntoIter = std::vec::IntoIter<Symbol>;
fn into_iter(self) -> Self::IntoIter {
self.symbols.into_iter()
}
}
impl<'a> IntoIterator for &'a ScanResult {
type Item = &'a Symbol;
type IntoIter = std::slice::Iter<'a, Symbol>;
fn into_iter(self) -> Self::IntoIter {
self.symbols.iter()
}
}
impl std::ops::Deref for ScanResult {
type Target = [Symbol];
fn deref(&self) -> &[Symbol] {
&self.symbols
}
}
pub struct Scanner {
scanner: ImageScanner,
retry_undecoded_regions: bool,
}
impl Scanner {
pub fn new() -> Self {
Self {
scanner: ImageScanner::default(),
retry_undecoded_regions: false,
}
}
pub fn with_config(config: DecoderConfig) -> Self {
let retry = config.retry_undecoded_regions;
Self {
scanner: ImageScanner::with_config(config),
retry_undecoded_regions: retry,
}
}
pub fn scan(&mut self, image: &mut Image) -> ScanResult {
let (symbols, raw_region) = self.scanner.scan_image(image.as_mut_image());
let finder_region = raw_region.map(|(x, y, w, h)| FinderRegion {
x,
y,
width: w,
height: h,
});
if !self.retry_undecoded_regions {
return ScanResult::new(symbols, finder_region);
}
let Some(finder_region) = finder_region else {
return ScanResult::new(symbols, finder_region);
};
const SCALES: &[u32] = &[2, 4, 6];
let apply_area_filter = image.width() >= 200 && image.height() >= 200;
if apply_area_filter {
let image_area = image.width() as u64 * image.height() as u64;
let region_area = finder_region.width as u64 * finder_region.height as u64;
if region_area > image_area / 10 {
return ScanResult::new(symbols, Some(finder_region));
}
}
let pad_x = finder_region.width / 2;
let pad_y = finder_region.height / 2;
let cx = finder_region.x.saturating_sub(pad_x);
let cy = finder_region.y.saturating_sub(pad_y);
let cw = (finder_region.width + 2 * pad_x).min(image.width().saturating_sub(cx));
let ch = (finder_region.height + 2 * pad_y).min(image.height().saturating_sub(cy));
let cropped = match image.crop(cx, cy, cw, ch) {
Some(c) => c,
None => return ScanResult::new(symbols, Some(finder_region)),
};
for &scale in SCALES {
if let Some(mut upscaled) = cropped.upscale(scale) {
let (mut retry_symbols, _) = self.scanner.scan_image(upscaled.as_mut_image());
if !retry_symbols.is_empty() {
let half = scale as i32 / 2;
for sym in &mut retry_symbols {
for pt in &mut sym.pts {
pt[0] = (pt[0] + half) / scale as i32 + cx as i32;
pt[1] = (pt[1] + half) / scale as i32 + cy as i32;
}
}
let mut all = symbols;
for sym in retry_symbols {
if !all
.iter()
.any(|s| s.symbol_type() == sym.symbol_type() && s.data == sym.data)
{
all.push(sym);
}
}
return ScanResult::new(all, None);
}
}
}
ScanResult::new(symbols, Some(finder_region))
}
}
impl Default for Scanner {
fn default() -> Self {
Self::new()
}
}