use wasm_bindgen::prelude::*;
use crate::config::DecoderConfig;
use crate::image::Image;
use crate::scanner::Scanner;
#[cfg(feature = "image")]
use image;
#[wasm_bindgen]
#[derive(Default)]
pub struct ScanOptions {
retry_undecoded_regions: Option<bool>,
}
#[wasm_bindgen]
impl ScanOptions {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
retry_undecoded_regions: None,
}
}
#[wasm_bindgen(setter, js_name = "retryUndecodedRegions")]
pub fn set_retry_undecoded_regions(&mut self, value: bool) {
self.retry_undecoded_regions = Some(value);
}
}
impl ScanOptions {
const DEFAULT_RETRY: bool = true;
fn retry(&self) -> bool {
self.retry_undecoded_regions.unwrap_or(Self::DEFAULT_RETRY)
}
}
#[wasm_bindgen]
pub struct DecodeResult {
symbol_type: String,
data: Vec<u8>,
text: Option<String>,
}
#[wasm_bindgen]
impl DecodeResult {
#[wasm_bindgen(getter, js_name = "symbolType")]
pub fn symbol_type(&self) -> String {
self.symbol_type.clone()
}
#[wasm_bindgen(getter)]
pub fn data(&self) -> Vec<u8> {
self.data.clone()
}
#[wasm_bindgen(getter)]
pub fn text(&self) -> Option<String> {
self.text.clone()
}
}
fn build_scanner(options: Option<ScanOptions>) -> Scanner {
let options = options.unwrap_or_default();
let config = DecoderConfig::new().retry_undecoded_regions(options.retry());
Scanner::with_config(config)
}
#[wasm_bindgen(js_name = "scanGrayscale")]
pub fn scan_grayscale(
data: &[u8],
width: u32,
height: u32,
options: Option<ScanOptions>,
) -> Result<Vec<DecodeResult>, JsValue> {
let mut image =
Image::from_gray(data, width, height).map_err(|e| JsValue::from_str(&format!("{e:?}")))?;
let mut scanner = build_scanner(options);
let symbols = scanner.scan(&mut image);
Ok(symbols
.into_iter()
.map(|s| DecodeResult {
symbol_type: s.symbol_type().to_string(),
data: s.raw_data().unwrap_or(s.data()).to_vec(),
text: s.data_string().map(|t| t.to_string()),
})
.collect())
}
#[wasm_bindgen(js_name = "scanImageBytes")]
pub fn scan_image_bytes(
bytes: &[u8],
options: Option<ScanOptions>,
) -> Result<Vec<DecodeResult>, JsValue> {
let img = image::load_from_memory(bytes)
.map_err(|e| JsValue::from_str(&format!("Failed to decode image: {}", e)))?;
let gray = img.to_luma8();
let (width, height) = gray.dimensions();
let data = gray.as_raw();
scan_grayscale(data, width, height, options)
}