use crate::{SymbolType, color::Color, img_scanner::ImageScanner};
const NIBUF: usize = 6;
macro_rules! zassert {
($condition:expr, $retval:expr, $($arg:tt)*) => {
if !$condition {
return $retval;
}
};
}
static CODABAR_LO: [i8; 12] = [0x0, 0x1, 0x4, 0x5, 0x2, 0xa, 0xb, 0x9, 0x6, 0x7, 0x8, 0x3];
static CODABAR_HI: [u8; 8] = [0x1, 0x4, 0x7, 0x6, 0x2, 0x3, 0x0, 0x5];
static CODABAR_CHARACTERS: &[u8; 20] = b"0123456789-$:/.+ABCD";
fn decode_sort3(dcode: &ImageScanner, i0: u8) -> u32 {
let w0 = dcode.get_width(i0);
let w2 = dcode.get_width(i0 + 2);
let w4 = dcode.get_width(i0 + 4);
if w0 < w2 {
if w2 < w4 {
((i0 as u32) << 8) | (((i0 + 2) as u32) << 4) | ((i0 + 4) as u32)
} else if w0 < w4 {
((i0 as u32) << 8) | (((i0 + 4) as u32) << 4) | ((i0 + 2) as u32)
} else {
(((i0 + 4) as u32) << 8) | ((i0 as u32) << 4) | ((i0 + 2) as u32)
}
} else if w4 < w2 {
(((i0 + 4) as u32) << 8) | (((i0 + 2) as u32) << 4) | (i0 as u32)
} else if w0 < w4 {
(((i0 + 2) as u32) << 8) | ((i0 as u32) << 4) | ((i0 + 4) as u32)
} else {
(((i0 + 2) as u32) << 8) | (((i0 + 4) as u32) << 4) | (i0 as u32)
}
}
fn decode_sortn(dcode: &ImageScanner, n: i32, i0: u8) -> u32 {
let mut mask: u32 = 0;
let mut sort: u32 = 0;
for _i in (0..n).rev() {
let mut wmin = u32::MAX;
let mut jmin: i32 = -1;
for j in (0..n).rev() {
if (mask >> j) & 1 != 0 {
continue;
}
let w = dcode.get_width(i0 + (j as u8) * 2);
if wmin >= w {
wmin = w;
jmin = j;
}
}
zassert!(jmin >= 0, 0, "sortn({},{}) jmin={}", n, i0, jmin);
sort <<= 4;
mask |= 1 << jmin;
sort |= (i0 as u32) + (jmin as u32) * 2;
}
sort
}
fn check_width(ref_width: u32, w: u32) -> bool {
let dref = ref_width;
let ref_4 = ref_width * 4;
let w_4 = w * 4;
ref_4.wrapping_sub(dref) <= w_4 && w_4 <= ref_4.wrapping_add(dref)
}
fn codabar_decode7(dcode: &ImageScanner) -> i8 {
let codabar = &dcode.codabar;
let s = codabar.s7;
if s < 7 {
return -1;
}
if !check_width(codabar.width, s) {
return -1;
}
let ibar = decode_sortn(dcode, 4, 1);
let wbmax = dcode.get_width((ibar & 0xf) as u8);
let wbmin = dcode.get_width((ibar >> 12) as u8);
if 8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax {
return -1;
}
let wb1 = dcode.get_width(((ibar >> 8) & 0xf) as u8);
let wb2 = dcode.get_width(((ibar >> 4) & 0xf) as u8);
let b0b3 = wbmin * wbmax;
let b1b2 = wb1 * wb2;
let ibar = if b1b2 + b1b2 / 8 < b0b3 {
if 8 * wbmin < 5 * wb1
|| 8 * wb1 < 5 * wb2
|| 4 * wb2 > 3 * wbmax
|| wb2 * wb2 >= wb1 * wbmax
{
return -1;
}
(ibar >> 1) & 0x3
} else if b1b2 > b0b3 + b0b3 / 8 {
if 4 * wbmin > 3 * wb1
|| 8 * wb1 < 5 * wb2
|| 8 * wb2 < 5 * wbmax
|| wbmin * wb2 >= wb1 * wb1
{
return -1;
}
(ibar >> 13) + 4
} else {
return -1;
};
let ispc = decode_sort3(dcode, 2);
let wsmax = dcode.get_width((ispc & 0xf) as u8);
let wsmid = dcode.get_width(((ispc >> 4) & 0xf) as u8);
let wsmin = dcode.get_width(((ispc >> 8) & 0xf) as u8);
if ibar >> 2 != 0 {
if 8 * wsmin < wsmax || 8 * wsmin < 5 * wsmid || 8 * wsmid < 5 * wsmax {
return -1;
}
let mut ibar = ibar & 0x3;
if codabar.direction() {
ibar = 3 - ibar;
}
let c = (0xfcde >> (ibar << 2)) & 0xf;
return c as i8;
}
if 8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax {
return -1;
}
let s0s2 = wsmin * wsmax;
let s1s1 = wsmid * wsmid;
if s1s1 + s1s1 / 8 < s0s2 {
if 8 * wsmin < 5 * wsmid || 4 * wsmid > 3 * wsmax {
return -1;
}
let ispc = (((ispc & 0xf) as u8) >> 1) - 1;
let mut ic = ((ispc as u32) << 2) | ibar;
if codabar.direction() {
ic = 11 - ic;
}
CODABAR_LO[ic as usize]
} else if s1s1 > s0s2 + s0s2 / 8 {
if 4 * wsmin > 3 * wsmid || 8 * wsmid < 5 * wsmax {
return -1;
}
if (ispc >> 8) == 4 {
return -1;
}
let ispc = ispc >> 10;
let ic = ispc * 4 + ibar;
zassert!(ic < 8, -1, "ic={} ispc={} ibar={}", ic, ispc, ibar);
let c = CODABAR_HI[ic as usize];
if (c >> 2) != (codabar.direction() as u8) {
return -1;
}
let c = (c & 0x3) | 0x10;
c as i8
} else {
-1
}
}
fn codabar_decode_start(dcode: &mut ImageScanner) -> SymbolType {
let s = dcode.codabar.s7;
if s < 8 {
return SymbolType::None;
}
let qz = dcode.get_width(8);
if (qz != 0 && qz * 2 < s) || 4 * dcode.get_width(0) > 3 * s {
return SymbolType::None;
}
let ispc = decode_sort3(dcode, 2);
if (ispc >> 8) == 4 {
return SymbolType::None;
}
let wsmax = dcode.get_width((ispc & 0xf) as u8);
let wsmin = dcode.get_width((ispc >> 8) as u8);
let wsmid = dcode.get_width(((ispc >> 4) & 0xf) as u8);
if 8 * wsmin < wsmax
|| 3 * wsmin > 2 * wsmax
|| 4 * wsmin > 3 * wsmid
|| 8 * wsmid < 5 * wsmax
|| wsmid * wsmid <= wsmax * wsmin
{
return SymbolType::None;
}
let ispc = ispc >> 10;
let ibar = decode_sortn(dcode, 4, 1);
let wbmax = dcode.get_width((ibar & 0xf) as u8);
let wbmin = dcode.get_width((ibar >> 12) as u8);
if 8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax {
return SymbolType::None;
}
let wb1 = dcode.get_width(((ibar >> 8) & 0xf) as u8);
let wb2 = dcode.get_width(((ibar >> 4) & 0xf) as u8);
if 8 * wbmin < 5 * wb1
|| 8 * wb1 < 5 * wb2
|| 4 * wb2 > 3 * wbmax
|| wb1 * wb2 >= wbmin * wbmax
|| wb2 * wb2 >= wb1 * wbmax
{
return SymbolType::None;
}
let ibar = (((ibar & 0xf) as u8) - 1) >> 1;
let ic = ispc * 4 + (ibar as u32);
zassert!(
ic < 8,
SymbolType::None,
"ic={} ispc={} ibar={}",
ic,
ispc,
ibar
);
let c = CODABAR_HI[ic as usize];
dcode.codabar.buf[0] = (c & 0x3) | 0x10;
dcode.codabar.set_direction((c >> 2) != 0);
dcode.codabar.set_element(4);
dcode.codabar.set_character(1);
dcode.codabar.width = dcode.codabar.s7;
SymbolType::Partial
}
fn codabar_postprocess(dcode: &mut ImageScanner) -> SymbolType {
let dir = dcode.codabar.direction();
dcode.direction = 1 - 2 * (dir as i32);
let mut n = dcode.codabar.character() as usize;
let copy_len = n.min(NIBUF);
let temp_buf = dcode.codabar.buf; let has_checksum = dcode.should_validate_checksum(SymbolType::Codabar);
let emit_check = dcode.should_emit_checksum(SymbolType::Codabar);
let buffer = match dcode.buffer_mut_slice(n + 1) {
Ok(buf) => buf,
Err(_) => return SymbolType::None,
};
buffer[..copy_len].copy_from_slice(&temp_buf[..copy_len]);
if dir {
buffer[..n].reverse();
}
if has_checksum {
let mut chk: u32 = 0;
for c in buffer.iter().take(n) {
chk += *c as u32;
}
if (chk & 0xf) != 0 {
return SymbolType::None;
}
if !emit_check {
buffer[n - 2] = buffer[n - 1];
n -= 1;
}
}
for c in buffer.iter_mut().take(n) {
*c = *CODABAR_CHARACTERS.get(*c as usize).unwrap_or(&b'?');
}
buffer[n] = 0;
dcode.truncate_buffer(n);
dcode.modifiers = 0;
dcode.codabar.set_character(-1);
SymbolType::Codabar
}
pub(crate) fn decode_codabar(dcode: &mut ImageScanner) -> SymbolType {
let w8 = dcode.get_width(8);
let w1 = dcode.get_width(1);
dcode.codabar.s7 = dcode.codabar.s7.wrapping_sub(w8).wrapping_add(w1);
if dcode.color() != Color::Space {
return SymbolType::None;
}
if dcode.codabar.character() < 0 {
return codabar_decode_start(dcode);
}
if dcode.codabar.character() < 2 && codabar_decode_start(dcode) != SymbolType::None {
return SymbolType::Partial;
}
let element = dcode.codabar.element() - 1;
dcode.codabar.set_element(element);
if element != 0 {
return SymbolType::None;
}
dcode.codabar.set_element(4);
let c = codabar_decode7(dcode);
if c < 0 {
let character = dcode.codabar.character();
if character >= NIBUF as i16 {
dcode.release_lock(SymbolType::Codabar);
}
dcode.codabar.set_character(-1);
return SymbolType::None;
}
let character = dcode.codabar.character();
if character < NIBUF as i16 {
dcode.codabar.buf[character as usize] = c as u8;
} else {
if dcode
.write_buffer_byte(character as usize, c as u8)
.is_err()
{
dcode.release_lock(SymbolType::Codabar);
dcode.codabar.set_character(-1);
return SymbolType::None;
}
}
dcode.codabar.set_character(character + 1);
if character + 1 == NIBUF as i16 && !dcode.acquire_lock(SymbolType::Codabar) {
dcode.codabar.set_character(-1);
return SymbolType::Partial;
}
let s = dcode.codabar.s7;
if (c & 0x10) != 0 {
let qz = dcode.get_width(0);
if qz != 0 && qz * 2 < s {
let character = dcode.codabar.character();
if character >= NIBUF as i16 {
dcode.release_lock(SymbolType::Codabar);
}
dcode.codabar.set_character(-1);
return SymbolType::None;
}
let n = dcode.codabar.character();
let (min_len, max_len) = dcode
.get_length_limits(SymbolType::Codabar)
.unwrap_or((4, 0)); if n < min_len as i16 || (max_len > 0 && n > max_len as i16) {
let character = dcode.codabar.character();
if character >= NIBUF as i16 {
dcode.release_lock(SymbolType::Codabar);
}
dcode.codabar.set_character(-1);
return SymbolType::None;
}
if dcode.codabar.character() < NIBUF as i16 && !dcode.acquire_lock(SymbolType::Codabar) {
dcode.codabar.set_character(-1);
return SymbolType::Partial;
}
let sym = codabar_postprocess(dcode);
if sym <= SymbolType::Partial {
dcode.release_lock(SymbolType::Codabar);
dcode.codabar.set_character(-1);
}
return sym;
} else if 4 * dcode.get_width(0) > 3 * s {
let character = dcode.codabar.character();
if character >= NIBUF as i16 {
dcode.release_lock(SymbolType::Codabar);
}
dcode.codabar.set_character(-1);
return SymbolType::None;
}
SymbolType::None
}