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::<Result<_, _>>()
.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<String> = 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);
}