use crate::{mode_encoder::Mode, reed::BlockSpec};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcLevel {
L = 0, M = 1, Q = 2, H = 3, }
impl EcLevel {
pub fn format_bits(&self) -> u32 {
match self {
EcLevel::L => 0b01,
EcLevel::M => 0b00,
EcLevel::Q => 0b11,
EcLevel::H => 0b10,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct QrSpec {
pub version: usize,
pub ec_level: EcLevel,
pub total_codewords: usize, pub data_codewords: usize, pub ec_codewords: usize, pub num_blocks: usize, }
impl QrSpec {
pub fn matrix_dim(&self) -> usize {
17 + 4 * self.version
}
pub fn char_count_bits(&self, mode: Mode) -> usize {
match (mode, self.version) {
(Mode::Numeric, 1..=9) => 10,
(Mode::Alphanumeric, 1..=9) => 9,
(Mode::Byte, 1..=9) => 8,
(Mode::Numeric, 10..=26) => 12,
(Mode::Alphanumeric, 10..=26) => 11,
(Mode::Byte, 10..=26) => 16,
(Mode::Numeric, 27..=40) => 14,
(Mode::Alphanumeric, 27..=40) => 13,
(Mode::Byte, 27..=40) => 16,
_ => unreachable!(),
}
}
pub fn to_block_spec(&self) -> BlockSpec {
let ec_codewords_per_block = self.ec_codewords / self.num_blocks;
let base_data = self.data_codewords / self.num_blocks;
let remainder = self.data_codewords % self.num_blocks;
if remainder == 0 {
BlockSpec {
total_data_codewords: self.data_codewords,
ec_codewords_per_block,
g1_blocks: self.num_blocks,
g1_data_per_block: base_data,
g2_blocks: 0,
g2_data_per_block: 0,
}
} else {
let g2_blocks = remainder;
let g1_blocks = self.num_blocks - g2_blocks;
BlockSpec {
total_data_codewords: self.data_codewords,
ec_codewords_per_block,
g1_blocks,
g1_data_per_block: base_data,
g2_blocks,
g2_data_per_block: base_data + 1,
}
}
}
}
pub fn get_qr_spec(version: usize, ec_level: EcLevel) -> QrSpec {
let (total_codewords, data_codewords, num_blocks) = match (version, ec_level) {
(1, EcLevel::L) => (26, 19, 1),
(1, EcLevel::M) => (26, 16, 1),
(1, EcLevel::Q) => (26, 13, 1),
(1, EcLevel::H) => (26, 9, 1),
(2, EcLevel::L) => (44, 34, 1),
(2, EcLevel::M) => (44, 28, 1),
(2, EcLevel::Q) => (44, 22, 1),
(2, EcLevel::H) => (44, 16, 1),
(3, EcLevel::L) => (70, 55, 1),
(3, EcLevel::M) => (70, 44, 1),
(3, EcLevel::Q) => (70, 34, 2),
(3, EcLevel::H) => (70, 26, 2),
(4, EcLevel::L) => (100, 80, 1),
(4, EcLevel::M) => (100, 64, 2),
(4, EcLevel::Q) => (100, 48, 2),
(4, EcLevel::H) => (100, 36, 4),
(5, EcLevel::L) => (134, 108, 1),
(5, EcLevel::M) => (134, 86, 2),
(5, EcLevel::Q) => (134, 62, 4),
(5, EcLevel::H) => (134, 46, 4),
(6, EcLevel::L) => (172, 136, 2),
(6, EcLevel::M) => (172, 108, 4),
(6, EcLevel::Q) => (172, 76, 4),
(6, EcLevel::H) => (172, 60, 4),
(7, EcLevel::L) => (196, 156, 2),
(7, EcLevel::M) => (196, 124, 4),
(7, EcLevel::Q) => (196, 88, 6),
(7, EcLevel::H) => (196, 66, 5),
(8, EcLevel::L) => (230, 184, 2),
(8, EcLevel::M) => (230, 144, 6),
(8, EcLevel::Q) => (230, 110, 6),
(8, EcLevel::H) => (230, 86, 6),
(9, EcLevel::L) => (271, 216, 2),
(9, EcLevel::M) => (271, 168, 8),
(9, EcLevel::Q) => (271, 132, 8),
(9, EcLevel::H) => (271, 100, 8),
(10, EcLevel::L) => (321, 256, 4),
(10, EcLevel::M) => (321, 200, 8),
(10, EcLevel::Q) => (321, 154, 8),
(10, EcLevel::H) => (321, 122, 10),
_ => todo!("Add entries for higher versions when needed"),
};
let ec_codewords = total_codewords - data_codewords;
QrSpec {
version,
ec_level,
total_codewords,
data_codewords,
ec_codewords,
num_blocks,
}
}
pub fn get_alignment_coords(version: usize) -> Vec<usize> {
match version {
2 => vec![6, 18],
3 => vec![6, 22],
4 => vec![6, 26],
5 => vec![6, 30],
6 => vec![6, 34],
7 => vec![6, 22, 38],
8 => vec![6, 24, 42],
9 => vec![6, 26, 46],
10 => vec![6, 28, 50],
_ => vec![],
}
}
pub fn ec_level_format_bits(ec_level: &str) -> u16 {
match ec_level {
"L" => 0b01,
"M" => 0b00,
"Q" => 0b11,
"H" => 0b10,
_ => 0b00,
}
}
pub fn is_mask_bit_set(mask: u8, r: usize, c: usize) -> bool {
let r = r as isize;
let c = c as isize;
match mask {
0 => (r+c) % 2 == 0,
1 => r % 2 == 0,
2 => c % 3 == 0,
3 => (r+c) % 3 == 0,
4 => ((r/2) + (c/3)) % 2 == 0,
5 => ((r*c) % 2) + ((r*c) % 3) == 0,
6 => (((r*c) % 2) + ((r*c) % 3)) % 2 == 0,
7 => (((r+c) % 2) + ((r*c) % 3)) % 2 == 0,
_ => false,
}
}