use std::path::PathBuf; use std::time::Instant; use quircs::*; #[derive(Debug, Clone)] struct ResultInfo { file_count: usize, id_count: usize, decode_count: usize, load_time: u128, identify_time: u128, total_time: u128, } #[derive(Default)] struct Opts { verbose: bool, cell_dump: bool, } fn print_result(name: &str, info: &mut ResultInfo) { print!("-------------------------------------------------------------------------------"); print!( "{}: {} files, {} codes, {} decoded ({} failures)", name, info.file_count, info.id_count, info.decode_count, info.id_count - info.decode_count, ); if info.id_count != 0 { print!( ", {}% success rate", (info.decode_count * 100 + info.id_count / 2) / info.id_count, ); } println!(); println!( "Total time [load: {}, identify: {}, total: {}]", info.load_time, info.identify_time, info.total_time, ); if info.file_count != 0 { println!( "Average time [load: {}, identify: {}, total: {}]", info.load_time.wrapping_div(info.file_count as u128), info.identify_time.wrapping_div(info.file_count as u128), info.total_time.wrapping_div(info.file_count as u128), ); } } fn add_result(sum: &mut ResultInfo, inf: &mut ResultInfo) { sum.file_count += inf.file_count; sum.id_count += inf.id_count; sum.decode_count += inf.decode_count; sum.load_time = sum.load_time.wrapping_add(inf.load_time); sum.identify_time = sum.identify_time.wrapping_add(inf.identify_time); sum.total_time = sum.total_time.wrapping_add(inf.total_time); } fn scan_file(decoder: &mut Quirc, opts: &Opts, path: &str, info: &mut ResultInfo) -> i32 { let path = PathBuf::from(path); let start = Instant::now(); let total_start = start; let img = image::open(&path) .expect("failed to open image") .into_luma8(); info.load_time = start.elapsed().as_millis(); let start = Instant::now(); let res: Vec<_> = decoder .identify(img.width() as usize, img.height() as usize, &img) .collect::>() .unwrap(); info.identify_time = start.elapsed().as_millis(); info.id_count = decoder.count(); for code in &res { if code.decode().is_ok() { info.decode_count += 1 } } info.total_time = total_start.elapsed().as_millis(); println!( " {:<30} {:<5} {:<5} {:<5} {:<5} {:<5}", path.file_name().unwrap().to_string_lossy(), info.load_time, info.identify_time, info.total_time, info.id_count, info.decode_count, ); if opts.cell_dump || opts.verbose { for code in &res { if opts.cell_dump { dump_cells(code); println!(); } if opts.verbose { match code.decode() { Ok(data) => { println!("\n Decode successful:"); dump_data(&data); println!(); } Err(err) => { println!(" ERROR: {err}\n"); } } } } } info.file_count = 1; 1 } fn test_scan(decoder: &mut Quirc, opts: &Opts, path: &str, info: &mut ResultInfo) -> i32 { scan_file(decoder, opts, path, info) } fn run_tests(opts: &Opts, paths: &[String]) -> i32 { let mut sum = ResultInfo { file_count: 0, id_count: 0, decode_count: 0, load_time: 0, identify_time: 0, total_time: 0, }; let mut count: i32 = 0; let mut decoder = Quirc::new(); println!(" {:30} {:^17} {:^11}", "", "Time (ms)", "Count"); println!( " {:30} {:5} {:5} {:5} {:5} {:5}", "Filename", "Load", "ID", "Total", "ID", "Dec", ); println!("-------------------------------------------------------------------------------"); for path in paths { let mut info: ResultInfo = ResultInfo { file_count: 0, id_count: 0, decode_count: 0, load_time: 0, identify_time: 0, total_time: 0, }; if test_scan(&mut decoder, opts, path, &mut info) > 0 { add_result(&mut sum, &mut info); count += 1 } } if count > 1 { print_result("TOTAL", &mut sum); } 0 } fn dump_data(data: &Data) { println!(" Version: {}", data.version); println!(" ECC level: {:?}", data.ecc_level); println!(" Mask: {}", data.mask); println!(" Data type: {:?}", data.data_type); println!(" Length: {}", data.payload.len()); println!(" Payload: {:?}", std::str::from_utf8(&data.payload)); println!(" ECI: {:?}", data.eci); } fn dump_cells(code: &Code) { let code = *code; print!(" {} cells, corners:", code.size); for u in 0..4 { print!(" ({},{})", code.corners[u].x, code.corners[u].y); } println!(); for v in 0..code.size { print!(" "); for u in 0..code.size { let p = v * code.size + u; if (code.cell_bitmap[(p >> 3) as usize] & (1 << (p & 7))) != 0 { print!("[]"); } else { print!(" "); } } println!(); } } fn main() { let args: Vec = std::env::args().skip(1).collect(); println!("quircs test program"); println!("Library version: {}\n", version()); let opts = Opts { verbose: true, cell_dump: false, }; let res = run_tests(&opts, &args); std::process::exit(res); }