use super::layout::Layout;
use super::{AztecMeta, QUIET_ZONE};
use crate::codes::aztec::gf::{Gf, rs_decode, rs_encode};
use crate::error::{Error, Result};
use crate::output::BitMatrix;
use crate::segment::Segment;
use crate::symbol::{Symbol, SymbolMeta};
use crate::symbology::Symbology;
pub(crate) const RUNE_SIZE: usize = 11;
const RUNE_MASK: u16 = 0b1010;
const RUNE_WORDS: usize = 7;
const RUNE_EC_WORDS: usize = 5;
fn rune_codewords(value: u8) -> [u16; RUNE_WORDS] {
let gf = Gf::gf16();
let data = [(value >> 4) as u16, (value & 0x0F) as u16];
let check = rs_encode(&gf, &data, RUNE_EC_WORDS);
let mut words = [0u16; RUNE_WORDS];
words[0] = data[0];
words[1] = data[1];
for (slot, &c) in words[2..].iter_mut().zip(check.iter()) {
*slot = c;
}
for w in &mut words {
*w ^= RUNE_MASK;
}
words
}
pub(super) fn render_rune(value: u8) -> BitMatrix {
let layout = Layout::new(true, 0);
let mut m = BitMatrix::new(layout.size, layout.size, QUIET_ZONE);
layout.draw_fixed(&mut m);
m.set(1, 0, true);
m.set(10, 1, true);
m.set(10, 9, true);
m.set(0, 10, false);
let words = rune_codewords(value);
let mut bits = Vec::with_capacity(RUNE_WORDS * 4);
for &w in &words {
for k in (0..4).rev() {
bits.push((w >> k) & 1 != 0);
}
}
for (bit, (x, y)) in bits.iter().zip(layout.mode_positions()) {
if *bit {
m.set(x, y, true);
}
}
m
}
pub(super) fn decode_rune(m: &BitMatrix) -> Result<u8> {
if m.width() != RUNE_SIZE || m.height() != RUNE_SIZE {
return Err(Error::undecodable("Aztec Rune grid is not 11×11"));
}
let layout = Layout::new(true, 0);
let bits: Vec<bool> = layout
.mode_positions()
.iter()
.map(|&(x, y)| m.get(x, y))
.collect();
let mut words: Vec<u16> = bits
.chunks(4)
.map(|c| c.iter().fold(0u16, |a, &b| (a << 1) | b as u16) ^ RUNE_MASK)
.collect();
debug_assert_eq!(words.len(), RUNE_WORDS);
let gf = Gf::gf16();
if !rs_decode(&gf, &mut words, RUNE_EC_WORDS) {
return Err(Error::ErrorCorrectionFailed);
}
Ok((((words[0] & 0x0F) << 4) | (words[1] & 0x0F)) as u8)
}
pub(super) fn rune_meta() -> AztecMeta {
AztecMeta {
compact: true,
layers: 0,
rune: true,
}
}
pub(super) fn rune_symbol(value: u8) -> Symbol {
Symbol::new(
Symbology::AztecRunes,
vec![Segment::byte(vec![value])],
SymbolMeta::Aztec(rune_meta()),
)
}