use super::Canvas;
use crate::types::{EcLevel, Version};
#[derive(Clone, Copy, Debug)]
pub enum MaskPattern {
Checkerboard = 0b000,
HorizontalLines = 0b001,
VerticalLines = 0b010,
DiagonalLines = 0b011,
LargeCheckerboard = 0b100,
Fields = 0b101,
Diamonds = 0b110,
Meadow = 0b111,
}
mod mask_functions {
pub const fn checkerboard(x: i16, y: i16) -> bool {
(x + y) % 2 == 0
}
pub const fn horizontal_lines(_: i16, y: i16) -> bool {
y % 2 == 0
}
pub const fn vertical_lines(x: i16, _: i16) -> bool {
x % 3 == 0
}
pub const fn diagonal_lines(x: i16, y: i16) -> bool {
(x + y) % 3 == 0
}
pub const fn large_checkerboard(x: i16, y: i16) -> bool {
((y / 2) + (x / 3)) % 2 == 0
}
pub const fn fields(x: i16, y: i16) -> bool {
(x * y) % 2 + (x * y) % 3 == 0
}
pub const fn diamonds(x: i16, y: i16) -> bool {
((x * y) % 2 + (x * y) % 3) % 2 == 0
}
pub const fn meadow(x: i16, y: i16) -> bool {
((x + y) % 2 + (x * y) % 3) % 2 == 0
}
}
fn get_mask_function(pattern: MaskPattern) -> fn(i16, i16) -> bool {
match pattern {
MaskPattern::Checkerboard => mask_functions::checkerboard,
MaskPattern::HorizontalLines => mask_functions::horizontal_lines,
MaskPattern::VerticalLines => mask_functions::vertical_lines,
MaskPattern::DiagonalLines => mask_functions::diagonal_lines,
MaskPattern::LargeCheckerboard => mask_functions::large_checkerboard,
MaskPattern::Fields => mask_functions::fields,
MaskPattern::Diamonds => mask_functions::diamonds,
MaskPattern::Meadow => mask_functions::meadow,
}
}
impl Canvas {
pub fn apply_mask(&mut self, pattern: MaskPattern) {
let mask_fn = get_mask_function(pattern);
for x in 0..self.width {
for y in 0..self.height {
let module = self.get_mut(x, y);
*module = module.mask(mask_fn(x, y));
}
}
self.draw_format_info_patterns(pattern);
}
fn draw_format_info_patterns(&mut self, pattern: MaskPattern) {
if self.version.is_rect_micro() {
return;
}
let format_number = match self.version {
Version::Normal(_) => {
let simple_format_number = ((self.ec_level as usize) ^ 1) << 3 | (pattern as usize);
FORMAT_INFOS_QR[simple_format_number]
}
Version::Micro(a) => {
let micro_pattern_number = match pattern {
MaskPattern::HorizontalLines => 0b00,
MaskPattern::LargeCheckerboard => 0b01,
MaskPattern::Diamonds => 0b10,
MaskPattern::Meadow => 0b11,
_ => panic!("Unsupported mask pattern in Micro QR code"),
};
let symbol_number = match (a, self.ec_level) {
(1, EcLevel::L) => 0b000,
(2, EcLevel::L) => 0b001,
(2, EcLevel::M) => 0b010,
(3, EcLevel::L) => 0b011,
(3, EcLevel::M) => 0b100,
(4, EcLevel::L) => 0b101,
(4, EcLevel::M) => 0b110,
(4, EcLevel::Q) => 0b111,
_ => panic!("Unsupported version/ec_level combination in Micro QR code"),
};
let simple_format_number = symbol_number << 2 | micro_pattern_number;
FORMAT_INFOS_MICRO_QR[simple_format_number]
}
Version::RectMicro(..) => return,
};
self.draw_format_info_patterns_with_number(format_number);
}
}
static FORMAT_INFOS_QR: [u16; 32] = [
0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0, 0x77C4, 0x72F3, 0x7DAA, 0x789D,
0x662F, 0x6318, 0x6C41, 0x6976, 0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x0762, 0x0255, 0x0D0C, 0x083B,
0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED,
];
static FORMAT_INFOS_MICRO_QR: [u16; 32] = [
0x4445, 0x4172, 0x4E2B, 0x4B1C, 0x55AE, 0x5099, 0x5FC0, 0x5AF7, 0x6793, 0x62A4, 0x6DFD, 0x68CA,
0x7678, 0x734F, 0x7C16, 0x7921, 0x06DE, 0x03E9, 0x0CB0, 0x0987, 0x1735, 0x1202, 0x1D5B, 0x186C,
0x2508, 0x203F, 0x2F66, 0x2A51, 0x34E3, 0x31D4, 0x3E8D, 0x3BBA,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_mask_qr() {
let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
c.draw_all_functional_patterns();
c.apply_mask(MaskPattern::Checkerboard);
assert_eq!(
c.to_debug_str(),
concat!(
"\n",
"#######...#.#.#######\n",
"#.....#..#.#..#.....#\n",
"#.###.#.#.#.#.#.###.#\n",
"#.###.#..#.#..#.###.#\n",
"#.###.#...#.#.#.###.#\n",
"#.....#..#.#..#.....#\n",
"#######.#.#.#.#######\n",
"........##.#.........\n",
"###.#####.#.###...#..\n",
".#.#.#.#.#.#.#.#.#.#.\n",
"#.#.#.#.#.#.#.#.#.#.#\n",
".#.#.#.#.#.#.#.#.#.#.\n",
"#.#.#.#.#.#.#.#.#.#.#\n",
"........##.#.#.#.#.#.\n",
"#######.#.#.#.#.#.#.#\n",
"#.....#.##.#.#.#.#.#.\n",
"#.###.#.#.#.#.#.#.#.#\n",
"#.###.#..#.#.#.#.#.#.\n",
"#.###.#.#.#.#.#.#.#.#\n",
"#.....#.##.#.#.#.#.#.\n",
"#######.#.#.#.#.#.#.#"
)
);
}
#[test]
fn test_draw_format_info_patterns_qr() {
let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
c.draw_format_info_patterns(MaskPattern::LargeCheckerboard);
assert_eq!(
c.to_debug_str(),
concat!(
"\n",
"????????#????????????\n",
"????????#????????????\n",
"????????#????????????\n",
"????????#????????????\n",
"????????.????????????\n",
"????????#????????????\n",
"?????????????????????\n",
"????????.????????????\n",
"##..##?..????..#.####\n",
"?????????????????????\n",
"?????????????????????\n",
"?????????????????????\n",
"?????????????????????\n",
"????????#????????????\n",
"????????.????????????\n",
"????????#????????????\n",
"????????#????????????\n",
"????????.????????????\n",
"????????.????????????\n",
"????????#????????????\n",
"????????#????????????"
)
);
}
#[test]
fn test_draw_format_info_patterns_micro_qr() {
let mut c = Canvas::new(Version::Micro(2), EcLevel::L);
c.draw_format_info_patterns(MaskPattern::LargeCheckerboard);
assert_eq!(
c.to_debug_str(),
concat!(
"\n",
"?????????????\n",
"????????#????\n",
"????????.????\n",
"????????.????\n",
"????????#????\n",
"????????#????\n",
"????????.????\n",
"????????.????\n",
"?#.#....#????\n",
"?????????????\n",
"?????????????\n",
"?????????????\n",
"?????????????"
)
);
}
}