From 4ed49e10ff5eef773964bfe347f3ec3a8da8b1a3 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 12:53:04 -0600 Subject: [PATCH 01/15] feat: ported qr-model1 support from zxing-cpp --- src/barcode_format.rs | 6 + .../base_extentions/qr_formatinformation.rs | 26 +- .../base_extentions/qrcode_version.rs | 28 +- src/common/cpp_essentials/decoder_result.rs | 7 + src/multi_format_reader.rs | 3 + src/qrcode/cpp_port/bitmatrix_parser.rs | 109 +++++++- src/qrcode/cpp_port/decoder.rs | 18 +- .../test/QRDecodedBitStreamParserTest.rs | 12 +- .../cpp_port/test/QRFormatInformationTest.rs | 2 +- src/qrcode/cpp_port/test/QRModeTest.rs | 39 ++- src/qrcode/cpp_port/test/QRVersionTest.rs | 15 +- src/qrcode/decoder/format_information.rs | 3 + src/qrcode/decoder/version.rs | 247 ++++++++++++++++++ .../cpp/qrcode-2/qr-model-1.metadata.txt | 2 + .../blackbox/cpp/qrcode-2/qr-model-1.png | Bin 0 -> 230 bytes .../blackbox/cpp/qrcode-2/qr-model-1.txt | 1 + tests/cpp_qr_code_blackbox_tests.rs | 8 +- 17 files changed, 485 insertions(+), 41 deletions(-) create mode 100644 test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt create mode 100644 test_resources/blackbox/cpp/qrcode-2/qr-model-1.png create mode 100644 test_resources/blackbox/cpp/qrcode-2/qr-model-1.txt diff --git a/src/barcode_format.rs b/src/barcode_format.rs index 2946991..70fd962 100644 --- a/src/barcode_format.rs +++ b/src/barcode_format.rs @@ -67,6 +67,8 @@ pub enum BarcodeFormat { MICRO_QR_CODE, + RECTANGULAR_MICRO_QR_CODE, + /** RSS 14 */ RSS_14, @@ -108,6 +110,7 @@ impl Display for BarcodeFormat { BarcodeFormat::PDF_417 => "pdf 417", BarcodeFormat::QR_CODE => "qrcode", BarcodeFormat::MICRO_QR_CODE => "mqr", + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE => "rmqr", BarcodeFormat::RSS_14 => "rss 14", BarcodeFormat::RSS_EXPANDED => "rss expanded", BarcodeFormat::TELEPEN => "telepen", @@ -150,6 +153,9 @@ impl From<&str> for BarcodeFormat { "mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => { BarcodeFormat::MICRO_QR_CODE } + "rmqr" | "rectangular_mqr" | "rectangular_micro_qr" | "rmqr_code" => { + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE + } "rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon" | "gs1_databar_coupon" => BarcodeFormat::RSS_14, "rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED, diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 398a54b..30919e2 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -2,6 +2,8 @@ use crate::qrcode::decoder::{ ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, FORMAT_INFO_MASK_QR, }; +pub const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825; + pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [[u32; 2]; 32] = [ [0x4445, 0x00], [0x4172, 0x01], @@ -50,7 +52,7 @@ impl FormatInformation { let formatInfoBits2 = ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); let mut fi = Self::FindBestFormatInfo( - FORMAT_INFO_MASK_QR, + &[0, FORMAT_INFO_MASK_QR], FORMAT_INFO_DECODE_LOOKUP, &[ formatInfoBits1, @@ -60,6 +62,22 @@ impl FormatInformation { ], ); + let mut fi_model1 = Self::FindBestFormatInfo( + &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], + FORMAT_INFO_DECODE_LOOKUP, + &[ + formatInfoBits1, + formatInfoBits2, + Self::MirrorBits(formatInfoBits1), + mirroredFormatInfoBits2, + ], + ); + + if fi_model1.hammingDistance < fi.hammingDistance { + fi_model1.isModel1 = true; + fi = fi_model1; + } + // Use bits 3/4 for error correction, and 0-2 for mask. fi.error_correction_level = ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false); @@ -72,7 +90,7 @@ impl FormatInformation { pub fn DecodeMQR(formatInfoBits: u32) -> Self { // We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders let mut fi = Self::FindBestFormatInfo( - 0, + &[0], FORMAT_INFO_DECODE_LOOKUP_MICRO, &[formatInfoBits, Self::MirrorBits(formatInfoBits)], ); @@ -94,11 +112,11 @@ impl FormatInformation { (bits.reverse_bits()) >> 17 } - pub fn FindBestFormatInfo(mask: u32, lookup: [[u32; 2]; 32], bits: &[u32]) -> Self { + pub fn FindBestFormatInfo(masks: &[u32], lookup: [[u32; 2]; 32], bits: &[u32]) -> Self { let mut fi = FormatInformation::default(); // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. - for mask in [0, mask] { + for mask in masks { // for (auto mask : {0, mask}) for (bitsIndex, bit_set) in bits.iter().enumerate() { // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index 892352f..d11cc56 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -1,5 +1,7 @@ use crate::common::Result; -use crate::qrcode::decoder::{Version, VersionRef, MICRO_VERSIONS, VERSIONS, VERSION_DECODE_INFO}; +use crate::qrcode::decoder::{ + Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, VERSIONS, VERSION_DECODE_INFO, +}; use crate::Exceptions; // const Version* Version::AllMicroVersions() @@ -16,7 +18,7 @@ use crate::Exceptions; // } impl Version { - pub fn FromDimension(dimension: u32) -> Result { + pub fn FromDimension(dimension: u32, is_model1: bool) -> Result { let isMicro = dimension < 21; if dimension % Self::DimensionStep(isMicro) != 1 { //throw std::invalid_argument("Unexpected dimension"); @@ -25,17 +27,31 @@ impl Version { Self::FromNumber( (dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro), isMicro, + is_model1, ) } - pub fn FromNumber(versionNumber: u32, is_micro: bool) -> Result { - if versionNumber < 1 || versionNumber > (if is_micro { 4 } else { 40 }) { + pub fn FromNumber(versionNumber: u32, is_micro: bool, is_model1: bool) -> Result { + if versionNumber < 1 + || versionNumber + > (if is_micro { + 4 + } else { + if is_model1 { + 14 + } else { + 40 + } + }) + { //throw std::invalid_argument("Version should be in range [1-40]."); return Err(Exceptions::ILLEGAL_ARGUMENT); } Ok(if is_micro { &MICRO_VERSIONS[versionNumber as usize - 1] + } else if is_model1 { + &MODEL1_VERSIONS[versionNumber as usize - 1] } else { &VERSIONS[versionNumber as usize - 1] }) @@ -92,4 +108,8 @@ impl Version { pub const fn isMicroQRCode(&self) -> bool { self.is_micro } + + pub const fn isQRCodeModel1(&self) -> bool { + self.is_model1 + } } diff --git a/src/common/cpp_essentials/decoder_result.rs b/src/common/cpp_essentials/decoder_result.rs index bd0967c..3504747 100644 --- a/src/common/cpp_essentials/decoder_result.rs +++ b/src/common/cpp_essentials/decoder_result.rs @@ -157,6 +157,13 @@ where self } + pub fn withIsModel1(mut self, is_model_1: bool) -> DecoderResult { + if is_model_1 { + self.content.symbology.modifier = 48 + } + self + } + // pub fn build(self) -> DecoderResult { // } diff --git a/src/multi_format_reader.rs b/src/multi_format_reader.rs index 2d3e99e..9583d6c 100644 --- a/src/multi_format_reader.rs +++ b/src/multi_format_reader.rs @@ -183,6 +183,9 @@ impl MultiFormatReader { } } BarcodeFormat::MICRO_QR_CODE => QrReader.decode_with_hints(image, &self.hints), + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE => { + QrReader.decode_with_hints(image, &self.hints) + } BarcodeFormat::DATA_MATRIX => { DataMatrixReader.decode_with_hints(image, &self.hints) } diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index 58a92bc..15c3d7b 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -33,7 +33,7 @@ pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool { pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result { let dimension = bitMatrix.height(); - let mut version = Version::FromDimension(dimension)?; + let mut version = Version::FromDimension(dimension, false)?; if version.getVersionNumber() < 7 { return Ok(version); @@ -235,6 +235,111 @@ pub fn ReadMQRCodewords( Ok(result.iter().copied().map(|x| x as u8).collect()) } +pub fn ReadQRCodewordsModel1( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + let mut result = Vec::with_capacity(version.getTotalCodewords() as usize); + let dimension = bitMatrix.height(); + let columns = dimension / 4 + 1 + 2; + for j in 0..columns { + // for (int j = 0; j < columns; j++) { + if j <= 1 { + // vertical symbols on the right side + let rows = (dimension - 8) / 4; + for i in 0..rows { + // for (int i = 0; i < rows; i++) { + if j == 0 && i % 2 == 0 && i > 0 && i < rows - 1 + // extension + { + continue; + } + let x = (dimension - 1) - (j * 2); + let y = (dimension - 1) - (i * 4); + let mut currentByte = 0; + for b in 0..8 { + // for (int b = 0; b < 8; b++) { + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, x - b % 2, y - (b / 2), None)? + != getBit( + bitMatrix, + x - b % 2, + y - (b / 2), + Some(formatInfo.isMirrored), + ), + ); + } + result.push(currentByte); + } + } else if columns - j <= 4 { + // vertical symbols on the left side + let rows = (dimension - 16) / 4; + for i in 0..rows { + // for (int i = 0; i < rows; i++) { + let x = (columns - j - 1) * 2 + 1 + (if columns - j == 4 { 1 } else { 0 }); // timing + let y = (dimension - 1) - 8 - (i * 4); + let mut currentByte = 0; + for b in 0..8 { + // for (int b = 0; b < 8; b++) { + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, x - b % 2, y - (b / 2), None)? + != getBit( + bitMatrix, + x - b % 2, + y - (b / 2), + Some(formatInfo.isMirrored), + ), + ); + } + result.push(currentByte); + } + } else { + // horizontal symbols + let rows = dimension / 2; + for i in 0..rows { + // for (int i = 0; i < rows; i++) { + if j == 2 && i >= rows - 4 + // alignment & finder + { + continue; + } + if i == 0 && j % 2 == 1 && j + 1 != columns - 4 + // extension + { + continue; + } + let x = (dimension - 1) - (2 * 2) - (j - 2) * 4; + let y = (dimension - 1) - (i * 2) - (if i >= rows - 3 { 1 } else { 0 }); // timing + let mut currentByte = 0; + for b in 0..8 { + // for (int b = 0; b < 8; b++) { + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, x - b % 4, y - (b / 4), None)? + != getBit( + bitMatrix, + x - b % 4, + y - (b / 4), + Some(formatInfo.isMirrored), + ), + ); + } + result.push(currentByte); + } + } + } + + result[0] &= 0xf; // ignore corner + if (result.len()) != version.getTotalCodewords() as usize { + return Err(Exceptions::FORMAT); + } + + Ok(result.iter().copied().map(|x| x as u8).collect()) +} + pub fn ReadCodewords( bitMatrix: &BitMatrix, version: VersionRef, @@ -246,6 +351,8 @@ pub fn ReadCodewords( if version.isMicroQRCode() { ReadMQRCodewords(bitMatrix, version, formatInfo) + } else if formatInfo.isModel1 { + ReadQRCodewordsModel1(bitMatrix, version, formatInfo) } else { ReadQRCodewords(bitMatrix, version, formatInfo) } diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs index 12aab65..0e0fceb 100644 --- a/src/qrcode/cpp_port/decoder.rs +++ b/src/qrcode/cpp_port/decoder.rs @@ -299,6 +299,10 @@ pub fn DecodeBitStream( let mut structuredAppend = StructuredAppendInfo::default(); let modeBitLength = Mode::get_codec_mode_bits_length(version); + if version.isQRCodeModel1() { + bits.readBits(4)?; /* Model 1 is leading with 4 0-bits -> drop them */ + } + let res = (|| { while !IsEndOfStream(&mut bits, version)? { let mode: Mode = if modeBitLength == 0 { @@ -387,11 +391,21 @@ pub fn DecodeBitStream( .withError(res.err()) .withEcLevel(ecLevel.to_string()) .withVersionNumber(version.getVersionNumber()) - .withStructuredAppend(structuredAppend)) + .withStructuredAppend(structuredAppend) + .withIsModel1(version.isQRCodeModel1())) } pub fn Decode(bits: &BitMatrix) -> Result> { - let Ok(pversion) = ReadVersion(bits) else { + let isMicroQRCode = bits.height() < 21; + let Ok(formatInfo) = ReadFormatInformation(bits, isMicroQRCode) else { + return Err(Exceptions::format_with("Invalid format information")); + }; + + let Ok(pversion) = (if formatInfo.isModel1 { + Version::FromDimension(bits.height(), true) + } else { + ReadVersion(bits) + }) else { return Err(Exceptions::format_with("Invalid version")); }; let version = pversion; diff --git a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs index 68c19b1..e777dc5 100644 --- a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs +++ b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs @@ -42,7 +42,7 @@ fn SimpleByteMode() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).expect("find_version"), + Version::FromNumber(1, false, false).expect("find_version"), ErrorCorrectionLevel::M, ) .expect("Decode") @@ -64,7 +64,7 @@ fn SimpleSJIS() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -86,7 +86,7 @@ fn ECI() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -105,7 +105,7 @@ fn Hanzi() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -127,7 +127,7 @@ fn HanziLevel1() { let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false).unwrap(), + Version::FromNumber(1, false, false).unwrap(), ErrorCorrectionLevel::M, ) .unwrap() @@ -138,7 +138,7 @@ fn HanziLevel1() { #[test] fn SymbologyIdentifier() { - let version = Version::FromNumber(1, false).unwrap(); + let version = Version::FromNumber(1, false, false).unwrap(); let ecLevel = ErrorCorrectionLevel::M; // Plain "ANUM(1) A" diff --git a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs index 67f326a..66b9c5f 100644 --- a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs +++ b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs @@ -63,7 +63,7 @@ fn DecodeWithBitDifference() { MASKED_TEST_FORMAT_INFO2 ^ 0x07, ), ); - assert!(!FormatInformation::DecodeQR( + assert!(FormatInformation::DecodeQR( MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO2 ^ 0x0F ) diff --git a/src/qrcode/cpp_port/test/QRModeTest.rs b/src/qrcode/cpp_port/test/QRModeTest.rs index fa356a0..03a3bae 100644 --- a/src/qrcode/cpp_port/test/QRModeTest.rs +++ b/src/qrcode/cpp_port/test/QRModeTest.rs @@ -27,27 +27,39 @@ fn CharacterCount() { // Spot check a few values assert_eq!( 10, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap()) + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::FromNumber(5, false, false).unwrap() + ) ); assert_eq!( 12, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap()) + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::FromNumber(26, false, false).unwrap() + ) ); assert_eq!( 14, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap()) + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::FromNumber(40, false, false).unwrap() + ) ); assert_eq!( 9, - Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap()) + Mode::CharacterCountBits( + &Mode::ALPHANUMERIC, + Version::FromNumber(6, false, false).unwrap() + ) ); assert_eq!( 8, - Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap()) + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false, false).unwrap()) ); assert_eq!( 8, - Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false).unwrap()) + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false, false).unwrap()) ); } @@ -110,26 +122,29 @@ fn MicroCharacterCount() { // Spot check a few values assert_eq!( 3, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true, false).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true, false).unwrap()) ); assert_eq!( 6, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true, false).unwrap()) ); assert_eq!( 3, - Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap()) + Mode::CharacterCountBits( + &Mode::ALPHANUMERIC, + Version::FromNumber(2, true, false).unwrap() + ) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap()) + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true, false).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap()) + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true, false).unwrap()) ); } diff --git a/src/qrcode/cpp_port/test/QRVersionTest.rs b/src/qrcode/cpp_port/test/QRVersionTest.rs index 6decf1b..c02f348 100644 --- a/src/qrcode/cpp_port/test/QRVersionTest.rs +++ b/src/qrcode/cpp_port/test/QRVersionTest.rs @@ -26,13 +26,13 @@ fn DoTestVersion(expectedVersion: u32, mask: i32) { #[test] fn VersionForNumber() { - let version = Version::FromNumber(0, false); + let version = Version::FromNumber(0, false, false); assert!(version.is_err(), "There is version with number 0"); for i in 1..=40 { // for (int i = 1; i <= 40; i++) { CheckVersion( - Version::FromNumber(i, false).expect("version number found"), + Version::FromNumber(i, false, false).expect("version number found"), i, 4 * i + 17, ); @@ -43,7 +43,7 @@ fn VersionForNumber() { fn GetProvisionalVersionForDimension() { for i in 1..=40 { // for (int i = 1; i <= 40; i++) { - let prov = Version::FromDimension(4 * i + 17) + let prov = Version::FromDimension(4 * i + 17, false) .unwrap_or_else(|_| panic!("version should exist for {i}")); // assert_ne!(prov, nullptr); assert_eq!(i, prov.getVersionNumber()); @@ -63,13 +63,14 @@ fn DecodeVersionInformation() { #[test] fn MicroVersionForNumber() { - let version = Version::FromNumber(0, true); + let version = Version::FromNumber(0, true, false); assert!(version.is_err(), "There is version with number 0"); for i in 1..=4 { // for (int i = 1; i <= 4; i++) { CheckVersion( - Version::FromNumber(i, true).unwrap_or_else(|_| panic!("version for {i} should exist")), + Version::FromNumber(i, true, false) + .unwrap_or_else(|_| panic!("version for {i} should exist")), i, 2 * i + 9, ); @@ -80,7 +81,7 @@ fn MicroVersionForNumber() { fn GetProvisionalMicroVersionForDimension() { for i in 1..=4 { // for (int i = 1; i <= 4; i++) { - let prov = Version::FromDimension(2 * i + 9) + let prov = Version::FromDimension(2 * i + 9, false) .unwrap_or_else(|_| panic!("version for micro {i} should exist")); // assert_ne!(prov, nullptr); assert_eq!(i, prov.getVersionNumber()); @@ -100,7 +101,7 @@ fn FunctionPattern() { }; for i in 1..=4 { // for (int i = 1; i <= 4; i++) { - let version = Version::FromNumber(i, true).expect("version must be found"); + let version = Version::FromNumber(i, true, false).expect("version must be found"); let functionPattern = version .buildFunctionPattern() .expect("function pattern must be found"); diff --git a/src/qrcode/decoder/format_information.rs b/src/qrcode/decoder/format_information.rs index 492b445..8e7281c 100644 --- a/src/qrcode/decoder/format_information.rs +++ b/src/qrcode/decoder/format_information.rs @@ -73,6 +73,7 @@ pub struct FormatInformation { pub data_mask: u8, pub microVersion: u32, pub isMirrored: bool, + pub isModel1: bool, pub index: u8, // = 255; pub bitsIndex: u8, // = 255; @@ -86,6 +87,7 @@ impl Default for FormatInformation { data_mask: Default::default(), microVersion: 0, isMirrored: false, + isModel1: false, index: 255, bitsIndex: 255, } @@ -104,6 +106,7 @@ impl FormatInformation { error_correction_level: errorCorrectionLevel, data_mask: dataMask, isMirrored: false, + isModel1: false, index: 255, bitsIndex: 255, }) diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index aeb58c7..e580a50 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -29,6 +29,7 @@ pub type VersionRef = &'static Version; pub static VERSIONS: Lazy> = Lazy::new(Version::buildVersions); pub static MICRO_VERSIONS: Lazy> = Lazy::new(Version::build_micro_versions); +pub static MODEL1_VERSIONS: Lazy> = Lazy::new(Version::build_model1_versions); /** * See ISO 18004:2006 Annex D. @@ -55,6 +56,7 @@ pub struct Version { ecBlocks: Vec, totalCodewords: u32, pub(crate) is_micro: bool, + pub(crate) is_model1: bool, } impl Version { fn new(versionNumber: u32, alignmentPatternCenters: Vec, ecBlocks: [ECBlocks; 4]) -> Self { @@ -73,6 +75,7 @@ impl Version { ecBlocks: ecBlocks.to_vec(), totalCodewords: total, is_micro: false, + is_model1: false, } } @@ -92,9 +95,35 @@ impl Version { ecBlocks, totalCodewords: total, is_micro: true, + is_model1: false, } } + fn new_model1(versionNumber: u32, ecBlocks: Vec) -> Self { + let mut total = 0; + let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); + let ecbArray = ecBlocks[0].getECBlocks(); + let mut i = 0; + while i < ecbArray.len() { + total += ecbArray[i].getCount() * (ecbArray[i].getDataCodewords() + ecCodewords); + i += 1; + } + + Self { + versionNumber, + alignmentPatternCenters: Vec::default(), + ecBlocks, + totalCodewords: total, + is_micro: false, + is_model1: true, + } + } + + // #[inline(always)] + // pub(crate) const fn isMicro(ecBlocks: &[ECBlocks; 4]) -> bool { + // ecBlocks[0].ecCodewordsPerBlock < 7 || ecBlocks[0].ecCodewordsPerBlock == 8 + // } + pub const fn getVersionNumber(&self) -> u32 { self.versionNumber } @@ -970,6 +999,224 @@ impl Version { new ECB::new(61, 16))) ]*/ } + + /* + * {1, { + 7 , 1, 19, 0, 0, + 10, 1, 16, 0, 0, + 13, 1, 13, 0, 0, + 17, 1, 9 , 0, 0 + }}, + {2, { + 10, 1, 36, 0, 0, + 16, 1, 30, 0, 0, + 22, 1, 24, 0, 0, + 30, 1, 16, 0, 0, + }}, + {3, { + 15, 1, 57, 0, 0, + 28, 1, 44, 0, 0, + 36, 1, 36, 0, 0, + 48, 1, 24, 0, 0, + }}, + {4, { + 20, 1, 80, 0, 0, + 40, 1, 60, 0, 0, + 50, 1, 50, 0, 0, + 66, 1, 34, 0, 0, + }}, + {5, { + 26, 1, 108, 0, 0, + 52, 1, 82 , 0, 0, + 66, 1, 68 , 0, 0, + 88, 2, 46 , 0, 0, + }}, + {6, { + 34 , 1, 136, 0, 0, + 63 , 2, 106, 0, 0, + 84 , 2, 86 , 0, 0, + 112, 2, 58 , 0, 0, + }}, + {7, { + 42 , 1, 170, 0, 0, + 80 , 2, 132, 0, 0, + 104, 2, 108, 0, 0, + 138, 3, 72 , 0, 0, + }}, + {8, { + 48 , 2, 208, 0, 0, + 96 , 2, 160, 0, 0, + 128, 2, 128, 0, 0, + 168, 3, 87 , 0, 0, + }}, + {9, { + 60 , 2, 246, 0, 0, + 120, 2, 186, 0, 0, + 150, 3, 156, 0, 0, + 204, 3, 102, 0, 0, + }}, + {10, { + 68 , 2, 290, 0, 0, + 136, 2, 222, 0, 0, + 174, 3, 183, 0, 0, + 232, 4, 124, 0, 0, + }}, + {11, { + 80 , 2, 336, 0, 0, + 160, 4, 256, 0, 0, + 208, 4, 208, 0, 0, + 270, 5, 145, 0, 0, + }}, + {12, { + 92 , 2, 384, 0, 0, + 184, 4, 292, 0, 0, + 232, 4, 244, 0, 0, + 310, 5, 165, 0, 0, + }}, + {13, { + 108, 3, 432, 0, 0, + 208, 4, 332, 0, 0, + 264, 4, 276, 0, 0, + 348, 6, 192, 0, 0, + }}, + {14, { + 120, 3, 489, 0, 0, + 240, 4, 368, 0, 0, + 300, 5, 310, 0, 0, + 396, 6, 210, 0, 0, + }}, + }; + */ + pub fn build_model1_versions() -> Vec { + Vec::from([ + Version::new_model1( + 1, + vec![ + ECBlocks::new(7, vec![ECB::new(1, 19)]), + ECBlocks::new(10, vec![ECB::new(1, 16)]), + ECBlocks::new(13, vec![ECB::new(1, 13)]), + ECBlocks::new(17, vec![ECB::new(1, 9)]), + ], + ), + Version::new_model1( + 2, + vec![ + ECBlocks::new(10, vec![ECB::new(1, 36)]), + ECBlocks::new(16, vec![ECB::new(1, 30)]), + ECBlocks::new(22, vec![ECB::new(1, 24)]), + ECBlocks::new(30, vec![ECB::new(1, 16)]), + ], + ), + Version::new_model1( + 3, + vec![ + ECBlocks::new(15, vec![ECB::new(1, 57)]), + ECBlocks::new(28, vec![ECB::new(1, 44)]), + ECBlocks::new(36, vec![ECB::new(1, 36)]), + ECBlocks::new(48, vec![ECB::new(1, 24)]), + ], + ), + Version::new_model1( + 4, + vec![ + ECBlocks::new(20, vec![ECB::new(1, 80)]), + ECBlocks::new(40, vec![ECB::new(1, 60)]), + ECBlocks::new(50, vec![ECB::new(1, 50)]), + ECBlocks::new(66, vec![ECB::new(1, 34)]), + ], + ), + Version::new_model1( + 5, + vec![ + ECBlocks::new(26, vec![ECB::new(1, 108)]), + ECBlocks::new(52, vec![ECB::new(1, 82)]), + ECBlocks::new(66, vec![ECB::new(1, 68)]), + ECBlocks::new(88, vec![ECB::new(2, 46)]), + ], + ), + Version::new_model1( + 6, + vec![ + ECBlocks::new(34, vec![ECB::new(1, 136)]), + ECBlocks::new(63, vec![ECB::new(2, 106)]), + ECBlocks::new(84, vec![ECB::new(2, 86)]), + ECBlocks::new(112, vec![ECB::new(2, 58)]), + ], + ), + Version::new_model1( + 7, + vec![ + ECBlocks::new(42, vec![ECB::new(1, 170)]), + ECBlocks::new(80, vec![ECB::new(2, 132)]), + ECBlocks::new(104, vec![ECB::new(2, 108)]), + ECBlocks::new(138, vec![ECB::new(3, 72)]), + ], + ), + Version::new_model1( + 8, + vec![ + ECBlocks::new(48, vec![ECB::new(2, 208)]), + ECBlocks::new(96, vec![ECB::new(2, 160)]), + ECBlocks::new(128, vec![ECB::new(2, 128)]), + ECBlocks::new(168, vec![ECB::new(3, 87)]), + ], + ), + Version::new_model1( + 9, + vec![ + ECBlocks::new(60, vec![ECB::new(2, 246)]), + ECBlocks::new(120, vec![ECB::new(2, 186)]), + ECBlocks::new(150, vec![ECB::new(3, 156)]), + ECBlocks::new(204, vec![ECB::new(3, 102)]), + ], + ), + Version::new_model1( + 10, + vec![ + ECBlocks::new(68, vec![ECB::new(2, 290)]), + ECBlocks::new(136, vec![ECB::new(2, 222)]), + ECBlocks::new(174, vec![ECB::new(3, 183)]), + ECBlocks::new(232, vec![ECB::new(4, 124)]), + ], + ), + Version::new_model1( + 11, + vec![ + ECBlocks::new(80, vec![ECB::new(2, 336)]), + ECBlocks::new(160, vec![ECB::new(4, 256)]), + ECBlocks::new(208, vec![ECB::new(4, 208)]), + ECBlocks::new(270, vec![ECB::new(5, 145)]), + ], + ), + Version::new_model1( + 12, + vec![ + ECBlocks::new(92, vec![ECB::new(2, 384)]), + ECBlocks::new(184, vec![ECB::new(4, 292)]), + ECBlocks::new(232, vec![ECB::new(4, 244)]), + ECBlocks::new(310, vec![ECB::new(5, 165)]), + ], + ), + Version::new_model1( + 13, + vec![ + ECBlocks::new(108, vec![ECB::new(3, 432)]), + ECBlocks::new(208, vec![ECB::new(4, 332)]), + ECBlocks::new(264, vec![ECB::new(4, 276)]), + ECBlocks::new(348, vec![ECB::new(6, 192)]), + ], + ), + Version::new_model1( + 14, + vec![ + ECBlocks::new(120, vec![ECB::new(3, 489)]), + ECBlocks::new(240, vec![ECB::new(4, 368)]), + ECBlocks::new(300, vec![ECB::new(5, 310)]), + ECBlocks::new(396, vec![ECB::new(6, 210)]), + ], + ), + ]) + } } impl fmt::Display for Version { diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt b/test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt new file mode 100644 index 0000000..7f36347 --- /dev/null +++ b/test_resources/blackbox/cpp/qrcode-2/qr-model-1.metadata.txt @@ -0,0 +1,2 @@ +symbologyIdentifier=]Q0 +ecLevel=M diff --git a/test_resources/blackbox/cpp/qrcode-2/qr-model-1.png b/test_resources/blackbox/cpp/qrcode-2/qr-model-1.png new file mode 100644 index 0000000000000000000000000000000000000000..4b199902d0bae9a8fd98dbe698d570069d80ac40 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^P9V(43?y&PnzR~7u?6^qxB}__|Nk$&IsYz@HQUq0 zF+}71+DnF9%my5+0S8SdtY(Q}jH(NKJ#hus_iDb=GVF(QR)qtdDm~>V#Of0& Date: Thu, 11 Jan 2024 17:28:46 -0600 Subject: [PATCH 02/15] port: migrate to new cpp Version / FormatInformation --- .../base_extentions/qr_formatinformation.rs | 168 ++++++++++-------- .../base_extentions/qrcode_version.rs | 115 ++++++------ src/qrcode/cpp_port/bitmatrix_parser.rs | 47 +++-- src/qrcode/cpp_port/decoder.rs | 22 +-- src/qrcode/cpp_port/mod.rs | 3 + src/qrcode/cpp_port/qr_type.rs | 14 ++ .../cpp_port/test/QRBitMatrixParserTest.rs | 12 +- .../test/QRDecodedBitStreamParserTest.rs | 52 ++---- .../cpp_port/test/QRFormatInformationTest.rs | 14 +- src/qrcode/cpp_port/test/QRModeTest.rs | 39 ++-- src/qrcode/cpp_port/test/QRVersionTest.rs | 32 ++-- src/qrcode/decoder/format_information.rs | 13 +- src/qrcode/decoder/mode.rs | 6 +- src/qrcode/decoder/version.rs | 17 +- 14 files changed, 282 insertions(+), 272 deletions(-) create mode 100644 src/qrcode/cpp_port/qr_type.rs diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 30919e2..143db83 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -1,43 +1,48 @@ -use crate::qrcode::decoder::{ - ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, FORMAT_INFO_MASK_QR, +use crate::qrcode::{ + cpp_port::Type, + decoder::{ + ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, + FORMAT_INFO_MASK_MODEL2, FORMAT_INFO_MASK_QR, + }, }; pub const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825; +pub const FORMAT_INFO_MASK_MICRO: u32 = 0x4445; -pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [[u32; 2]; 32] = [ - [0x4445, 0x00], - [0x4172, 0x01], - [0x4E2B, 0x02], - [0x4B1C, 0x03], - [0x55AE, 0x04], - [0x5099, 0x05], - [0x5FC0, 0x06], - [0x5AF7, 0x07], - [0x6793, 0x08], - [0x62A4, 0x09], - [0x6DFD, 0x0A], - [0x68CA, 0x0B], - [0x7678, 0x0C], - [0x734F, 0x0D], - [0x7C16, 0x0E], - [0x7921, 0x0F], - [0x06DE, 0x10], - [0x03E9, 0x11], - [0x0CB0, 0x12], - [0x0987, 0x13], - [0x1735, 0x14], - [0x1202, 0x15], - [0x1D5B, 0x16], - [0x186C, 0x17], - [0x2508, 0x18], - [0x203F, 0x19], - [0x2F66, 0x1A], - [0x2A51, 0x1B], - [0x34E3, 0x1C], - [0x31D4, 0x1D], - [0x3E8D, 0x1E], - [0x3BBA, 0x1F], -]; +// pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [u32 ;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, +// ]; impl FormatInformation { /** @@ -51,9 +56,9 @@ impl FormatInformation { ); let formatInfoBits2 = ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); + // Some (Model2) QR codes apparently do not apply the XOR mask. Try with (standard) and without (quirk) masking. let mut fi = Self::FindBestFormatInfo( - &[0, FORMAT_INFO_MASK_QR], - FORMAT_INFO_DECODE_LOOKUP, + &[FORMAT_INFO_MASK_QR, 0, FORMAT_INFO_MASK_QR_MODEL1], &[ formatInfoBits1, formatInfoBits2, @@ -62,26 +67,10 @@ impl FormatInformation { ], ); - let mut fi_model1 = Self::FindBestFormatInfo( - &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], - FORMAT_INFO_DECODE_LOOKUP, - &[ - formatInfoBits1, - formatInfoBits2, - Self::MirrorBits(formatInfoBits1), - mirroredFormatInfoBits2, - ], - ); - - if fi_model1.hammingDistance < fi.hammingDistance { - fi_model1.isModel1 = true; - fi = fi_model1; - } - // Use bits 3/4 for error correction, and 0-2 for mask. fi.error_correction_level = - ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false); - fi.data_mask = fi.index & 0x07; + ErrorCorrectionLevel::ECLevelFromBits((fi.data >> 3) as u8 & 0x03, false); + fi.data_mask = fi.data as u8 & 0x07; fi.isMirrored = fi.bitsIndex > 1; fi @@ -90,8 +79,7 @@ impl FormatInformation { pub fn DecodeMQR(formatInfoBits: u32) -> Self { // We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders let mut fi = Self::FindBestFormatInfo( - &[0], - FORMAT_INFO_DECODE_LOOKUP_MICRO, + &[FORMAT_INFO_MASK_MICRO, 0], &[formatInfoBits, Self::MirrorBits(formatInfoBits)], ); @@ -99,9 +87,9 @@ impl FormatInformation { // Bits 2/3/4 contain both error correction level and version, 0/1 contain mask. fi.error_correction_level = - ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 2) & 0x07, true); - fi.data_mask = fi.index & 0x03; - fi.microVersion = BITS_TO_VERSION[((fi.index >> 2) & 0x07) as usize] as u32; + ErrorCorrectionLevel::ECLevelFromBits((fi.data >> 2) as u8 & 0x07, true); + fi.data_mask = fi.data as u8 & 0x03; + fi.microVersion = BITS_TO_VERSION[((fi.data >> 2) as u8 & 0x07) as usize] as u32; fi.isMirrored = fi.bitsIndex == 1; fi @@ -112,21 +100,30 @@ impl FormatInformation { (bits.reverse_bits()) >> 17 } - pub fn FindBestFormatInfo(masks: &[u32], lookup: [[u32; 2]; 32], bits: &[u32]) -> Self { + pub fn FindBestFormatInfo(masks: &[u32], bits: &[u32]) -> Self { let mut fi = FormatInformation::default(); - // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. + // See ISO 18004:2015, Annex C, Table C.1 + const MODEL2_MASKED_PATTERNS: [u32; 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, + ]; + for mask in masks { - // for (auto mask : {0, mask}) - for (bitsIndex, bit_set) in bits.iter().enumerate() { + // for (auto mask : masks) + for bitsIndex in 0..bits.len() { // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) - for [pattern, index] in lookup { - // for (const auto& [pattern, index] : lookup) { - // Find the int in lookup with fewest bits differing - let hammingDist = ((bit_set ^ mask) ^ pattern).count_ones(); + for ref_pattern in MODEL2_MASKED_PATTERNS { + // for (uint32_t pattern : MODEL2_MASKED_PATTERNS) { + // 'unmask' the pattern first to get the original 5-data bits + 10-ec bits back + let pattern = ref_pattern ^ FORMAT_INFO_MASK_MODEL2; + // Find the pattern with fewest bits differing + let hammingDist = ((bits[bitsIndex] ^ mask) ^ pattern).count_ones(); + // if (int hammingDist = BitHacks::CountBitsSet((bits[bitsIndex] ^ mask) ^ pattern); if hammingDist < fi.hammingDistance { - // if (int hammingDist = BitHacks::CountBitsSet((bits[bitsIndex] ^ mask) ^ pattern); hammingDist < fi.hammingDistance) { - fi.index = index as u8; + fi.mask = *mask; // store the used mask to discriminate between types/models + fi.data = pattern >> 10; // drop the 10 BCH error correction bits fi.hammingDistance = hammingDist; fi.bitsIndex = bitsIndex as u8; } @@ -134,9 +131,37 @@ impl FormatInformation { } } + // // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. + // for mask in masks { + // // for (auto mask : {0, mask}) + // for (bitsIndex, bit_set) in bits.iter().enumerate() { + // // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) + // for [pattern, _index] in FORMAT_INFO_DECODE_LOOKUP { + // // for (const auto& [pattern, index] : lookup) { + // // Find the int in lookup with fewest bits differing + // let hammingDist = ((bit_set ^ mask) ^ pattern).count_ones(); + // if hammingDist < fi.hammingDistance { + // // if (int hammingDist = BitHacks::CountBitsSet((bits[bitsIndex] ^ mask) ^ pattern); hammingDist < fi.hammingDistance) { + // fi.mask = *mask; // store the used mask to discriminate between types/models + // fi.data = pattern >> 10; // drop the 10 BCH error correction bits + // fi.hammingDistance = hammingDist; + // fi.bitsIndex = bitsIndex as u8; + // } + // } + // } + // } + fi } + pub fn qr_type(&self) -> Type { + match self.mask { + FORMAT_INFO_MASK_QR_MODEL1 => Type::Model1, + FORMAT_INFO_MASK_MICRO => Type::Micro, + _ => Type::Model2, + } + } + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match pub fn isValid(&self) -> bool { self.hammingDistance <= 3 @@ -145,5 +170,6 @@ impl FormatInformation { pub fn cpp_eq(&self, other: &Self) -> bool { self.data_mask == other.data_mask && self.error_correction_level == other.error_correction_level + && self.qr_type() == other.qr_type() } } diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index d11cc56..ca63868 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -1,60 +1,40 @@ -use crate::common::Result; +/* +* Copyright 2016 Nu-book Inc. +* Copyright 2016 ZXing authors +* Copyright 2023 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{BitMatrix, Result}; +use crate::qrcode::cpp_port::Type; use crate::qrcode::decoder::{ Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, VERSIONS, VERSION_DECODE_INFO, }; use crate::Exceptions; -// const Version* Version::AllMicroVersions() -// { -// /** -// * See ISO 18004:2006 6.5.1 Table 9 -// */ -// static const Version allVersions[] = { -// {1, {2, 1, 3, 0, 0}}, -// {2, {5, 1, 5, 0, 0, 6, 1, 4, 0, 0}}, -// {3, {6, 1, 11, 0, 0, 8, 1, 9, 0, 0}}, -// {4, {8, 1, 16, 0, 0, 10, 1, 14, 0, 0, 14, 1, 10, 0, 0}}}; -// return allVersions; -// } - impl Version { - pub fn FromDimension(dimension: u32, is_model1: bool) -> Result { - let isMicro = dimension < 21; - if dimension % Self::DimensionStep(isMicro) != 1 { - //throw std::invalid_argument("Unexpected dimension"); - return Err(Exceptions::ILLEGAL_ARGUMENT); + pub fn Model1(version_number: u32) -> Result { + if version_number < 1 || version_number > 14 { + Err(Exceptions::ILLEGAL_ARGUMENT) + } else { + Ok(&MODEL1_VERSIONS[version_number as usize - 1]) } - Self::FromNumber( - (dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro), - isMicro, - is_model1, - ) } - pub fn FromNumber(versionNumber: u32, is_micro: bool, is_model1: bool) -> Result { - if versionNumber < 1 - || versionNumber - > (if is_micro { - 4 - } else { - if is_model1 { - 14 - } else { - 40 - } - }) - { - //throw std::invalid_argument("Version should be in range [1-40]."); - return Err(Exceptions::ILLEGAL_ARGUMENT); - } - - Ok(if is_micro { - &MICRO_VERSIONS[versionNumber as usize - 1] - } else if is_model1 { - &MODEL1_VERSIONS[versionNumber as usize - 1] + pub fn Model2(version_number: u32) -> Result { + if version_number < 1 || version_number > 40 { + Err(Exceptions::ILLEGAL_ARGUMENT) } else { - &VERSIONS[versionNumber as usize - 1] - }) + Ok(&VERSIONS[version_number as usize - 1]) + } + } + + pub fn Micro(version_number: u32) -> Result { + if version_number < 1 || version_number > 4 { + Err(Exceptions::ILLEGAL_ARGUMENT) + } else { + Ok(&MICRO_VERSIONS[version_number as usize - 1]) + } } pub fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 { @@ -80,13 +60,6 @@ impl Version { let mut bestDifference = u32::MAX; let mut bestVersion = 0; for (i, targetVersion) in VERSION_DECODE_INFO.into_iter().enumerate() { - // for (int targetVersion : VERSION_DECODE_INFO) { - // Do the version info bits match exactly? done. - if targetVersion == versionBitsA as u32 || targetVersion == versionBitsB as u32 { - return Self::getVersionForNumber(i as u32 + 7); - } - // Otherwise see if this is the closest to a real version info bit string - // we have seen so far for bits in [versionBitsA, versionBitsB] { // for (int bits : {versionBitsA, versionBitsB}) { let bitsDifference = ((bits as u32) ^ targetVersion).count_ones(); //BitHacks::CountBitsSet(bits ^ targetVersion); @@ -95,6 +68,9 @@ impl Version { bestDifference = bitsDifference; } } + if bestDifference == 0 { + break; + } } // We can tolerate up to 3 bits of error since no two version info codewords will // differ in less than 8 bits. @@ -105,11 +81,34 @@ impl Version { Err(Exceptions::ILLEGAL_STATE) } - pub const fn isMicroQRCode(&self) -> bool { - self.is_micro + pub const fn isMicro(&self) -> bool { + Type::const_eq(self.qr_type, Type::Micro) } - pub const fn isQRCodeModel1(&self) -> bool { - self.is_model1 + pub const fn isModel1(&self) -> bool { + Type::const_eq(self.qr_type, Type::Model1) + } + + pub const fn isModel2(&self) -> bool { + Type::const_eq(self.qr_type, Type::Model2) + } + + pub fn HasMicroSize(bitMatrix: &BitMatrix) -> bool { + let size = bitMatrix.height(); + size >= 11 && size <= 17 && (size % 2) == 1 + } + + pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool { + let size = bitMatrix.height(); + Self::HasMicroSize(bitMatrix) || (size >= 21 && size <= 177 && (size % 4) == 1) + } + + pub fn Number(bitMatrix: &BitMatrix) -> u32 { + if !Self::HasValidSize(bitMatrix) { + 0 + } else { + let isMicro = Self::HasMicroSize(bitMatrix); + (bitMatrix.height() - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro) + } } } diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index 15c3d7b..957f35c 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -10,7 +10,7 @@ use crate::{ Exceptions, }; -use super::{data_mask::GetDataMaskBit, detector::AppendBit}; +use super::{data_mask::GetDataMaskBit, detector::AppendBit, Type}; pub fn getBit(bitMatrix: &BitMatrix, x: u32, y: u32, mirrored: Option) -> bool { let mirrored = mirrored.unwrap_or(false); @@ -21,24 +21,27 @@ pub fn getBit(bitMatrix: &BitMatrix, x: u32, y: u32, mirrored: Option) -> } } -pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool { - let dimension = bitMatrix.height(); - if isMicro { - (11..=17).contains(&dimension) && (dimension % 2) == 1 - } else { - (21..=177).contains(&dimension) && (dimension % 4) == 1 +pub fn ReadVersion(bitMatrix: &BitMatrix, qr_type: Type) -> Result { + if !Version::HasValidSize(bitMatrix) { + return Err(Exceptions::FORMAT); } -} -pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result { - let dimension = bitMatrix.height(); + let number = Version::Number(bitMatrix); - let mut version = Version::FromDimension(dimension, false)?; + match qr_type { + Type::Model1 => return Version::Model1(number), + + Type::Micro => return Version::Micro(number), + Type::Model2 => {} + } + let mut version = Version::Model2(number)?; if version.getVersionNumber() < 7 { return Ok(version); } + let dimension = bitMatrix.height(); + for mirror in [false, true] { // for (bool mirror : {false, true}) { // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored) @@ -60,12 +63,8 @@ pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result { Err(Exceptions::FORMAT) } -pub fn ReadFormatInformation(bitMatrix: &BitMatrix, isMicro: bool) -> Result { - if !hasValidDimension(bitMatrix, isMicro) { - return Err(Exceptions::FORMAT); - } - - if isMicro { +pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result { + if Version::HasMicroSize(bitMatrix) { // Read top-left format info bits let mut formatInfoBits = 0; for x in 1..9 { @@ -345,15 +344,9 @@ pub fn ReadCodewords( version: VersionRef, formatInfo: &FormatInformation, ) -> Result> { - if !hasValidDimension(bitMatrix, version.isMicroQRCode()) { - return Err(Exceptions::FORMAT); - } - - if version.isMicroQRCode() { - ReadMQRCodewords(bitMatrix, version, formatInfo) - } else if formatInfo.isModel1 { - ReadQRCodewordsModel1(bitMatrix, version, formatInfo) - } else { - ReadQRCodewords(bitMatrix, version, formatInfo) + match version.qr_type { + Type::Model1 => ReadQRCodewordsModel1(bitMatrix, version, formatInfo), + Type::Model2 => ReadQRCodewords(bitMatrix, version, formatInfo), + Type::Micro => ReadMQRCodewords(bitMatrix, version, formatInfo), } } diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs index 0e0fceb..8b60af5 100644 --- a/src/qrcode/cpp_port/decoder.rs +++ b/src/qrcode/cpp_port/decoder.rs @@ -17,6 +17,8 @@ use crate::qrcode::cpp_port::bitmatrix_parser::{ use crate::qrcode::decoder::{DataBlock, ErrorCorrectionLevel, Mode, Version}; use crate::Exceptions; +use super::Type; + /** *

Given data and error-correction codewords received, possibly corrupted by errors, attempts to * correct the errors in-place using Reed-Solomon error correction.

@@ -299,7 +301,7 @@ pub fn DecodeBitStream( let mut structuredAppend = StructuredAppendInfo::default(); let modeBitLength = Mode::get_codec_mode_bits_length(version); - if version.isQRCodeModel1() { + if version.isModel1() { bits.readBits(4)?; /* Model 1 is leading with 4 0-bits -> drop them */ } @@ -310,7 +312,7 @@ pub fn DecodeBitStream( } else { Mode::CodecModeForBits( bits.readBits(modeBitLength as usize)?, - Some(version.isMicroQRCode()), + Some(version.isMicro()), )? }; @@ -392,25 +394,23 @@ pub fn DecodeBitStream( .withEcLevel(ecLevel.to_string()) .withVersionNumber(version.getVersionNumber()) .withStructuredAppend(structuredAppend) - .withIsModel1(version.isQRCodeModel1())) + .withIsModel1(version.isModel1())) } pub fn Decode(bits: &BitMatrix) -> Result> { - let isMicroQRCode = bits.height() < 21; - let Ok(formatInfo) = ReadFormatInformation(bits, isMicroQRCode) else { + if !Version::HasValidSize(bits) { + return Err(Exceptions::format_with("Invalid symbol size")); + } + let Ok(formatInfo) = ReadFormatInformation(bits) else { return Err(Exceptions::format_with("Invalid format information")); }; - let Ok(pversion) = (if formatInfo.isModel1 { - Version::FromDimension(bits.height(), true) - } else { - ReadVersion(bits) - }) else { + let Ok(pversion) = ReadVersion(bits, formatInfo.qr_type()) else { return Err(Exceptions::format_with("Invalid version")); }; let version = pversion; - let Ok(formatInfo) = ReadFormatInformation(bits, version.isMicroQRCode()) else { + let Ok(formatInfo) = ReadFormatInformation(bits) else { return Err(Exceptions::format_with("Invalid format information")); }; diff --git a/src/qrcode/cpp_port/mod.rs b/src/qrcode/cpp_port/mod.rs index f18f038..8b9931e 100644 --- a/src/qrcode/cpp_port/mod.rs +++ b/src/qrcode/cpp_port/mod.rs @@ -7,5 +7,8 @@ pub use qr_cpp_reader::QrReader; mod bitmatrix_parser; +mod qr_type; +pub use qr_type::Type; + #[cfg(test)] mod test; diff --git a/src/qrcode/cpp_port/qr_type.rs b/src/qrcode/cpp_port/qr_type.rs new file mode 100644 index 0000000..610d741 --- /dev/null +++ b/src/qrcode/cpp_port/qr_type.rs @@ -0,0 +1,14 @@ +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum Type { + Model1, + Model2, + Micro, +} + +impl Type { + pub const fn const_eq(a: Type, b: Type) -> bool { + let (a, b) = (a as u8, b as u8); + + a == b + } +} diff --git a/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs b/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs index f6610e3..1c04e9a 100644 --- a/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs +++ b/src/qrcode/cpp_port/test/QRBitMatrixParserTest.rs @@ -33,10 +33,10 @@ XXX XX X X XXXX ) .expect("parse must parse"); - let version = ReadVersion(&bitMatrix).unwrap(); + let format = ReadFormatInformation(&bitMatrix).expect("could not read format information"); + let version = ReadVersion(&bitMatrix, format.qr_type()).expect("version found"); assert_eq!(3, version.getVersionNumber()); - let format = - ReadFormatInformation(&bitMatrix, true).expect("could not read format information"); + let codewords = ReadCodewords(&bitMatrix, version, &format).expect("could not read codewords"); assert_eq!(17, codewords.len()); assert_eq!(0x0, codewords[10]); @@ -67,10 +67,10 @@ X X XXXX XXX ) .unwrap(); - let version = ReadVersion(&bitMatrix).unwrap(); + let format = ReadFormatInformation(&bitMatrix).expect("could not read format information"); + let version = ReadVersion(&bitMatrix, format.qr_type()).expect("could not read version"); assert_eq!(3, version.getVersionNumber()); - let format = - ReadFormatInformation(&bitMatrix, true).expect("could not read format information"); + let codewords = ReadCodewords(&bitMatrix, version, &format).expect("could not read codewords"); assert_eq!(17, codewords.len()); assert_eq!(0x0, codewords[8]); diff --git a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs index e777dc5..2b5bc74 100644 --- a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs +++ b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.rs @@ -42,7 +42,7 @@ fn SimpleByteMode() { let bytes: Vec = ba.into(); let result = DecodeBitStream( &bytes, - Version::FromNumber(1, false, false).expect("find_version"), + Version::Model2(1).expect("find_version"), ErrorCorrectionLevel::M, ) .expect("Decode") @@ -62,14 +62,10 @@ fn SimpleSJIS() { ba.appendBits(0xA3, 8).expect("append"); ba.appendBits(0xD0, 8).expect("append"); let bytes: Vec = ba.into(); - let result = DecodeBitStream( - &bytes, - Version::FromNumber(1, false, false).unwrap(), - ErrorCorrectionLevel::M, - ) - .unwrap() - .content() - .to_string(); + let result = DecodeBitStream(&bytes, Version::Model2(1).unwrap(), ErrorCorrectionLevel::M) + .unwrap() + .content() + .to_string(); assert_eq!("\u{ff61}\u{ff62}\u{ff63}\u{ff90}", result); } @@ -84,14 +80,10 @@ fn ECI() { ba.appendBits(0xA2, 8).expect("append"); ba.appendBits(0xA3, 8).expect("append"); let bytes: Vec = ba.into(); - let result = DecodeBitStream( - &bytes, - Version::FromNumber(1, false, false).unwrap(), - ErrorCorrectionLevel::M, - ) - .unwrap() - .content() - .to_string(); + let result = DecodeBitStream(&bytes, Version::Model2(1).unwrap(), ErrorCorrectionLevel::M) + .unwrap() + .content() + .to_string(); assert_eq!("\u{ED}\u{F3}\u{FA}", result); } @@ -103,14 +95,10 @@ fn Hanzi() { ba.appendBits(0x01, 8).expect("append"); // 1 characters ba.appendBits(0x03C1, 13).expect("append"); let bytes: Vec = ba.into(); - let result = DecodeBitStream( - &bytes, - Version::FromNumber(1, false, false).unwrap(), - ErrorCorrectionLevel::M, - ) - .unwrap() - .content() - .to_string(); + let result = DecodeBitStream(&bytes, Version::Model2(1).unwrap(), ErrorCorrectionLevel::M) + .unwrap() + .content() + .to_string(); assert_eq!("\u{963f}", result); } @@ -125,20 +113,16 @@ fn HanziLevel1() { let bytes: Vec = ba.into(); - let result = DecodeBitStream( - &bytes, - Version::FromNumber(1, false, false).unwrap(), - ErrorCorrectionLevel::M, - ) - .unwrap() - .content() - .to_string(); + let result = DecodeBitStream(&bytes, Version::Model2(1).unwrap(), ErrorCorrectionLevel::M) + .unwrap() + .content() + .to_string(); assert_eq!("\u{30a2}", result); } #[test] fn SymbologyIdentifier() { - let version = Version::FromNumber(1, false, false).unwrap(); + let version = Version::Model2(1).unwrap(); let ecLevel = ErrorCorrectionLevel::M; // Plain "ANUM(1) A" diff --git a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs index 66b9c5f..352329c 100644 --- a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs +++ b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs @@ -4,7 +4,10 @@ */ // SPDX-License-Identifier: Apache-2.0 -use crate::qrcode::decoder::{ErrorCorrectionLevel, FormatInformation}; +use crate::qrcode::{ + cpp_port::Type, + decoder::{ErrorCorrectionLevel, FormatInformation}, +}; const MASKED_TEST_FORMAT_INFO: u32 = 0x2BED; const MASKED_TEST_FORMAT_INFO2: u32 = @@ -63,11 +66,12 @@ fn DecodeWithBitDifference() { MASKED_TEST_FORMAT_INFO2 ^ 0x07, ), ); - assert!(FormatInformation::DecodeQR( + let unexpected = FormatInformation::DecodeQR( MASKED_TEST_FORMAT_INFO ^ 0x0F, - MASKED_TEST_FORMAT_INFO2 ^ 0x0F - ) - .isValid()); + MASKED_TEST_FORMAT_INFO2 ^ 0x0F, + ); + assert!(!&expected.cpp_eq(&unexpected)); + assert!(!(unexpected.isValid() && unexpected.qr_type() == Type::Model2)); } #[test] diff --git a/src/qrcode/cpp_port/test/QRModeTest.rs b/src/qrcode/cpp_port/test/QRModeTest.rs index 03a3bae..324f06a 100644 --- a/src/qrcode/cpp_port/test/QRModeTest.rs +++ b/src/qrcode/cpp_port/test/QRModeTest.rs @@ -27,39 +27,27 @@ fn CharacterCount() { // Spot check a few values assert_eq!( 10, - Mode::CharacterCountBits( - &Mode::NUMERIC, - Version::FromNumber(5, false, false).unwrap() - ) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::Model2(5).unwrap()) ); assert_eq!( 12, - Mode::CharacterCountBits( - &Mode::NUMERIC, - Version::FromNumber(26, false, false).unwrap() - ) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::Model2(26).unwrap()) ); assert_eq!( 14, - Mode::CharacterCountBits( - &Mode::NUMERIC, - Version::FromNumber(40, false, false).unwrap() - ) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::Model2(40).unwrap()) ); assert_eq!( 9, - Mode::CharacterCountBits( - &Mode::ALPHANUMERIC, - Version::FromNumber(6, false, false).unwrap() - ) + Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::Model2(6).unwrap()) ); assert_eq!( 8, - Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false, false).unwrap()) + Mode::CharacterCountBits(&Mode::BYTE, Version::Model2(7).unwrap()) ); assert_eq!( 8, - Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false, false).unwrap()) + Mode::CharacterCountBits(&Mode::KANJI, Version::Model2(8).unwrap()) ); } @@ -122,29 +110,26 @@ fn MicroCharacterCount() { // Spot check a few values assert_eq!( 3, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true, false).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::Micro(1).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true, false).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::Micro(2).unwrap()) ); assert_eq!( 6, - Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true, false).unwrap()) + Mode::CharacterCountBits(&Mode::NUMERIC, Version::Micro(4).unwrap()) ); assert_eq!( 3, - Mode::CharacterCountBits( - &Mode::ALPHANUMERIC, - Version::FromNumber(2, true, false).unwrap() - ) + Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::Micro(2).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true, false).unwrap()) + Mode::CharacterCountBits(&Mode::BYTE, Version::Micro(3).unwrap()) ); assert_eq!( 4, - Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true, false).unwrap()) + Mode::CharacterCountBits(&Mode::KANJI, Version::Micro(4).unwrap()) ); } diff --git a/src/qrcode/cpp_port/test/QRVersionTest.rs b/src/qrcode/cpp_port/test/QRVersionTest.rs index c02f348..d5d0b1e 100644 --- a/src/qrcode/cpp_port/test/QRVersionTest.rs +++ b/src/qrcode/cpp_port/test/QRVersionTest.rs @@ -12,7 +12,7 @@ use crate::{ fn CheckVersion(version: VersionRef, number: u32, dimension: u32) { // assert_ne!(version, nullptr); assert_eq!(number, version.getVersionNumber()); - if number > 1 && !version.isMicroQRCode() { + if number > 1 && version.isModel2() { assert!(!version.getAlignmentPatternCenters().is_empty()); } assert_eq!(dimension, version.getDimensionForVersion()); @@ -26,13 +26,13 @@ fn DoTestVersion(expectedVersion: u32, mask: i32) { #[test] fn VersionForNumber() { - let version = Version::FromNumber(0, false, false); + let version = Version::Model2(0); assert!(version.is_err(), "There is version with number 0"); for i in 1..=40 { // for (int i = 1; i <= 40; i++) { CheckVersion( - Version::FromNumber(i, false, false).expect("version number found"), + Version::Model2(i).expect("version number found"), i, 4 * i + 17, ); @@ -43,10 +43,13 @@ fn VersionForNumber() { fn GetProvisionalVersionForDimension() { for i in 1..=40 { // for (int i = 1; i <= 40; i++) { - let prov = Version::FromDimension(4 * i + 17, false) - .unwrap_or_else(|_| panic!("version should exist for {i}")); // assert_ne!(prov, nullptr); - assert_eq!(i, prov.getVersionNumber()); + assert_eq!( + i, + Version::Number( + &BitMatrix::with_single_dimension(4 * i + 17).expect("must create bitmatrix") + ) + ); } } @@ -63,14 +66,13 @@ fn DecodeVersionInformation() { #[test] fn MicroVersionForNumber() { - let version = Version::FromNumber(0, true, false); + let version = Version::Micro(0); assert!(version.is_err(), "There is version with number 0"); for i in 1..=4 { // for (int i = 1; i <= 4; i++) { CheckVersion( - Version::FromNumber(i, true, false) - .unwrap_or_else(|_| panic!("version for {i} should exist")), + Version::Micro(i).unwrap_or_else(|_| panic!("version for {i} should exist")), i, 2 * i + 9, ); @@ -81,10 +83,12 @@ fn MicroVersionForNumber() { fn GetProvisionalMicroVersionForDimension() { for i in 1..=4 { // for (int i = 1; i <= 4; i++) { - let prov = Version::FromDimension(2 * i + 9, false) - .unwrap_or_else(|_| panic!("version for micro {i} should exist")); - // assert_ne!(prov, nullptr); - assert_eq!(i, prov.getVersionNumber()); + assert_eq!( + i, + Version::Number( + &BitMatrix::with_single_dimension(2 * i + 9).expect("must create bitmatrix") + ) + ); } } @@ -101,7 +105,7 @@ fn FunctionPattern() { }; for i in 1..=4 { // for (int i = 1; i <= 4; i++) { - let version = Version::FromNumber(i, true, false).expect("version must be found"); + let version = Version::Micro(i).expect("version must be found"); let functionPattern = version .buildFunctionPattern() .expect("function pattern must be found"); diff --git a/src/qrcode/decoder/format_information.rs b/src/qrcode/decoder/format_information.rs index 8e7281c..4591cc9 100644 --- a/src/qrcode/decoder/format_information.rs +++ b/src/qrcode/decoder/format_information.rs @@ -19,6 +19,7 @@ use crate::common::Result; use super::ErrorCorrectionLevel; pub const FORMAT_INFO_MASK_QR: u32 = 0x5412; +pub const FORMAT_INFO_MASK_MODEL2: u32 = FORMAT_INFO_MASK_QR; /** * See ISO 18004:2006, Annex C, Table C.1 @@ -73,9 +74,9 @@ pub struct FormatInformation { pub data_mask: u8, pub microVersion: u32, pub isMirrored: bool, - pub isModel1: bool, - pub index: u8, // = 255; + pub mask: u32, // = 0 + pub data: u32, // = 255 pub bitsIndex: u8, // = 255; } @@ -87,8 +88,8 @@ impl Default for FormatInformation { data_mask: Default::default(), microVersion: 0, isMirrored: false, - isModel1: false, - index: 255, + mask: 0, + data: 255, bitsIndex: 255, } } @@ -106,9 +107,9 @@ impl FormatInformation { error_correction_level: errorCorrectionLevel, data_mask: dataMask, isMirrored: false, - isModel1: false, - index: 255, + mask: 0, bitsIndex: 255, + data: 255, }) } diff --git a/src/qrcode/decoder/mode.rs b/src/qrcode/decoder/mode.rs index 858f4ba..c6cb3ad 100644 --- a/src/qrcode/decoder/mode.rs +++ b/src/qrcode/decoder/mode.rs @@ -123,14 +123,14 @@ impl Mode { } pub const fn get_terminator_bit_length(version: &Version) -> u8 { - (if version.isMicroQRCode() { + (if version.isMicro() { version.getVersionNumber() * 2 + 1 } else { 4 }) as u8 } pub const fn get_codec_mode_bits_length(version: &Version) -> u8 { - (if version.isMicroQRCode() { + (if version.isMicro() { version.getVersionNumber() - 1 } else { 4 @@ -168,7 +168,7 @@ impl Mode { */ pub fn CharacterCountBits(&self, version: &Version) -> u32 { let number = version.getVersionNumber() as usize; - if version.isMicroQRCode() { + if version.isMicro() { match self { Mode::NUMERIC=> return [3, 4, 5, 6][number - 1], Mode::ALPHANUMERIC=> return [3, 4, 5][number - 2], diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index e580a50..59e22af 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -18,6 +18,7 @@ use std::fmt; use crate::{ common::{BitMatrix, Result}, + qrcode::cpp_port::Type, Exceptions, }; @@ -55,8 +56,7 @@ pub struct Version { alignmentPatternCenters: Vec, ecBlocks: Vec, totalCodewords: u32, - pub(crate) is_micro: bool, - pub(crate) is_model1: bool, + pub(crate) qr_type: Type, } impl Version { fn new(versionNumber: u32, alignmentPatternCenters: Vec, ecBlocks: [ECBlocks; 4]) -> Self { @@ -74,8 +74,7 @@ impl Version { alignmentPatternCenters, ecBlocks: ecBlocks.to_vec(), totalCodewords: total, - is_micro: false, - is_model1: false, + qr_type: Type::Model2, } } @@ -94,8 +93,7 @@ impl Version { alignmentPatternCenters: Vec::default(), ecBlocks, totalCodewords: total, - is_micro: true, - is_model1: false, + qr_type: Type::Micro, } } @@ -114,8 +112,7 @@ impl Version { alignmentPatternCenters: Vec::default(), ecBlocks, totalCodewords: total, - is_micro: false, - is_model1: true, + qr_type: Type::Model1, } } @@ -137,7 +134,7 @@ impl Version { } pub fn getDimensionForVersion(&self) -> u32 { - Self::DimensionOfVersion(self.versionNumber, self.is_micro) + Self::DimensionOfVersion(self.versionNumber, self.qr_type == Type::Micro) // 17 + 4 * self.versionNumber } @@ -205,7 +202,7 @@ impl Version { // Top left finder pattern + separator + format bitMatrix.setRegion(0, 0, 9, 9)?; - if !self.is_micro { + if self.qr_type != Type::Micro { // Top right finder pattern + separator + format bitMatrix.setRegion(dimension - 8, 0, 8, 9)?; // Bottom left finder pattern + separator + format From 5e50394ba6ca43ef3ba92d1cdf0cee2bc13e0794 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 17:35:23 -0600 Subject: [PATCH 03/15] chore: clippy cleanups --- .../base_extentions/qr_formatinformation.rs | 3 +-- .../cpp_essentials/base_extentions/qrcode_version.rs | 10 +++++----- src/qrcode/cpp_port/decoder.rs | 2 -- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 143db83..a212329 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -1,8 +1,7 @@ use crate::qrcode::{ cpp_port::Type, decoder::{ - ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_DECODE_LOOKUP, - FORMAT_INFO_MASK_MODEL2, FORMAT_INFO_MASK_QR, + ErrorCorrectionLevel, FormatInformation, FORMAT_INFO_MASK_MODEL2, FORMAT_INFO_MASK_QR, }, }; diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index ca63868..ab779bd 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -14,7 +14,7 @@ use crate::Exceptions; impl Version { pub fn Model1(version_number: u32) -> Result { - if version_number < 1 || version_number > 14 { + if !(1..=14).contains(&version_number) { Err(Exceptions::ILLEGAL_ARGUMENT) } else { Ok(&MODEL1_VERSIONS[version_number as usize - 1]) @@ -22,7 +22,7 @@ impl Version { } pub fn Model2(version_number: u32) -> Result { - if version_number < 1 || version_number > 40 { + if !(1..=40).contains(&version_number) { Err(Exceptions::ILLEGAL_ARGUMENT) } else { Ok(&VERSIONS[version_number as usize - 1]) @@ -30,7 +30,7 @@ impl Version { } pub fn Micro(version_number: u32) -> Result { - if version_number < 1 || version_number > 4 { + if !(1..=4).contains(&version_number) { Err(Exceptions::ILLEGAL_ARGUMENT) } else { Ok(&MICRO_VERSIONS[version_number as usize - 1]) @@ -95,12 +95,12 @@ impl Version { pub fn HasMicroSize(bitMatrix: &BitMatrix) -> bool { let size = bitMatrix.height(); - size >= 11 && size <= 17 && (size % 2) == 1 + (11..=17).contains(&size) && (size % 2) == 1 } pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool { let size = bitMatrix.height(); - Self::HasMicroSize(bitMatrix) || (size >= 21 && size <= 177 && (size % 4) == 1) + Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1) } pub fn Number(bitMatrix: &BitMatrix) -> u32 { diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs index 8b60af5..f9b3c84 100644 --- a/src/qrcode/cpp_port/decoder.rs +++ b/src/qrcode/cpp_port/decoder.rs @@ -17,8 +17,6 @@ use crate::qrcode::cpp_port::bitmatrix_parser::{ use crate::qrcode::decoder::{DataBlock, ErrorCorrectionLevel, Mode, Version}; use crate::Exceptions; -use super::Type; - /** *

Given data and error-correction codewords received, possibly corrupted by errors, attempts to * correct the errors in-place using Reed-Solomon error correction.

From 415001f62297c6ede235eb365b62caaad4db9247 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 17:39:27 -0600 Subject: [PATCH 04/15] perf: QRCode: skip extra version check ported from: https://github.com/zxing-cpp/zxing-cpp/commit/ac88bce74311fdbf376faf0bc88e90d0990d048a The version bits have already been parsed during detection. If they would have been wrong then, we would not have ended up here. If we did, there is no point in reading them again. --- src/qrcode/cpp_port/bitmatrix_parser.rs | 34 +++---------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index 957f35c..393f021 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -29,38 +29,10 @@ pub fn ReadVersion(bitMatrix: &BitMatrix, qr_type: Type) -> Result { let number = Version::Number(bitMatrix); match qr_type { - Type::Model1 => return Version::Model1(number), - - Type::Micro => return Version::Micro(number), - Type::Model2 => {} + Type::Model1 => Version::Model1(number), + Type::Micro => Version::Micro(number), + Type::Model2 => Version::Model2(number), } - let mut version = Version::Model2(number)?; - - if version.getVersionNumber() < 7 { - return Ok(version); - } - - let dimension = bitMatrix.height(); - - for mirror in [false, true] { - // for (bool mirror : {false, true}) { - // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored) - let mut versionBits = 0; - for y in (0..=5).rev() { - // for (int y = 5; y >= 0; --y) { - for x in ((dimension - 11)..=(dimension - 9)).rev() { - // for (int x = dimension - 9; x >= dimension - 11; --x) { - AppendBit(&mut versionBits, getBit(bitMatrix, x, y, Some(mirror))); - } - } - - version = Version::DecodeVersionInformation(versionBits, 0)?; // THIS MIGHT BE WRONG todo!() - if version.getDimensionForVersion() == dimension { - return Ok(version); - } - } - - Err(Exceptions::FORMAT) } pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result { From 86689a87d9d846d465376e2a863985659c016606 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 13:48:13 -0600 Subject: [PATCH 05/15] feat: initial port for Rectangular Micro QR This is the intial port for rMQR support. Directly ported from https://github.com/zxing-cpp/zxing-cpp/commit/7a294f2f3c8c31b8b5c9c62a03bceb9af11dea6f#diff-5d6a0ddd024f3102876492f502a41becde594de81f46d942b87b64cbfdc1985b subsequent improvements will be incorporated in future patches. --- src/common/bit_matrix.rs | 4 +- .../base_extentions/qr_formatinformation.rs | 92 +- .../base_extentions/qrcode_version.rs | 104 +- src/common/eci_string_builder.rs | 2 +- src/common/perspective_transform.rs | 2 +- src/qrcode/cpp_port/bitmatrix_parser.rs | 96 + src/qrcode/cpp_port/decoder.rs | 2 +- src/qrcode/cpp_port/detector.rs | 234 ++- src/qrcode/cpp_port/qr_cpp_reader.rs | 104 +- src/qrcode/cpp_port/qr_type.rs | 1 + .../cpp_port/test/QRFormatInformationTest.rs | 111 +- src/qrcode/cpp_port/test/QRModeTest.rs | 134 +- src/qrcode/cpp_port/test/QRVersionTest.rs | 127 ++ src/qrcode/cpp_port/test/RMQRDecoderTest.rs | 250 +++ src/qrcode/cpp_port/test/mod.rs | 2 + src/qrcode/decoder/format_information.rs | 9 +- src/qrcode/decoder/mod.rs | 1 + src/qrcode/decoder/mode.rs | 72 +- src/qrcode/decoder/version.rs | 1055 +---------- .../decoder/version_build_versions_arrays.rs | 1547 +++++++++++++++++ .../blackbox/cpp/rmqrcode-1/R17x139.png | Bin 0 -> 1113 bytes .../blackbox/cpp/rmqrcode-1/R17x139.txt | 1 + .../cpp/rmqrcode-1/R7x43-H.metadata.txt | 1 + .../blackbox/cpp/rmqrcode-1/R7x43-H.png | Bin 0 -> 186 bytes .../blackbox/cpp/rmqrcode-1/R7x43-H.txt | 1 + .../rmqrcode-1/R7x43-H_inverted.metadata.txt | 1 + .../cpp/rmqrcode-1/R7x43-H_inverted.png | Bin 0 -> 152 bytes .../cpp/rmqrcode-1/R7x43-H_inverted.txt | 1 + tests/cpp_qr_code_blackbox_tests.rs | 15 + 29 files changed, 2876 insertions(+), 1093 deletions(-) create mode 100644 src/qrcode/cpp_port/test/RMQRDecoderTest.rs create mode 100644 src/qrcode/decoder/version_build_versions_arrays.rs create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R17x139.png create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R17x139.txt create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.metadata.txt create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.png create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.txt create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.png create mode 100644 test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.txt diff --git a/src/common/bit_matrix.rs b/src/common/bit_matrix.rs index 01b63c4..9a30749 100644 --- a/src/common/bit_matrix.rs +++ b/src/common/bit_matrix.rs @@ -636,7 +636,7 @@ impl BitMatrix { self.width() } - pub fn width(&self) -> u32 { + pub const fn width(&self) -> u32 { self.width } @@ -647,7 +647,7 @@ impl BitMatrix { self.height() } - pub fn height(&self) -> u32 { + pub const fn height(&self) -> u32 { self.height } diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index a212329..533a6c3 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -7,6 +7,8 @@ use crate::qrcode::{ pub const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825; pub const FORMAT_INFO_MASK_MICRO: u32 = 0x4445; +pub const FORMAT_INFO_MASK_RMQR: u32 = 0x1FAB2; // Finder pattern side +pub const FORMAT_INFO_MASK_RMQR_SUB: u32 = 0x20A7B; // Finder sub pattern side // pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [u32 ;32] = [ // 0x4445, @@ -94,6 +96,33 @@ impl FormatInformation { fi } + /** + * @param formatInfoBits1 format info indicator, with mask still applied + * @param formatInfoBits2 second copy of same info; both are checked at the same time to establish best match + */ + pub fn DecodeRMQR(formatInfoBits1: u32, formatInfoBits2: u32) -> Self { + //FormatInformation fi; + let mirror18Bits = |bits: u32| bits.reverse_bits() >> 14; + let mut fi = if (formatInfoBits2 != 0) { + Self::FindBestFormatInfoRMQR( + &[formatInfoBits1, mirror18Bits(formatInfoBits1)], + &[formatInfoBits2, mirror18Bits(formatInfoBits2)], + ) + } else { + // TODO probably remove if `sampleRMQR()` done properly + Self::FindBestFormatInfoRMQR(&[formatInfoBits1, mirror18Bits(formatInfoBits1)], &[]) + }; + + // Bit 6 is error correction (M/H), and bits 0-5 version. + fi.error_correction_level = + ErrorCorrectionLevel::ECLevelFromBits(((fi.data >> 5) as u8 & 1) << 1, false); // Shift to match QRCode M/H + fi.data_mask = 4; // ((y / 2) + (x / 3)) % 2 == 0 + fi.rMQRVersion = fi.data as u8 & 0x1F; + fi.isMirrored = fi.bitsIndex > 1; + + fi + } + #[inline(always)] pub fn MirrorBits(bits: u32) -> u32 { (bits.reverse_bits()) >> 17 @@ -153,16 +182,73 @@ impl FormatInformation { fi } - pub fn qr_type(&self) -> Type { + pub fn FindBestFormatInfoRMQR(bits: &[u32], subbits: &[u32]) -> Self { + // See ISO/IEC 23941:2022, Annex C, Table C.1 - Valid format information sequences + const MASKED_PATTERNS: [u32; 64] = [ + // Finder pattern side + 0x1FAB2, 0x1E597, 0x1DBDD, 0x1C4F8, 0x1B86C, 0x1A749, 0x19903, 0x18626, 0x17F0E, + 0x1602B, 0x15E61, 0x14144, 0x13DD0, 0x122F5, 0x11CBF, 0x1039A, 0x0F1CA, 0x0EEEF, + 0x0D0A5, 0x0CF80, 0x0B314, 0x0AC31, 0x0927B, 0x08D5E, 0x07476, 0x06B53, 0x05519, + 0x04A3C, 0x036A8, 0x0298D, 0x017C7, 0x008E2, 0x3F367, 0x3EC42, 0x3D208, 0x3CD2D, + 0x3B1B9, 0x3AE9C, 0x390D6, 0x38FF3, 0x376DB, 0x369FE, 0x357B4, 0x34891, 0x33405, + 0x32B20, 0x3156A, 0x30A4F, 0x2F81F, 0x2E73A, 0x2D970, 0x2C655, 0x2BAC1, 0x2A5E4, + 0x29BAE, 0x2848B, 0x27DA3, 0x26286, 0x25CCC, 0x243E9, 0x23F7D, 0x22058, 0x21E12, + 0x20137, + ]; + const MASKED_PATTERNS_SUB: [u32; 64] = [ + // Finder sub pattern side + 0x20A7B, 0x2155E, 0x22B14, 0x23431, 0x248A5, 0x25780, 0x269CA, 0x276EF, 0x28FC7, + 0x290E2, 0x2AEA8, 0x2B18D, 0x2CD19, 0x2D23C, 0x2EC76, 0x2F353, 0x30103, 0x31E26, + 0x3206C, 0x33F49, 0x343DD, 0x35CF8, 0x362B2, 0x37D97, 0x384BF, 0x39B9A, 0x3A5D0, + 0x3BAF5, 0x3C661, 0x3D944, 0x3E70E, 0x3F82B, 0x003AE, 0x01C8B, 0x022C1, 0x03DE4, + 0x04170, 0x05E55, 0x0601F, 0x07F3A, 0x08612, 0x09937, 0x0A77D, 0x0B858, 0x0C4CC, + 0x0DBE9, 0x0E5A3, 0x0FA86, 0x108D6, 0x117F3, 0x129B9, 0x1369C, 0x14A08, 0x1552D, + 0x16B67, 0x17442, 0x18D6A, 0x1924F, 0x1AC05, 0x1B320, 0x1CFB4, 0x1D091, 0x1EEDB, + 0x1F1FE, + ]; + + let mut fi = FormatInformation::default(); + + let mut best = |bits: &[u32], &patterns: &[u32; 64], mask: u32| { + for bitsIndex in 0..bits.len() { + // for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) { + for l_pattern in patterns { + // for (uint32_t pattern : patterns) { + // 'unmask' the pattern first to get the original 6-data bits + 12-ec bits back + let pattern = l_pattern ^ mask; + // Find the pattern with fewest bits differing + let hammingDist = ((bits[bitsIndex] ^ mask) ^ pattern).count_ones(); + if (hammingDist < fi.hammingDistance) { + fi.mask = mask; // store the used mask to discriminate between types/models + fi.data = pattern >> 12; // drop the 12 BCH error correction bits + fi.hammingDistance = hammingDist; + fi.bitsIndex = bitsIndex as u8; + } + } + } + }; + + best(bits, &MASKED_PATTERNS, FORMAT_INFO_MASK_RMQR); + if (!subbits.is_empty()) + // TODO probably remove if `sampleRMQR()` done properly + { + best(subbits, &MASKED_PATTERNS_SUB, FORMAT_INFO_MASK_RMQR_SUB); + } + + fi + } + + pub const fn qr_type(&self) -> Type { match self.mask { FORMAT_INFO_MASK_QR_MODEL1 => Type::Model1, FORMAT_INFO_MASK_MICRO => Type::Micro, + FORMAT_INFO_MASK_RMQR | FORMAT_INFO_MASK_RMQR_SUB => Type::RectMicro, _ => Type::Model2, } } - // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match - pub fn isValid(&self) -> bool { + // Hamming distance of the 32 masked codes is 7 (64 and 8 for rMQR), by construction, so <= 3 bits differing means we found a match + pub const fn isValid(&self) -> bool { self.hammingDistance <= 3 } diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index ab779bd..2ba7059 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -8,9 +8,45 @@ use crate::common::{BitMatrix, Result}; use crate::qrcode::cpp_port::Type; use crate::qrcode::decoder::{ - Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, VERSIONS, VERSION_DECODE_INFO, + Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, RMQR_VERSIONS, VERSIONS, + VERSION_DECODE_INFO, }; -use crate::Exceptions; +use crate::{point, Exceptions, PointI}; + +const dimsVersionRMQR: [PointI; 32] = [ + point(43, 7), + point(59, 7), + point(77, 7), + point(99, 7), + point(139, 7), + point(43, 9), + point(59, 9), + point(77, 9), + point(99, 9), + point(139, 9), + point(27, 11), + point(43, 11), + point(59, 11), + point(77, 11), + point(99, 11), + point(139, 11), + point(27, 13), + point(43, 13), + point(59, 13), + point(77, 13), + point(99, 13), + point(139, 13), + point(43, 15), + point(59, 15), + point(77, 15), + point(99, 15), + point(139, 15), + point(43, 17), + point(59, 17), + point(77, 17), + point(99, 17), + point(139, 17), +]; impl Version { pub fn Model1(version_number: u32) -> Result { @@ -37,11 +73,20 @@ impl Version { } } - pub fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 { + pub fn rMQR(version_number: u32) -> Result { + let version_number = version_number as usize; + if (version_number < 1 || version_number > (RMQR_VERSIONS.len())) { + Err(Exceptions::ILLEGAL_ARGUMENT) + } else { + Ok(&RMQR_VERSIONS[version_number as usize - 1]) + } + } + + pub const fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 { Self::DimensionOffset(is_micro) + Self::DimensionStep(is_micro) * version } - pub fn DimensionOffset(is_micro: bool) -> u32 { + pub const fn DimensionOffset(is_micro: bool) -> u32 { match is_micro { true => 9, false => 17, @@ -49,7 +94,7 @@ impl Version { // return std::array{17, 9}[isMicro]; } - pub fn DimensionStep(is_micro: bool) -> u32 { + pub const fn DimensionStep(is_micro: bool) -> u32 { match is_micro { true => 2, false => 4, @@ -93,22 +138,65 @@ impl Version { Type::const_eq(self.qr_type, Type::Model2) } + pub const fn isRMQR(&self) -> bool { + Type::const_eq(self.qr_type, Type::RectMicro) + } + pub fn HasMicroSize(bitMatrix: &BitMatrix) -> bool { let size = bitMatrix.height(); - (11..=17).contains(&size) && (size % 2) == 1 + size == bitMatrix.width() && size >= 11 && size <= 17 && (size % 2) == 1 + } + + pub fn HasRMQRSize(bitMatrix: &BitMatrix) -> bool { + Self::getVersionRMQR(bitMatrix) != -1 } pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool { let size = bitMatrix.height(); - Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1) + if bitMatrix.width() != size { + Self::HasRMQRSize(bitMatrix) + } else { + Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1) + } } pub fn Number(bitMatrix: &BitMatrix) -> u32 { - if !Self::HasValidSize(bitMatrix) { + if bitMatrix.width() != bitMatrix.height() { + Self::getVersionRMQR(bitMatrix) as u32 + 1 + } else if !Self::HasValidSize(bitMatrix) { 0 } else { let isMicro = Self::HasMicroSize(bitMatrix); (bitMatrix.height() - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro) } } + + pub fn DimensionOfVersionRMQR(version_number: u32) -> PointI { + if version_number < 1 || version_number as usize > dimsVersionRMQR.len() { + point(0, 0) + } else { + dimsVersionRMQR[version_number as usize - 1] + } + } + + fn getVersionRMQR(bitMatrix: &BitMatrix) -> i32 { + let width = bitMatrix.width() as i32; + let height = bitMatrix.height() as i32; + if width != height + && (width & 1 != 0) + && (height & 1 != 0) + && width >= 27 + && width <= 139 + && height >= 7 + && height <= 17 + { + for i in 0..dimsVersionRMQR.len() { + // for (int i = 0; i < Size(dimsVersionRMQR); i++){ + if width == dimsVersionRMQR[i].x && height == dimsVersionRMQR[i].y { + return i as i32; + } + } + } + return -1; + } } diff --git a/src/common/eci_string_builder.rs b/src/common/eci_string_builder.rs index 657906a..031bcff 100644 --- a/src/common/eci_string_builder.rs +++ b/src/common/eci_string_builder.rs @@ -38,7 +38,7 @@ pub struct ECIStringBuilder { pub has_eci: bool, eci_result: Option, bytes: Vec, - eci_positions: Vec<(Eci, usize, usize)>, // (Eci, start, end) + pub(crate) eci_positions: Vec<(Eci, usize, usize)>, // (Eci, start, end) pub symbology: SymbologyIdentifier, eci_list: HashSet, } diff --git a/src/common/perspective_transform.rs b/src/common/perspective_transform.rs index a0a05b5..03695fe 100644 --- a/src/common/perspective_transform.rs +++ b/src/common/perspective_transform.rs @@ -29,7 +29,7 @@ use super::Quadrilateral; * * @author Sean Owen */ -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Default)] pub struct PerspectiveTransform { a11: f32, a12: f32, diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index 393f021..a0c9f31 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -32,6 +32,7 @@ pub fn ReadVersion(bitMatrix: &BitMatrix, qr_type: Type) -> Result { Type::Model1 => Version::Model1(number), Type::Micro => Version::Micro(number), Type::Model2 => Version::Model2(number), + Type::RectMicro => Version::rMQR(number), } } @@ -51,6 +52,47 @@ pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result return Ok(FormatInformation::DecodeMQR(formatInfoBits as u32)); } + if (Version::HasRMQRSize(bitMatrix)) { + // Read top-left format info bits + let mut formatInfoBits1 = 0; + for y in (1..=3).rev() { + // for (int y = 3; y >= 1; y--){ + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, 11, y, None)); + } + for x in (8..=10).rev() { + // for (int x = 10; x >= 8; x--){ + for y in (1..=5).rev() { + // for (int y = 5; y >= 1; y--){ + AppendBit(&mut formatInfoBits1, getBit(bitMatrix, x, y, None)); + } + } + // Read bottom-right format info bits + let mut formatInfoBits2 = 0; + let width = bitMatrix.width(); + let height = bitMatrix.height(); + for x in 3..=5 { + // for (int x = 3; x <= 5; x++){ + AppendBit( + &mut formatInfoBits2, + getBit(bitMatrix, width - x, height - 6, None), + ); + } + for x in 6..=8 { + // for (int x = 6; x <= 8; x++){ + for y in 2..=6 { + // for (int y = 2; y <= 6; y++){ + AppendBit( + &mut formatInfoBits2, + getBit(bitMatrix, width - x, height - y, None), + ); + } + } + return Ok(FormatInformation::DecodeRMQR( + formatInfoBits1 as u32, + formatInfoBits2 as u32, + )); + } + // Read top-left format info bits let mut formatInfoBits1 = 0; for x in 0..6 { @@ -311,6 +353,59 @@ pub fn ReadQRCodewordsModel1( Ok(result.iter().copied().map(|x| x as u8).collect()) } +pub fn ReadRMQRCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + let functionPattern = version.buildFunctionPattern()?; + + let mut result = Vec::with_capacity(version.getTotalCodewords() as usize); + let mut currentByte = 0; + let mut readingUp = true; + let mut bitsRead = 0; + let width = bitMatrix.width(); + let height = bitMatrix.height(); + // Read columns in pairs, from right to left + let mut x = width as i32 - 1 - 1; + while x > 0 { + // for (int x = width - 1 - 1; x > 0; x -= 2) { // Skip right edge alignment + // Read alternatingly from bottom to top then top to bottom + for row in 0..height { + // for (int row = 0; row < height; row++) { + let y = if readingUp { height - 1 - row } else { row }; + for col in 0..2 { + // for (int col = 0; col < 2; col++) { + let xx = x - col; + // Ignore bits covered by the function pattern + if (!functionPattern.get(xx as u32, y)) { + // Read a bit + AppendBit( + &mut currentByte, + GetDataMaskBit(formatInfo.data_mask as u32, xx as u32, y, None)? + != getBit(bitMatrix, xx as u32, y, Some(formatInfo.isMirrored)), + ); + // If we've made a whole byte, save it off + bitsRead += 1; + if bitsRead % 8 == 0 { + // if (++bitsRead % 8 == 0){ + result.push(currentByte); + currentByte = 0; + } + } + } + } + readingUp = !readingUp; // switch directions + + x -= 2 + } + if ((result.len()) != version.getTotalCodewords() as usize) { + return Err(Exceptions::FORMAT); + } + + Ok(result.iter().copied().map(|x| x as u8).collect()) +} + pub fn ReadCodewords( bitMatrix: &BitMatrix, version: VersionRef, @@ -320,5 +415,6 @@ pub fn ReadCodewords( Type::Model1 => ReadQRCodewordsModel1(bitMatrix, version, formatInfo), Type::Model2 => ReadQRCodewords(bitMatrix, version, formatInfo), Type::Micro => ReadMQRCodewords(bitMatrix, version, formatInfo), + Type::RectMicro => ReadRMQRCodewords(bitMatrix, version, formatInfo), } } diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs index f9b3c84..a454902 100644 --- a/src/qrcode/cpp_port/decoder.rs +++ b/src/qrcode/cpp_port/decoder.rs @@ -310,7 +310,7 @@ pub fn DecodeBitStream( } else { Mode::CodecModeForBits( bits.readBits(modeBitLength as usize)?, - Some(version.isMicro()), + Some(version.qr_type), )? }; diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 61f7ea1..a43fb52 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -5,14 +5,15 @@ use crate::{ }, DefaultGridSampler, GridSampler, Result, SamplerControl, }, - point_i, + point, point_i, qrcode::{ decoder::{FormatInformation, Version, VersionRef}, detector::QRCodeDetectorResult, }, - Exceptions, + Exceptions, PointF, }; use multimap::MultiMap; +use num::Integer; use crate::{ common::{ @@ -23,7 +24,7 @@ use crate::{ }, BitMatrix, PerspectiveTransform, Quadrilateral, }, - point_f, Point, + point_f, Point, PointI, }; #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] @@ -39,6 +40,8 @@ pub type FinderPatternSets = Vec; const LEN: usize = 5; const SUM: usize = 7; const PATTERN: FixedPattern = FixedPattern::new([1, 1, 3, 1, 1]); +const SUBPATTERN_RMQR: FixedPattern<5, 5, false> = FixedPattern::new([1, 1, 1, 1, 1]); +const CORNER_EDGE_RMQR: FixedPattern<2, 4, false> = FixedPattern::new([3, 1]); const E2E: bool = true; fn FindPattern(view: PatternView<'_>) -> Result> { @@ -885,8 +888,8 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result { pub fn DetectPureMQR(image: &BitMatrix) -> Result { type Pattern = [PatternType; 5]; - let MIN_MODULES = Version::DimensionOfVersion(1, true); - let MAX_MODULES = Version::DimensionOfVersion(4, true); + const MIN_MODULES: u32 = Version::DimensionOfVersion(1, true); + const MAX_MODULES: u32 = Version::DimensionOfVersion(4, true); let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); @@ -949,6 +952,139 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result { // {{left, top}, {right, top}, {right, bottom}, {left, bottom}}}; } +pub fn DetectPureRMQR(image: &BitMatrix) -> Result { + type Pattern = [PatternType; 5]; //std::array; + type SubPattern = [PatternType; 5]; //std::array; + type CornerEdgePattern = [PatternType; 2]; //std::array; + + // #ifdef PRINT_DEBUG + // SaveAsPBM(image, "weg.pbm"); + // #endif + + const MIN_MODULES: u32 = 7; + const MIN_MODULES_W: u32 = 27; + const MIN_MODULES_H: u32 = 7; + const MAX_MODULES_W: u32 = 139; + const MAX_MODULES_H: u32 = 17; + + let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); + + if (!found) { + return Err(Exceptions::NOT_FOUND); + } + let right = left + width - 1; + let bottom = top + height - 1; + + let tl = point_i(left, top); + let tr = point_i(right, top); + let br = point_i(right, bottom); + let bl = point_i(left, bottom); + + // allow corners be moved one pixel inside to accommodate for possible aliasing artifacts + let diagonal: Pattern = EdgeTracer::new(image, tl, point_i(1, 1)) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; + let diag_hld = diagonal.to_vec().into(); + let view = PatternView::new(&diag_hld); + if !(IsPattern::(&view, &PATTERN, None, 0.0, 0.0) != 0.0) { + return Err(Exceptions::NOT_FOUND); + } + + // Finder sub pattern + let mut subdiagonal: SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; + if (subdiagonal.len() == 5 && subdiagonal[4] > subdiagonal[3]) { + // Sub pattern has no separator so can run off along the diagonal + subdiagonal[4] = subdiagonal[3]; // Hack it back to previous + } + let subdiagonal_hld = subdiagonal.to_vec().into(); + let view = PatternView::new(&subdiagonal_hld); + if !(IsPattern::(&view, &SUBPATTERN_RMQR, None, 0.0, 0.0) != 0.0) { + return Err(Exceptions::NOT_FOUND); + } + + // Horizontal corner finder patterns (for vertical ones see below) + for (p, d) in [(tr, point_i(-1, 0)), (bl, point_i(1, 0))] { + // for (auto [p, d] : {std::pair(tr, PointI{-1, 0}), {bl, {1, 0}}}) { + let corner: CornerEdgePattern = EdgeTracer::new(image, p, d) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; + let corner_hld = corner.to_vec().into(); + let view = PatternView::new(&corner_hld); + if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { + { + return Err(Exceptions::NOT_FOUND); + } + } + } + + let fpWidth = (diagonal).into_iter().sum::(); + let moduleSize = (fpWidth as f32) / 7.0; + let dimW = (width as f32 / moduleSize as f32).floor() as u32; + let dimH = (height as f32 / moduleSize as f32).floor() as u32; + + if (dimW == dimH + || dimW.is_even() + || dimH.is_even() + || dimW < MIN_MODULES_W + || dimW > MAX_MODULES_W + || dimH < MIN_MODULES_H + || dimH > MAX_MODULES_H + || !image.is_in(point_f( + left as f32 + moduleSize / 2.0 + (dimW as f32 - 1.0) * moduleSize, + top as f32 + moduleSize / 2.0 + (dimH as f32 - 1.0) * moduleSize, + ))) + { + return Err(Exceptions::NOT_FOUND); + } + + // Vertical corner finder patterns + if (dimH > 7) { + // None for R7 + let corner: CornerEdgePattern = EdgeTracer::new(image, tr, point_i(0, 1)) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; + let corner_hld = corner.to_vec().into(); + let view = PatternView::new(&corner_hld); + if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { + return Err(Exceptions::NOT_FOUND); + } + if (dimH > 9) { + // No bottom left for R9 + let corner: CornerEdgePattern = EdgeTracer::new(image, bl, point_i(0, -1)) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; + let corner_hld = corner.to_vec().into(); + let view = PatternView::new(&corner_hld); + if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { + return Err(Exceptions::NOT_FOUND); + } + } + } + + // #ifdef PRINT_DEBUG + // LogMatrix log; + // LogMatrixWriter lmw(log, image, 5, "grid2.pnm"); + // for (int y = 0; y < dimH; y++) + // for (int x = 0; x < dimW; x++) + // log(PointF(left + (x + .5f) * moduleSize, top + (y + .5f) * moduleSize)); + // #endif + + // Now just read off the bits (this is a crop + subsample) + Ok(QRCodeDetectorResult::new( + image.Deflate( + dimW, + dimH, + top as f32 + moduleSize / 2.0, + left as f32 + moduleSize / 2.0, + moduleSize, + )?, + vec![tl, tr, br, bl], + )) + // return {Deflate(image, dimW, dimH, top + moduleSize / 2, left + moduleSize / 2, moduleSize), {tl, tr, br, bl}}; +} + pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result { let Some(fpQuad) = FindConcentricPatternCorners(image, fp.p, fp.size, 2) else { return Err(Exceptions::NOT_FOUND); @@ -1057,3 +1193,91 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result { + // TODO proper + let Some(fpQuad) = FindConcentricPatternCorners(image, fp.p, fp.size, 2) else { + return Err(Exceptions::NOT_FOUND); + }; + + let srcQuad = Quadrilateral::rectangle(7, 7, Some(0.5)); + + let FORMAT_INFO_EDGE_COORDS: [Point; 4] = + [point_i(8, 0), point_i(9, 0), point_i(10, 0), point_i(11, 0)]; + let FORMAT_INFO_COORDS: [Point; 18] = [ + point_i(8, 1), + point_i(8, 2), + point_i(8, 3), + point_i(8, 4), + point_i(8, 5), + point_i(9, 1), + point_i(9, 2), + point_i(9, 3), + point_i(9, 4), + point_i(9, 5), + point_i(10, 1), + point_i(10, 2), + point_i(10, 3), + point_i(10, 4), + point_i(10, 5), + point_i(11, 1), + point_i(11, 2), + point_i(11, 3), + ]; + + let mut bestFI: FormatInformation = FormatInformation::default(); + let mut bestPT: PerspectiveTransform = PerspectiveTransform::default(); + + for i in 0..4 { + // for (int i = 0; i < 4; ++i) { + let mod2Pix = PerspectiveTransform::quadrilateralToQuadrilateral( + srcQuad, + fpQuad.rotated_corners(Some(i), None), + )?; + + let check = |i: usize, on: bool| { + let p = mod2Pix.transform_point(Point::centered(FORMAT_INFO_EDGE_COORDS[i])); + image.is_in(p) && image.get_point(p) == on + }; + + // check that we see top edge timing pattern modules + if (!check(0, true) || !check(1, false) || !check(2, true) || !check(3, false)) { + continue; + } + + let mut formatInfoBits = 0; + for i in 0..FORMAT_INFO_COORDS.len() { + // for (int i = 0; i < Size(FORMAT_INFO_COORDS); ++i) + AppendBit( + &mut formatInfoBits, + image.get_point(mod2Pix.transform_point(Point::centered(FORMAT_INFO_COORDS[i]))), + ); + } + + let fi = FormatInformation::DecodeRMQR(formatInfoBits as u32, 0 /*formatInfoBits2*/); + if (fi.hammingDistance < bestFI.hammingDistance) { + bestFI = fi; + bestPT = mod2Pix; + } + } + + if (!bestFI.isValid()) { + return Err(Exceptions::NOT_FOUND); + } + + let dim = Version::DimensionOfVersionRMQR(bestFI.rMQRVersion as u32 + 1); + + let grid_sampler = DefaultGridSampler; + let (sample, rps) = grid_sampler.sample_grid( + image, + dim.x as u32, + dim.y as u32, + &[SamplerControl { + p1: point_i(dim.x, dim.y), + p0: point_i(0, 0), + transform: bestPT, + }], + )?; + Ok(QRCodeDetectorResult::new(sample, rps.to_vec())) + // SampleGrid(image, dim.x, dim.y, bestPT) +} diff --git a/src/qrcode/cpp_port/qr_cpp_reader.rs b/src/qrcode/cpp_port/qr_cpp_reader.rs index 6d711a2..d1bab45 100644 --- a/src/qrcode/cpp_port/qr_cpp_reader.rs +++ b/src/qrcode/cpp_port/qr_cpp_reader.rs @@ -127,8 +127,8 @@ use crate::{ use super::{ decoder::Decode, detector::{ - DetectPureMQR, DetectPureQR, FindFinderPatterns, GenerateFinderPatternSets, SampleMQR, - SampleQR, + DetectPureMQR, DetectPureQR, DetectPureRMQR, FindFinderPatterns, GenerateFinderPatternSets, + SampleMQR, SampleQR, SampleRMQR, }, }; @@ -179,10 +179,15 @@ impl Reader for QrReader { if formats.contains(&BarcodeFormat::MICRO_QR_CODE) && detectorResult.is_err() { detectorResult = DetectPureMQR(binImg); } + if formats.contains(&BarcodeFormat::RECTANGULAR_MICRO_QR_CODE) + && detectorResult.is_err() + { + detectorResult = DetectPureRMQR(binImg); + } } if detectorResult.is_err() { - for decode_function in [DetectPureQR, DetectPureMQR] { + for decode_function in [DetectPureQR, DetectPureMQR, DetectPureRMQR] { detectorResult = decode_function(binImg); if detectorResult.is_ok() { break; @@ -208,25 +213,16 @@ impl Reader for QrReader { Ok(RXingResult::with_decoder_result( decoderResult, position, - if detectorResult.getBits().width() < 21 { - BarcodeFormat::MICRO_QR_CODE + if detectorResult.getBits().width() != detectorResult.getBits().height() { + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE } else { - BarcodeFormat::QR_CODE + if detectorResult.getBits().width() < 21 { + BarcodeFormat::MICRO_QR_CODE + } else { + BarcodeFormat::QR_CODE + } }, )) - - // Ok(RXingResult::new( - // &decoderResult.content().to_string(), - // decoderResult.content().bytes().to_vec(), - // position.to_vec(), - // if detectorResult.getBits().width() < 21 { - // BarcodeFormat::MICRO_QR_CODE - // } else { - // BarcodeFormat::QR_CODE - // }, - // )) - // return Result(std::move(decoderResult), std::move(position), - // detectorResult.bits().width() < 21 ? BarcodeFormat::MICRO_QR_CODE : BarcodeFormat::QR_CODE); } } @@ -276,16 +272,18 @@ impl QrReader { let mut usedFPs: Vec = Vec::new(); let mut results: Vec = Vec::new(); - let (check_qr, check_mqr) = if let Some(DecodeHintValue::PossibleFormats(formats)) = - hints.get(&DecodeHintType::POSSIBLE_FORMATS) - { - ( - formats.contains(&BarcodeFormat::QR_CODE), - formats.contains(&BarcodeFormat::MICRO_QR_CODE), - ) - } else { - (true, true) - }; + let (check_qr, check_mqr, check_rmqr) = + if let Some(DecodeHintValue::PossibleFormats(formats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + ( + formats.contains(&BarcodeFormat::QR_CODE), + formats.contains(&BarcodeFormat::MICRO_QR_CODE), + formats.contains(&BarcodeFormat::RECTANGULAR_MICRO_QR_CODE), + ) + } else { + (true, true, true) + }; if check_qr { // if (_hints.hasFormat(BarcodeFormat::QRCode)) { @@ -314,18 +312,11 @@ impl QrReader { } if decoderResult.isValid() { - // results.push(RXingResult::new( - // &decoderResult.content().to_string(), - // decoderResult.content().bytes().to_vec(), - // position.to_vec(), - // BarcodeFormat::QR_CODE, - // )); results.push(RXingResult::with_decoder_result( decoderResult, position, BarcodeFormat::QR_CODE, )); - // results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QR_CODE); if maxSymbols != 0 && (results.len() as u32) == maxSymbols { break; @@ -337,13 +328,13 @@ impl QrReader { } if check_mqr && !(maxSymbols != 0 && (results.len() as u32) == maxSymbols) { // if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) { - for fp in allFPs { + for fp in &allFPs { // for (const auto& fp : allFPs) { - if usedFPs.contains(&fp) { + if usedFPs.contains(fp) { continue; } - let detectorResult = SampleMQR(binImg, fp); + let detectorResult = SampleMQR(binImg, *fp); if let Ok(detectorResult) = detectorResult { // if (detectorResult.is_ok()) { let decoderResult = Decode(detectorResult.getBits()); @@ -355,13 +346,34 @@ impl QrReader { position, BarcodeFormat::MICRO_QR_CODE, )); - // results.push(RXingResult::new( - // &decoderResult.content().to_string(), - // decoderResult.content().bytes().to_vec(), - // position.to_vec(), - // BarcodeFormat::MICRO_QR_CODE, - // )); - // results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MICRO_QR_CODE); + + if maxSymbols != 0 && (results.len() as u32) == maxSymbols { + break; + } + } + } + } + } + } + if check_rmqr && !(maxSymbols != 0 && (results.len() as u32) == maxSymbols) { + for fp in &allFPs { + // for (const auto& fp : allFPs) { + if usedFPs.contains(fp) { + continue; + } + + let detectorResult = SampleRMQR(binImg, *fp); + if let Ok(detectorResult) = detectorResult { + // if (detectorResult.is_ok()) { + let decoderResult = Decode(detectorResult.getBits()); + let position = detectorResult.getPoints(); + if let Ok(decoderResult) = decoderResult { + if decoderResult.isValid() { + results.push(RXingResult::with_decoder_result( + decoderResult, + position, + BarcodeFormat::RECTANGULAR_MICRO_QR_CODE, + )); if maxSymbols != 0 && (results.len() as u32) == maxSymbols { break; diff --git a/src/qrcode/cpp_port/qr_type.rs b/src/qrcode/cpp_port/qr_type.rs index 610d741..7988e16 100644 --- a/src/qrcode/cpp_port/qr_type.rs +++ b/src/qrcode/cpp_port/qr_type.rs @@ -3,6 +3,7 @@ pub enum Type { Model1, Model2, Micro, + RectMicro, } impl Type { diff --git a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs index 352329c..3177acc 100644 --- a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs +++ b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs @@ -15,6 +15,13 @@ const MASKED_TEST_FORMAT_INFO2: u32 = const UNMASKED_TEST_FORMAT_INFO: u32 = MASKED_TEST_FORMAT_INFO ^ 0x5412; const MICRO_MASKED_TEST_FORMAT_INFO: u32 = 0x3BBA; // const MICRO_UNMASKED_TEST_FORMAT_INFO: u32 = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445; +const RMQR_MASKED_TEST_FORMAT_INFO: u32 = 0x20137; +const RMQR_MASKED_TEST_FORMAT_INFO_SUB: u32 = 0x1F1FE; + +const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825; +const FORMAT_INFO_MASK_MICRO: u32 = 0x4445; +const FORMAT_INFO_MASK_RMQR: u32 = 0x1FAB2; // Finder pattern side +const FORMAT_INFO_MASK_RMQR_SUB: u32 = 0x20A7B; // Finder sub pattern side fn DoFormatInformationTest(formatInfo: u32, expectedMask: u8, expectedECL: ErrorCorrectionLevel) { let parsedFormat = FormatInformation::DecodeMQR(formatInfo); @@ -23,6 +30,27 @@ fn DoFormatInformationTest(formatInfo: u32, expectedMask: u8, expectedECL: Error assert_eq!(expectedECL, parsedFormat.error_correction_level); } +// Helper for rMQR to unset `numBits` number of bits +fn RMQRUnsetBits(formatInfoBits: u32, numBits: u32) -> u32 { + let mut formatInfoBits = formatInfoBits; + let mut numBits = numBits as i32; + for i in 0..18 { + // for (int i = 0; i < 18 && numBits; i++) { + if (formatInfoBits & (1 << i)) != 0 { + formatInfoBits ^= 1 << i; + numBits -= 1; + } + if numBits == 0 { + break; + } + } + formatInfoBits +} + +fn cpp_eq(rhs: &FormatInformation, lhs: &FormatInformation) { + assert!(rhs.cpp_eq(lhs)) +} + #[test] fn Decode() { // Normal case @@ -131,6 +159,85 @@ fn DecodeMicroWithBitDifference() { // FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).errorCorrectionLevel()); } -fn cpp_eq(rhs: &FormatInformation, lhs: &FormatInformation) { - assert!(rhs.cpp_eq(lhs)) +#[test] +fn DecodeRMQR() { + // Normal case + let expected = FormatInformation::DecodeRMQR( + RMQR_MASKED_TEST_FORMAT_INFO, + RMQR_MASKED_TEST_FORMAT_INFO_SUB, + ); + assert!(expected.isValid()); + assert_eq!(4, expected.data_mask); + assert_eq!(ErrorCorrectionLevel::H, expected.error_correction_level); + assert_eq!(FORMAT_INFO_MASK_RMQR, expected.mask); + // Not catered for: where the code forgot the mask! +} + +#[test] +fn DecodeRMQRWithBitDifference() { + let expected = FormatInformation::DecodeRMQR( + RMQR_MASKED_TEST_FORMAT_INFO, + RMQR_MASKED_TEST_FORMAT_INFO_SUB, + ); + assert_eq!(expected.error_correction_level, ErrorCorrectionLevel::H); + // 1,2,3,4,5 bits difference + cpp_eq( + &expected, + &FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 1), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 1), + ), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 2), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 2), + ), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 3), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 3), + ), + ); + cpp_eq( + &expected, + &FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 4), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4), + ), + ); + let unexpected = FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 5), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 5), + ); + assert!(!(expected == unexpected)); + assert!(!(unexpected.isValid())); + assert!(unexpected.qr_type() == Type::RectMicro); // Note `mask` (used to determine type) set regardless +} + +#[test] +fn DecodeRMQRWithMisread() { + let expected = FormatInformation::DecodeRMQR( + RMQR_MASKED_TEST_FORMAT_INFO, + RMQR_MASKED_TEST_FORMAT_INFO_SUB, + ); + { + let actual = FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 2), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4), + ); + cpp_eq(&expected, &actual); + assert_eq!(actual.mask, FORMAT_INFO_MASK_RMQR); + } + { + let actual = FormatInformation::DecodeRMQR( + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO, 5), + RMQRUnsetBits(RMQR_MASKED_TEST_FORMAT_INFO_SUB, 4), + ); + cpp_eq(&expected, &actual); + assert_eq!(actual.mask, FORMAT_INFO_MASK_RMQR_SUB); + } } diff --git a/src/qrcode/cpp_port/test/QRModeTest.rs b/src/qrcode/cpp_port/test/QRModeTest.rs index 324f06a..52ccb7c 100644 --- a/src/qrcode/cpp_port/test/QRModeTest.rs +++ b/src/qrcode/cpp_port/test/QRModeTest.rs @@ -4,22 +4,34 @@ */ // SPDX-License-Identifier: Apache-2.0 -use crate::qrcode::decoder::{Mode, Version}; +use crate::qrcode::{ + cpp_port::Type, + decoder::{Mode, Version}, +}; #[test] fn ForBits() { assert_eq!( Mode::TERMINATOR, - Mode::CodecModeForBits(0x00, None).unwrap() + Mode::CodecModeForBits(0x00, Some(Type::Model2)).unwrap() + ); + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x01, Some(Type::Model2)).unwrap() ); - assert_eq!(Mode::NUMERIC, Mode::CodecModeForBits(0x01, None).unwrap()); assert_eq!( Mode::ALPHANUMERIC, - Mode::CodecModeForBits(0x02, None).unwrap() + Mode::CodecModeForBits(0x02, Some(Type::Model2)).unwrap() ); - assert_eq!(Mode::BYTE, Mode::CodecModeForBits(0x04, None).unwrap()); - assert_eq!(Mode::KANJI, Mode::CodecModeForBits(0x08, None).unwrap()); - assert!(Mode::CodecModeForBits(0x10, None).is_err()); + assert_eq!( + Mode::BYTE, + Mode::CodecModeForBits(0x04, Some(Type::Model2)).unwrap() + ); + assert_eq!( + Mode::KANJI, + Mode::CodecModeForBits(0x08, Some(Type::Model2)).unwrap() + ); + assert!(Mode::CodecModeForBits(0x10, Some(Type::Model2)).is_err()); } #[test] @@ -56,53 +68,53 @@ fn MicroForBits() { // M1 assert_eq!( Mode::NUMERIC, - Mode::CodecModeForBits(0x00, Some(true)).unwrap() + Mode::CodecModeForBits(0x00, Some(Type::Micro)).unwrap() ); // M2 assert_eq!( Mode::NUMERIC, - Mode::CodecModeForBits(0x00, Some(true)).unwrap() + Mode::CodecModeForBits(0x00, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::ALPHANUMERIC, - Mode::CodecModeForBits(0x01, Some(true)).unwrap() + Mode::CodecModeForBits(0x01, Some(Type::Micro)).unwrap() ); // M3 assert_eq!( Mode::NUMERIC, - Mode::CodecModeForBits(0x00, Some(true)).unwrap() + Mode::CodecModeForBits(0x00, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::ALPHANUMERIC, - Mode::CodecModeForBits(0x01, Some(true)).unwrap() + Mode::CodecModeForBits(0x01, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::BYTE, - Mode::CodecModeForBits(0x02, Some(true)).unwrap() + Mode::CodecModeForBits(0x02, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::KANJI, - Mode::CodecModeForBits(0x03, Some(true)).unwrap() + Mode::CodecModeForBits(0x03, Some(Type::Micro)).unwrap() ); // M4 assert_eq!( Mode::NUMERIC, - Mode::CodecModeForBits(0x00, Some(true)).unwrap() + Mode::CodecModeForBits(0x00, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::ALPHANUMERIC, - Mode::CodecModeForBits(0x01, Some(true)).unwrap() + Mode::CodecModeForBits(0x01, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::BYTE, - Mode::CodecModeForBits(0x02, Some(true)).unwrap() + Mode::CodecModeForBits(0x02, Some(Type::Micro)).unwrap() ); assert_eq!( Mode::KANJI, - Mode::CodecModeForBits(0x03, Some(true)).unwrap() + Mode::CodecModeForBits(0x03, Some(Type::Micro)).unwrap() ); - assert!(Mode::CodecModeForBits(0x04, Some(true)).is_err()); + assert!(Mode::CodecModeForBits(0x04, Some(Type::Micro)).is_err()); } #[test] @@ -133,3 +145,87 @@ fn MicroCharacterCount() { Mode::CharacterCountBits(&Mode::KANJI, Version::Micro(4).unwrap()) ); } + +#[test] +fn RMQRForBits() { + assert_eq!( + Mode::TERMINATOR, + Mode::CodecModeForBits(0x00, Some(Type::RectMicro)).expect("could not decode 0x00") + ); + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x01, Some(Type::RectMicro)).expect("could not decode 0x01") + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x02, Some(Type::RectMicro)).expect("could not decode 0x02") + ); + assert_eq!( + Mode::BYTE, + Mode::CodecModeForBits(0x03, Some(Type::RectMicro)).expect("could not decode 0x03") + ); + assert_eq!( + Mode::KANJI, + Mode::CodecModeForBits(0x04, Some(Type::RectMicro)).expect("could not decode 0x04") + ); + assert_eq!( + Mode::FNC1_FIRST_POSITION, + Mode::CodecModeForBits(0x05, Some(Type::RectMicro)).expect("could not decode 0x05") + ); + assert_eq!( + Mode::FNC1_SECOND_POSITION, + Mode::CodecModeForBits(0x06, Some(Type::RectMicro)).expect("could not decode 0x06") + ); + assert_eq!( + Mode::ECI, + Mode::CodecModeForBits(0x07, Some(Type::RectMicro)).expect("could not decode 0x07") + ); + assert!(Mode::CodecModeForBits(0x08, Some(Type::RectMicro)).is_err()); +} + +#[test] +fn RMQRCharacterCount() { + // Spot check a few values + assert_eq!( + 7, + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::rMQR(5).expect("should return version") + ) + ); + assert_eq!( + 8, + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::rMQR(26).expect("should return version") + ) + ); + assert_eq!( + 9, + Mode::CharacterCountBits( + &Mode::NUMERIC, + Version::rMQR(32).expect("should return version") + ) + ); + assert_eq!( + 5, + Mode::CharacterCountBits( + &Mode::ALPHANUMERIC, + Version::rMQR(6).expect("should return version") + ) + ); + assert_eq!( + 5, + Mode::CharacterCountBits( + &Mode::BYTE, + Version::rMQR(7).expect("should return version") + ) + ); + assert_eq!( + 5, + Mode::CharacterCountBits( + &Mode::KANJI, + Version::rMQR(8).expect("should return version") + ) + ); +} diff --git a/src/qrcode/cpp_port/test/QRVersionTest.rs b/src/qrcode/cpp_port/test/QRVersionTest.rs index d5d0b1e..b2214d3 100644 --- a/src/qrcode/cpp_port/test/QRVersionTest.rs +++ b/src/qrcode/cpp_port/test/QRVersionTest.rs @@ -125,3 +125,130 @@ fn FunctionPattern() { } } } + +fn CheckRMQRVersion(version: VersionRef, number: u32) { + assert_eq!(number, version.getVersionNumber()); + assert_eq!( + Version::DimensionOfVersionRMQR(number).x == 27, + version.getAlignmentPatternCenters().is_empty() + ); +} + +#[test] +fn RMQRVersionForNumber() { + let version = Version::rMQR(0); + assert!(version.is_err(), "There is version with number 0"); + + for i in 1..=32 { + // for (int i = 1; i <= 32; i++) { + CheckRMQRVersion(Version::rMQR(i).expect("version {i} should exist"), i); + } +} + +#[test] +fn RMQRFunctionPattern1() { + { + let expected = BitMatrix::parse_strings( + r"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXX XXX XXXXXXXX +XXXXXXXXXXXX XXX XXXXXXXX +XXXXXXXXXXXX X XXXXXXXX +XXXXXXXXXXX XXX XXXXXXXX +XXXXXXXXXXX XXX XXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +", + "X", + " ", + ) + .unwrap(); + let version = Version::rMQR(1).unwrap(); // R7x43 + let functionPattern = version.buildFunctionPattern().unwrap(); + assert_eq!(expected, functionPattern); + } + { + let expected = BitMatrix::parse_strings( + r"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXX XXX XX +XXXXXXXXXXXX XXX X +XXXXXXXXXXXX X XXXXXX X +XXXXXXXXXXX X XXXXXXXX +XXXXXXXXXXX X XXXXXXXX +XXXXXXXX XXX XXXXXXXX +XXXXXXXX XXX XXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +", + "X", + " ", + ) + .unwrap(); + let version = Version::rMQR(6).unwrap(); // R9x43 + let functionPattern = version.buildFunctionPattern().unwrap(); + assert_eq!(expected, functionPattern); + } + { + let expected = BitMatrix::parse_strings( + r"XXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXX XX +XXXXXXXXXXXX X +XXXXXXXXXXXX X +XXXXXXXXXXX X +XXXXXXXXXXX XXXXXX X +XXXXXXXX XXXXXXXX +XXXXXXXX XXXXXXXX +X XXXXXXXX +XX XXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXX +", + "X", + " ", + ) + .unwrap(); + let version = Version::rMQR(11).unwrap(); // R11x27 + let functionPattern = version.buildFunctionPattern().unwrap(); + assert_eq!(expected, functionPattern); + } + { + let expected = BitMatrix::parse_strings( + r"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXX XXX XX +XXXXXXXXXXXX XXX X +XXXXXXXXXXXX X X +XXXXXXXXXXX X X +XXXXXXXXXXX X XXXXXX X +XXXXXXXX X XXXXXXXX +XXXXXXXX X XXXXXXXX +X XXX XXXXXXXX +XX XXX XXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +", + "X", + " ", + ) + .unwrap(); + let version = Version::rMQR(12).unwrap(); // R11x43 + let functionPattern = version.buildFunctionPattern().unwrap(); + assert_eq!(expected, functionPattern); + } + { + let expected = BitMatrix::parse_strings( + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXXXXXX XXX XXX XX +XXXXXXXXXXXX XXX XXX X +XXXXXXXXXXXX X X X +XXXXXXXXXXX X X X +XXXXXXXXXXX X X XXXXXX X +XXXXXXXX X X XXXXXXXX +XXXXXXXX X X XXXXXXXX +X XXX XXX XXXXXXXX +XX XXX XXX XXXXXXXX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +", + "X", + " ", + ) + .unwrap(); + let version = Version::rMQR(13).unwrap(); // R11x59 + let functionPattern = version.buildFunctionPattern().unwrap(); + assert_eq!(expected, functionPattern); + } +} diff --git a/src/qrcode/cpp_port/test/RMQRDecoderTest.rs b/src/qrcode/cpp_port/test/RMQRDecoderTest.rs new file mode 100644 index 0000000..13e12ab --- /dev/null +++ b/src/qrcode/cpp_port/test/RMQRDecoderTest.rs @@ -0,0 +1,250 @@ +/* + * Copyright 2023 gitlost +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{BitMatrix, Eci}, + qrcode::cpp_port::decoder::Decode, + Exceptions, +}; + +#[test] +fn RMQRCodeR7x43M() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X X XXX X X X X X X X X XXX +X X X XXX XXXXX XXX X X XX X X +X XXX X X XXX X X X XXXX XXXX X X XXXXXXXX +X XXX X XX XXXXX XXXXXX X X X X +X XXX X XX XXX XXXXXXX X X XX X X X +X X XXXXX XXX XXX XXXXX XXXXXX X X +XXXXXXX X X X X X X XXX X X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + + assert_eq!(result.text(), "ABCDEFG"); +} + +#[test] +fn RMQRCodeR7x43MError6Bits() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X X XXX X X X X X X X X XXX +X X X XXX XXXXX XXX X X XX X X +X XXX X X XXX X X XXXX XXXX XX X XXXXXXXX +X XXX X XX XXXXX X XXXXXX X X X X +X XXX X XX XXX XXXXXXX X X XXX X X X +X X XXXXX XXX XXX XXXX X XXXXXX X X +XXXXXXX X X X X X X XXX X X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix); + + assert!(matches!( + result.err(), + Some(Exceptions::ReedSolomonException(_)) + )); + // assert_eq!(Error::Checksum, result.error()); + // assert!(result.text().empty()); + // assert!(result.content().text(TextMode::Plain).empty()); +} + +#[test] +fn RMQRCodeR7x139H() { + let bitMatrix = BitMatrix::parse_strings( +r"XXXXXXX X X X X X X X X X XXX X X X X X X X X X X X X XXX X X X X X X X X X X X X XXX X X X X X X X X X X X X XXX X X X X X X X X X X X XXX +X X XX XXX X X X X X XX XX X X X XXX XX XXXX XXX XX XX XX X XX X X X XXX X XX XX XX X X XX X XX XXXX X X X +X XXX X X XXXXX X XXXXX X X XXX XX X XXX X XX XXX XX X XXX X X XXXX X XXXXXXX X XX XXX X X X XXX X XXXXX +X XXX X XXXX X XX X X XX XX X XX XX X XXX XX X XX X XX X X XX X X XXX X X X X X X X XX X XX XX X X X +X XXX X XXXX XXXXX X X XXXXXX XX X XXXX X XXXX X XXX XXXX X XXXXXXX XXX XXXXXX X X XX X XXX X XXXXXXXXX X XXXX X X X X X +X X X XX XX X X XX X X X XXXX X X X XX X XXX X X X X X XXX XX XXX X X XX XXXX XX X X X X XXXXX XXX XX X XX X +XXXXXXX X X X X X X X X X XXX X X X X X X X X X X X X XXX X X X X X X X X X X X X XXX X X X X X X X X X X X X XXX X X X X X X X X X X XXXXX +", +"X", +" ", +) +.unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + assert_eq!(result.text(), "1234567890,ABCDEFGHIJKLMOPQRSTUVW"); +} + +#[test] +fn RMQRCodeR9x59H() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X XXX X X X X X X X X XXX X X X X X X X XXX +X X X XXXXX XXX X X XXXXXXXX X X X X XXXX X X +X XXX X XX XXX X XXX XXXX X XXXXXXX X XXXXX X X +X XXX X XXXX X XX X XX XXXX XX XX X X X XXX X +X XXX X X X XX XXXXXX X X XX X XX X X XXXX XXXXX +X X X X X X XXX X X X XX X XXXX XX X X X X +XXXXXXX XXXXX XXXXXX X XX XXX X XXXX X X X XX X X + XXX XXXX XX XXX X XXXXXXX X XX XXX XX XX X +XXX X X X X X X X XXX X X X X X X X X XXX X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + assert_eq!(result.text(), "ABCDEFGHIJKLMN"); +} + +#[test] +fn RMQRCodeR9x77M() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X X X X XXX X X X X X X X X X X X XXX X X X X X X X X X X XXX +X X XXX XX XXX XXX XXXX XXX XX X XXXXXXXXX X XXX XXXX X XXXX XX XXX X +X XXX X X X X XXX X XXXX XX XX X XX XX XXX XXXX X X XX X X XX X +X XXX X X X XXXXXX X XX XXXX X XXX X XX X XX XX XX X XXX X X XXX XX +X XXX X XXXX X X XXXX XXXX XX XXX X XX XXXXXX X X XXX XX XXXXX +X X X X XX XXX X X XX X X XX XXX X X X X X XX XXXXX X +XXXXXXX X XX XX X XXXX X X X X X XX XXX X XX X XXX XX X X + X XXXXX XX X XXXXXX XX XXXXX X XX XX XXXXX XXX X +XXX X X X X X X X X X X XXX X X X X X X X X X X X XXX X X X X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + assert_eq!(result.text(), "__ABCDEFGH__1234567890___ABCDEFGHIJK"); +} + +#[test] +fn RMQRCodeR11x27H() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X X X X XXX +X X XX X X X X +X XXX X X XX X X XX +X XXX X XXXX XX X XXXXXX +X XXX X X X XX XX XXX X +X X XXX X XX XXXX X +XXXXXXX X XX X XXXXX + X X X X X +XXXX X X X XX XXXXXX X X +X XX XXXXXX XXX XXXX X X +XXX X X X X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + assert_eq!(result.text(), "ABCDEF"); +} + +#[test] +fn RMQRCodeR13x27M_ECI() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X X X X XXX +X X XX XX XXX XX X +X XXX X XX X XX XX XXX X +X XXX X XX X XX X X XX +X XXX X XXXXXXX X X XX +X X XX X XXX XX XX +XXXXXXX X X X X XXX + XXX XX X XX XXX +XXX XX XX X X XX XX XXXXX + XXX X X X X X X +X XX X X XX X XX X X X X +X X X X X X X X X +XXX X X X X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + assert_eq!(result.text(), "AB貫12345AB"); + assert!(result.content().has_eci); + assert_eq!(result.content().eci_positions[0].0, Eci::Shift_JIS); + // assert_eq!(result.symbologyIdentifier(), "]Q2"); // Shouldn't this be Q1 TODO: Fix +} + +#[test] +fn RMQRCodeR15x59H_GS1() { + let bitMatrix = BitMatrix::parse_strings( + r"XXXXXXX X X X X X XXX X X X X X X X X XXX X X X X X X X XXX +X X XXX XXX X XXXXX XX XXX X X X X X X XXX X +X XXX X XXX XX X XXX XXX X X XXX XXXXX XX XXX XX +X XXX X X X XX X X XXX X X X XXXXX XX XXX +X XXX X XX XXX XX X X X XX XX XX XXX XXXX X XXXX +X X X X X X X XXX XXX XXXX X XXX XX X X +XXXXXXX X XXX XXXX X XX XXXX X X XX XXX XXXXX X + X XXX X XXXXX X XX XXXX XX X +XX XX X X X XXXXX XX X X XX XX X XX X X XX X + XX XX X XXXXXX XXX XX X X XX XXX X X XXX +X X XX XXXXXXXXXX XX X X XX XX XX X XXXX XX XXXXXX + XX X XX X XXX X X X XXX X XXX X X XXX XXXX X +XXXX X X XX XXX X X X XX XXXXX XX X XX XXX X X +X X X XX XXX XXXXXXX XXX X XXX XX X X X XX X +XXX X X X X X X X XXX X X X X X X X X XXX X X X X X X XXXXX +", + "X", + " ", + ) + .unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + // assert!(result.content().type() == ContentType::GS1); + // assert_eq!( + // result.text(), + // "(01)09524000059109(21)12345678p901(10)1234567p(17)231120" + // ); + // TODO: Right now we aren't properly handling the parsing of this, but we can just check that + // the data comes back as valid, it's not perfect, but it works ok. +} + +#[test] +fn RMQRCodeR17x99H() { + let bitMatrix = BitMatrix::parse_strings( +r"XXXXXXX X X X X X X X XXX X X X X X X X X X X X XXX X X X X X X X X X X X XXX X X X X X X X X X XXX +X X X XXXXX XXX X X X XX X X XX XXXXX X XX X XX XXX X X XX X X XXX X X XX X X X +X XXX X X X XXX XXX X XXX XXX X X XX XXXX X X X X XXX XXXXX X X XX X XX X X +X XXX X XX X XX X X XX X XXXX X XXXXX X X XX X XXX XX X X X X XXXXXX X +X XXX X X XX X X X X X X X X XXX XX XXXXXX X X XXX X XXXXXX X X X X X X X XX X +X X XX X X XXXXX XX X XXX X XX X X XXX X XXX XXX X XXXX XX X X X XX XXXX +XXXXXXX X XX X XX X X XXX XX X XXXX X X XXX X X XX X XXXX XX X X X XX X XXXX + XX XX XX XX X XX X X X XXX XX X X XXX XXXX XX X X X X XX XX XXX +XX X XXX X X XXXX XXX XXXXX XXX XXX X X X X X XXX X XX XX X X X X XX X XXX + X XXXXX X X XXXXX X XX X XX XXXX X X XXXXX X XX X XX X XX X XX XX +X XX XX X XX XXX XX XXXXXX X XXXXX XX XXXX X X X X XXXX XX X X XXXXXX XX X X + XXX XX XXX XX XX X X X XX X X X X XX XXX XXXX X XX XXX X X X XXXX XXXXX X XXX +X X XX X XX XX XX X X XX X X X XX XXXXXXXX X XX XX X X X X X XX X X XXXXXXXXXXX + X X X XX X X X XX XXXX X XXX X XX X X X X X XXX XXXXX XX X X X XXXXX X X X +XXXX XX XX X XXXX XXXX X XX X XX XX XX XXXX XXX X X XX XX X XXXX X XXX XX X XX X X +X XXX XX XXX X X X XXX X XXX X XXXX XX X X XXXXX X XX X X X X X X X X XXXX XXXX X +XXX X X X X X X X X X XXX X X X X X X X X X X X XXX X X X X X X X X X X X XXX X X X X X X X X XXXXX +", +"X", +" ", +) +.unwrap(); + + let result = Decode(&bitMatrix).unwrap(); + // assert!(result.isValid()); + assert_eq!( + result.text(), + "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890________________________" + ); +} diff --git a/src/qrcode/cpp_port/test/mod.rs b/src/qrcode/cpp_port/test/mod.rs index 4dd3017..4df88c0 100644 --- a/src/qrcode/cpp_port/test/mod.rs +++ b/src/qrcode/cpp_port/test/mod.rs @@ -12,3 +12,5 @@ mod QRDataMaskTest; mod QRBitMatrixParserTest; mod MQRDecoderTest; + +mod RMQRDecoderTest; diff --git a/src/qrcode/decoder/format_information.rs b/src/qrcode/decoder/format_information.rs index 4591cc9..879edd0 100644 --- a/src/qrcode/decoder/format_information.rs +++ b/src/qrcode/decoder/format_information.rs @@ -75,9 +75,10 @@ pub struct FormatInformation { pub microVersion: u32, pub isMirrored: bool, - pub mask: u32, // = 0 - pub data: u32, // = 255 - pub bitsIndex: u8, // = 255; + pub mask: u32, // = 0 + pub data: u32, // = 255 + pub bitsIndex: u8, // = 255; + pub rMQRVersion: u8, //= 0; } impl Default for FormatInformation { @@ -91,6 +92,7 @@ impl Default for FormatInformation { mask: 0, data: 255, bitsIndex: 255, + rMQRVersion: 0, } } } @@ -110,6 +112,7 @@ impl FormatInformation { mask: 0, bitsIndex: 255, data: 255, + rMQRVersion: 0, }) } diff --git a/src/qrcode/decoder/mod.rs b/src/qrcode/decoder/mod.rs index 835abd7..2e3285a 100644 --- a/src/qrcode/decoder/mod.rs +++ b/src/qrcode/decoder/mod.rs @@ -8,6 +8,7 @@ mod mode; mod qr_code_decoder_meta_data; pub mod qrcode_decoder; mod version; +mod version_build_versions_arrays; #[cfg(test)] mod DecodedBitStreamParserTestCase; diff --git a/src/qrcode/decoder/mode.rs b/src/qrcode/decoder/mode.rs index c6cb3ad..7c6f032 100644 --- a/src/qrcode/decoder/mode.rs +++ b/src/qrcode/decoder/mode.rs @@ -15,6 +15,7 @@ */ use crate::common::Result; +use crate::qrcode::cpp_port::Type; use crate::Exceptions; use super::Version; @@ -82,6 +83,7 @@ impl Mode { */ pub fn getCharacterCountBits(&self, version: &Version) -> u8 { let number = version.getVersionNumber(); + let offset = if number <= 9 { 0 } else if number <= 26 { @@ -122,18 +124,19 @@ impl Mode { } } - pub const fn get_terminator_bit_length(version: &Version) -> u8 { + pub fn get_terminator_bit_length(version: &Version) -> u8 { (if version.isMicro() { version.getVersionNumber() * 2 + 1 } else { - 4 + 4 - u32::from(version.isRMQR()) }) as u8 } - pub const fn get_codec_mode_bits_length(version: &Version) -> u8 { + + pub fn get_codec_mode_bits_length(version: &Version) -> u8 { (if version.isMicro() { version.getVersionNumber() - 1 } else { - 4 + 4 - u32::from(version.isRMQR()) }) as u8 } /** @@ -142,19 +145,33 @@ impl Mode { * @return Mode encoded by these bits * @throws FormatError if bits do not correspond to a known mode */ - pub fn CodecModeForBits(bits: u32, isMicro: Option) -> Result { - let isMicro = isMicro.unwrap_or(false); - const BITS_2_MODE_LEN: usize = 4; + pub fn CodecModeForBits(bits: u32, qr_type: Option) -> Result { + let qr_type = qr_type.unwrap_or(Type::Model2); + let bits = bits as usize; - if !isMicro { - if (0x00..=0x05).contains(&bits) || (0x07..=0x09).contains(&bits) || bits == 0x0d { - return Mode::try_from(bits); + if (qr_type == Type::Micro) { + const Bits2Mode: [Mode; 4] = + [Mode::NUMERIC, Mode::ALPHANUMERIC, Mode::BYTE, Mode::KANJI]; + if (bits < (Bits2Mode.len())) { + return Ok(Bits2Mode[bits]); + } + } else if (qr_type == Type::RectMicro) { + const Bits2Mode: [Mode; 8] = [ + Mode::TERMINATOR, + Mode::NUMERIC, + Mode::ALPHANUMERIC, + Mode::BYTE, + Mode::KANJI, + Mode::FNC1_FIRST_POSITION, + Mode::FNC1_SECOND_POSITION, + Mode::ECI, + ]; + if (bits < (Bits2Mode.len())) { + return Ok(Bits2Mode[bits]); } } else { - const BITS_2_MODE: [Mode; BITS_2_MODE_LEN] = - [Mode::NUMERIC, Mode::ALPHANUMERIC, Mode::BYTE, Mode::KANJI]; - if (bits as usize) < BITS_2_MODE_LEN { - return Ok(BITS_2_MODE[bits as usize]); + if ((bits >= 0x00 && bits <= 0x05) || (bits >= 0x07 && bits <= 0x09) || bits == 0x0d) { + return Mode::try_from(bits as u32); } } @@ -179,6 +196,33 @@ impl Mode { } } + if (version.isRMQR()) { + // See ISO/IEC 23941:2022 7.4.1, Table 3 - Number of bits of character count indicator + const numeric: [u32; 32] = [ + 4, 5, 6, 7, 7, 5, 6, 7, 7, 8, 4, 6, 7, 7, 8, 8, 5, 6, 7, 7, 8, 8, 7, 7, 8, 8, 9, 7, + 8, 8, 8, 9, + ]; + const alphanum: [u32; 32] = [ + 3, 5, 5, 6, 6, 5, 5, 6, 6, 7, 4, 5, 6, 6, 7, 7, 5, 6, 6, 7, 7, 8, 6, 7, 7, 7, 8, 6, + 7, 7, 8, 8, + ]; + const byte: [u32; 32] = [ + 3, 4, 5, 5, 6, 4, 5, 5, 6, 6, 3, 5, 5, 6, 6, 7, 4, 5, 6, 6, 7, 7, 6, 6, 7, 7, 7, 6, + 6, 7, 7, 8, + ]; + const kanji: [u32; 32] = [ + 2, 3, 4, 5, 5, 3, 4, 5, 5, 6, 2, 4, 5, 5, 6, 6, 3, 5, 5, 6, 6, 7, 5, 5, 6, 6, 7, 5, + 6, 6, 6, 7, + ]; + match (self) { + Mode::NUMERIC => return numeric[number - 1], + Mode::ALPHANUMERIC => return alphanum[number - 1], + Mode::BYTE => return byte[number - 1], + Mode::KANJI => return kanji[number - 1], + _ => return 0, + } + } + let i = if number <= 9 { 0 } else if number <= 26 { diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index 59e22af..1b0a8ff 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -31,6 +31,7 @@ pub type VersionRef = &'static Version; pub static VERSIONS: Lazy> = Lazy::new(Version::buildVersions); pub static MICRO_VERSIONS: Lazy> = Lazy::new(Version::build_micro_versions); pub static MODEL1_VERSIONS: Lazy> = Lazy::new(Version::build_model1_versions); +pub static RMQR_VERSIONS: Lazy> = Lazy::new(Version::build_rmqr_versions); /** * See ISO 18004:2006 Annex D. @@ -59,14 +60,19 @@ pub struct Version { pub(crate) qr_type: Type, } impl Version { - fn new(versionNumber: u32, alignmentPatternCenters: Vec, ecBlocks: [ECBlocks; 4]) -> Self { + pub(super) fn new( + versionNumber: u32, + alignmentPatternCenters: Vec, + ecBlocks: [ECBlocks; 4], + ) -> Self { let mut total = 0; - let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); - let ecbArray = ecBlocks[0].getECBlocks(); - let mut i = 0; - while i < ecbArray.len() { - total += ecbArray[i].getCount() * (ecbArray[i].getDataCodewords() + ecCodewords); - i += 1; + let ecCodewords = ecBlocks[1].getECCodewordsPerBlock(); + let ecbArray = ecBlocks[1].getECBlocks(); + // let mut i = 0; + for ecb in ecbArray { + // while i < ecbArray.len() { + total += ecb.getCount() * (ecb.getDataCodewords() + ecCodewords); + // i += 1; } Self { @@ -74,11 +80,15 @@ impl Version { alignmentPatternCenters, ecBlocks: ecBlocks.to_vec(), totalCodewords: total, - qr_type: Type::Model2, + qr_type: if ecBlocks[0].getECCodewordsPerBlock() != 0 { + Type::Model2 + } else { + Type::RectMicro + }, } } - fn new_micro(versionNumber: u32, ecBlocks: Vec) -> Self { + pub(super) fn new_micro(versionNumber: u32, ecBlocks: Vec) -> Self { let mut total = 0; let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); let ecbArray = ecBlocks[0].getECBlocks(); @@ -97,7 +107,7 @@ impl Version { } } - fn new_model1(versionNumber: u32, ecBlocks: Vec) -> Self { + pub(super) fn new_model1(versionNumber: u32, ecBlocks: Vec) -> Self { let mut total = 0; let ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); let ecbArray = ecBlocks[0].getECBlocks(); @@ -116,11 +126,6 @@ impl Version { } } - // #[inline(always)] - // pub(crate) const fn isMicro(ecBlocks: &[ECBlocks; 4]) -> bool { - // ecBlocks[0].ecCodewordsPerBlock < 7 || ecBlocks[0].ecCodewordsPerBlock == 8 - // } - pub const fn getVersionNumber(&self) -> u32 { self.versionNumber } @@ -196,6 +201,53 @@ impl Version { * See ISO 18004:2006 Annex E */ pub fn buildFunctionPattern(&self) -> Result { + if (self.isRMQR()) { + let dimension = Version::DimensionOfVersionRMQR(self.versionNumber); + let mut bitMatrix = BitMatrix::new(dimension.x as u32, dimension.y as u32)?; + + // Set edge timing patterns + bitMatrix.setRegion(0, 0, dimension.x as u32, 1)?; // Top + bitMatrix.setRegion(0, (dimension.y - 1) as u32, dimension.x as u32, 1)?; // Bottom + bitMatrix.setRegion(0, 1, 1, (dimension.y - 2) as u32)?; // Left + bitMatrix.setRegion((dimension.x - 1) as u32, 1, 1, (dimension.y - 2) as u32)?; // Right + + // Set vertical timing and alignment patterns + let max = self.alignmentPatternCenters.len(); // Same as vertical timing column + for x in 0..max { + // for (size_t x = 0; x < max; ++x) { + let cx = self.alignmentPatternCenters[x]; + bitMatrix.setRegion(cx - 1, 1, 3, 2)?; // Top alignment pattern + bitMatrix.setRegion(cx - 1, (dimension.y - 3) as u32, 3, 2)?; // Bottom alignment pattern + bitMatrix.setRegion(cx, 3, 1, (dimension.y - 6) as u32)?; // Vertical timing pattern + } + + // Top left finder pattern + separator + bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(dimension.y == 7))?; // R7 finder bottom flush with edge + // Top left format + bitMatrix.setRegion(8, 1, 3, 5)?; + bitMatrix.setRegion(11, 1, 1, 3)?; + + // Bottom right finder subpattern + bitMatrix.setRegion( + (dimension.x - 5) as u32, + (dimension.y - 5) as u32, + 5 - 1, + 5 - 1, + )?; + // Bottom right format + bitMatrix.setRegion((dimension.x - 8) as u32, (dimension.y - 6) as u32, 3, 5)?; + bitMatrix.setRegion((dimension.x - 5) as u32, (dimension.y - 6) as u32, 3, 1)?; + + // Top right corner finder + bitMatrix.set((dimension.x - 2) as u32, 1); + if (dimension.y > 9) { + // Bottom left corner finder + bitMatrix.set(1, (dimension.y - 2) as u32); + } + + return Ok(bitMatrix); + } + let dimension = self.getDimensionForVersion(); let mut bitMatrix = BitMatrix::with_single_dimension(dimension)?; @@ -241,979 +293,6 @@ impl Version { Ok(bitMatrix) } - - pub fn build_micro_versions() -> Vec { - vec![ - Version::new_micro(1, vec![ECBlocks::new(2, vec![ECB::new(1, 3)])]), - Version::new_micro( - 2, - vec![ - ECBlocks::new(5, vec![ECB::new(1, 5)]), - ECBlocks::new(6, vec![ECB::new(1, 4)]), - ], - ), - Version::new_micro( - 3, - vec![ - ECBlocks::new(6, vec![ECB::new(1, 11)]), - ECBlocks::new(8, vec![ECB::new(1, 9)]), - ], - ), - Version::new_micro( - 4, - vec![ - ECBlocks::new(8, vec![ECB::new(1, 16)]), - ECBlocks::new(10, vec![ECB::new(1, 14)]), - ECBlocks::new(14, vec![ECB::new(1, 10)]), - ], - ), - ] - // static const Version allVersions[] = { - // {1, {2, 1, 3, 0, 0}}, - // {2, {5, 1, 5, 0, 0, 6, 1, 4, 0, 0}}, - // {3, {6, 1, 11, 0, 0, 8, 1, 9, 0, 0}}, - // {4, {8, 1, 16, 0, 0, 10, 1, 14, 0, 0, 14, 1, 10, 0, 0}}}; - } - - /** - * See ISO 18004:2006 6.5.1 Table 9 - */ - pub fn buildVersions() -> Vec { - Vec::from([ - Version::new( - 1, - Vec::from([]), - [ - ECBlocks::new(7, Vec::from([ECB::new(1, 19)])), - ECBlocks::new(10, Vec::from([ECB::new(1, 16)])), - ECBlocks::new(13, Vec::from([ECB::new(1, 13)])), - ECBlocks::new(17, Vec::from([ECB::new(1, 9)])), - ], - ), - Version::new( - 2, - Vec::from([6, 18]), - [ - ECBlocks::new(10, Vec::from([ECB::new(1, 34)])), - ECBlocks::new(16, Vec::from([ECB::new(1, 28)])), - ECBlocks::new(22, Vec::from([ECB::new(1, 22)])), - ECBlocks::new(28, Vec::from([ECB::new(1, 16)])), - ], - ), - Version::new( - 3, - Vec::from([6, 22]), - [ - ECBlocks::new(15, Vec::from([ECB::new(1, 55)])), - ECBlocks::new(26, Vec::from([ECB::new(1, 44)])), - ECBlocks::new(18, Vec::from([ECB::new(2, 17)])), - ECBlocks::new(22, Vec::from([ECB::new(2, 13)])), - ], - ), - Version::new( - 4, - Vec::from([6, 26]), - [ - ECBlocks::new(20, Vec::from([ECB::new(1, 80)])), - ECBlocks::new(18, Vec::from([ECB::new(2, 32)])), - ECBlocks::new(26, Vec::from([ECB::new(2, 24)])), - ECBlocks::new(16, Vec::from([ECB::new(4, 9)])), - ], - ), - Version::new( - 5, - Vec::from([6, 30]), - [ - ECBlocks::new(26, Vec::from([ECB::new(1, 108)])), - ECBlocks::new(24, Vec::from([ECB::new(2, 43)])), - ECBlocks::new(18, Vec::from([ECB::new(2, 15), ECB::new(2, 16)])), - ECBlocks::new(22, Vec::from([ECB::new(2, 11), ECB::new(2, 12)])), - ], - ), - Version::new( - 6, - Vec::from([6, 34]), - [ - ECBlocks::new(18, Vec::from([ECB::new(2, 68)])), - ECBlocks::new(16, Vec::from([ECB::new(4, 27)])), - ECBlocks::new(24, Vec::from([ECB::new(4, 19)])), - ECBlocks::new(28, Vec::from([ECB::new(4, 15)])), - ], - ), - Version::new( - 7, - Vec::from([6, 22, 38]), - [ - ECBlocks::new(20, Vec::from([ECB::new(2, 78)])), - ECBlocks::new(18, Vec::from([ECB::new(4, 31)])), - ECBlocks::new(18, Vec::from([ECB::new(2, 14), ECB::new(4, 15)])), - ECBlocks::new(26, Vec::from([ECB::new(4, 13), ECB::new(1, 14)])), - ], - ), - Version::new( - 8, - Vec::from([6, 24, 42]), - [ - ECBlocks::new(24, Vec::from([ECB::new(2, 97)])), - ECBlocks::new(22, Vec::from([ECB::new(2, 38), ECB::new(2, 39)])), - ECBlocks::new(22, Vec::from([ECB::new(4, 18), ECB::new(2, 19)])), - ECBlocks::new(26, Vec::from([ECB::new(4, 14), ECB::new(2, 15)])), - ], - ), - Version::new( - 9, - Vec::from([6, 26, 46]), - [ - ECBlocks::new(30, Vec::from([ECB::new(2, 116)])), - ECBlocks::new(22, Vec::from([ECB::new(3, 36), ECB::new(2, 37)])), - ECBlocks::new(20, Vec::from([ECB::new(4, 16), ECB::new(4, 17)])), - ECBlocks::new(24, Vec::from([ECB::new(4, 12), ECB::new(4, 13)])), - ], - ), - Version::new( - 10, - Vec::from([6, 28, 50]), - [ - ECBlocks::new(18, Vec::from([ECB::new(2, 68), ECB::new(2, 69)])), - ECBlocks::new(26, Vec::from([ECB::new(4, 43), ECB::new(1, 44)])), - ECBlocks::new(24, Vec::from([ECB::new(6, 19), ECB::new(2, 20)])), - ECBlocks::new(28, Vec::from([ECB::new(6, 15), ECB::new(2, 16)])), - ], - ), - Version::new( - 11, - Vec::from([6, 30, 54]), - [ - ECBlocks::new(20, Vec::from([ECB::new(4, 81)])), - ECBlocks::new(30, Vec::from([ECB::new(1, 50), ECB::new(4, 51)])), - ECBlocks::new(28, Vec::from([ECB::new(4, 22), ECB::new(4, 23)])), - ECBlocks::new(24, Vec::from([ECB::new(3, 12), ECB::new(8, 13)])), - ], - ), - Version::new( - 12, - Vec::from([6, 32, 58]), - [ - ECBlocks::new(24, Vec::from([ECB::new(2, 92), ECB::new(2, 93)])), - ECBlocks::new(22, Vec::from([ECB::new(6, 36), ECB::new(2, 37)])), - ECBlocks::new(26, Vec::from([ECB::new(4, 20), ECB::new(6, 21)])), - ECBlocks::new(28, Vec::from([ECB::new(7, 14), ECB::new(4, 15)])), - ], - ), - Version::new( - 13, - Vec::from([6, 34, 62]), - [ - ECBlocks::new(26, Vec::from([ECB::new(4, 107)])), - ECBlocks::new(22, Vec::from([ECB::new(8, 37), ECB::new(1, 38)])), - ECBlocks::new(24, Vec::from([ECB::new(8, 20), ECB::new(4, 21)])), - ECBlocks::new(22, Vec::from([ECB::new(12, 11), ECB::new(4, 12)])), - ], - ), - Version::new( - 14, - Vec::from([6, 26, 46, 66]), - [ - ECBlocks::new(30, Vec::from([ECB::new(3, 115), ECB::new(1, 116)])), - ECBlocks::new(24, Vec::from([ECB::new(4, 40), ECB::new(5, 41)])), - ECBlocks::new(20, Vec::from([ECB::new(11, 16), ECB::new(5, 17)])), - ECBlocks::new(24, Vec::from([ECB::new(11, 12), ECB::new(5, 13)])), - ], - ), - Version::new( - 15, - Vec::from([6, 26, 48, 70]), - [ - ECBlocks::new(22, Vec::from([ECB::new(5, 87), ECB::new(1, 88)])), - ECBlocks::new(24, Vec::from([ECB::new(5, 41), ECB::new(5, 42)])), - ECBlocks::new(30, Vec::from([ECB::new(5, 24), ECB::new(7, 25)])), - ECBlocks::new(24, Vec::from([ECB::new(11, 12), ECB::new(7, 13)])), - ], - ), - Version::new( - 16, - Vec::from([6, 26, 50, 74]), - [ - ECBlocks::new(24, Vec::from([ECB::new(5, 98), ECB::new(1, 99)])), - ECBlocks::new(28, Vec::from([ECB::new(7, 45), ECB::new(3, 46)])), - ECBlocks::new(24, Vec::from([ECB::new(15, 19), ECB::new(2, 20)])), - ECBlocks::new(30, Vec::from([ECB::new(3, 15), ECB::new(13, 16)])), - ], - ), - Version::new( - 17, - Vec::from([6, 30, 54, 78]), - [ - ECBlocks::new(28, Vec::from([ECB::new(1, 107), ECB::new(5, 108)])), - ECBlocks::new(28, Vec::from([ECB::new(10, 46), ECB::new(1, 47)])), - ECBlocks::new(28, Vec::from([ECB::new(1, 22), ECB::new(15, 23)])), - ECBlocks::new(28, Vec::from([ECB::new(2, 14), ECB::new(17, 15)])), - ], - ), - Version::new( - 18, - Vec::from([6, 30, 56, 82]), - [ - ECBlocks::new(30, Vec::from([ECB::new(5, 120), ECB::new(1, 121)])), - ECBlocks::new(26, Vec::from([ECB::new(9, 43), ECB::new(4, 44)])), - ECBlocks::new(28, Vec::from([ECB::new(17, 22), ECB::new(1, 23)])), - ECBlocks::new(28, Vec::from([ECB::new(2, 14), ECB::new(19, 15)])), - ], - ), - Version::new( - 19, - Vec::from([6, 30, 58, 86]), - [ - ECBlocks::new(28, Vec::from([ECB::new(3, 113), ECB::new(4, 114)])), - ECBlocks::new(26, Vec::from([ECB::new(3, 44), ECB::new(11, 45)])), - ECBlocks::new(26, Vec::from([ECB::new(17, 21), ECB::new(4, 22)])), - ECBlocks::new(26, Vec::from([ECB::new(9, 13), ECB::new(16, 14)])), - ], - ), - Version::new( - 20, - Vec::from([6, 34, 62, 90]), - [ - ECBlocks::new(28, Vec::from([ECB::new(3, 107), ECB::new(5, 108)])), - ECBlocks::new(26, Vec::from([ECB::new(3, 41), ECB::new(13, 42)])), - ECBlocks::new(30, Vec::from([ECB::new(15, 24), ECB::new(5, 25)])), - ECBlocks::new(28, Vec::from([ECB::new(15, 15), ECB::new(10, 16)])), - ], - ), - Version::new( - 21, - Vec::from([6, 28, 50, 72, 94]), - [ - ECBlocks::new(28, Vec::from([ECB::new(4, 116), ECB::new(4, 117)])), - ECBlocks::new(26, Vec::from([ECB::new(17, 42)])), - ECBlocks::new(28, Vec::from([ECB::new(17, 22), ECB::new(6, 23)])), - ECBlocks::new(30, Vec::from([ECB::new(19, 16), ECB::new(6, 17)])), - ], - ), - Version::new( - 22, - Vec::from([6, 26, 50, 74, 98]), - [ - ECBlocks::new(28, Vec::from([ECB::new(2, 111), ECB::new(7, 112)])), - ECBlocks::new(28, Vec::from([ECB::new(17, 46)])), - ECBlocks::new(30, Vec::from([ECB::new(7, 24), ECB::new(16, 25)])), - ECBlocks::new(24, Vec::from([ECB::new(34, 13)])), - ], - ), - Version::new( - 23, - Vec::from([6, 30, 54, 78, 102]), - [ - ECBlocks::new(30, Vec::from([ECB::new(4, 121), ECB::new(5, 122)])), - ECBlocks::new(28, Vec::from([ECB::new(4, 47), ECB::new(14, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(11, 24), ECB::new(14, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(16, 15), ECB::new(14, 16)])), - ], - ), - Version::new( - 24, - Vec::from([6, 28, 54, 80, 106]), - [ - ECBlocks::new(30, Vec::from([ECB::new(6, 117), ECB::new(4, 118)])), - ECBlocks::new(28, Vec::from([ECB::new(6, 45), ECB::new(14, 46)])), - ECBlocks::new(30, Vec::from([ECB::new(11, 24), ECB::new(16, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(30, 16), ECB::new(2, 17)])), - ], - ), - Version::new( - 25, - Vec::from([6, 32, 58, 84, 110]), - [ - ECBlocks::new(26, Vec::from([ECB::new(8, 106), ECB::new(4, 107)])), - ECBlocks::new(28, Vec::from([ECB::new(8, 47), ECB::new(13, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(7, 24), ECB::new(22, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(22, 15), ECB::new(13, 16)])), - ], - ), - Version::new( - 26, - Vec::from([6, 30, 58, 86, 114]), - [ - ECBlocks::new(28, Vec::from([ECB::new(10, 114), ECB::new(2, 115)])), - ECBlocks::new(28, Vec::from([ECB::new(19, 46), ECB::new(4, 47)])), - ECBlocks::new(28, Vec::from([ECB::new(28, 22), ECB::new(6, 23)])), - ECBlocks::new(30, Vec::from([ECB::new(33, 16), ECB::new(4, 17)])), - ], - ), - Version::new( - 27, - Vec::from([6, 34, 62, 90, 118]), - [ - ECBlocks::new(30, Vec::from([ECB::new(8, 122), ECB::new(4, 123)])), - ECBlocks::new(28, Vec::from([ECB::new(22, 45), ECB::new(3, 46)])), - ECBlocks::new(30, Vec::from([ECB::new(8, 23), ECB::new(26, 24)])), - ECBlocks::new(30, Vec::from([ECB::new(12, 15), ECB::new(28, 16)])), - ], - ), - Version::new( - 28, - Vec::from([6, 26, 50, 74, 98, 122]), - [ - ECBlocks::new(30, Vec::from([ECB::new(3, 117), ECB::new(10, 118)])), - ECBlocks::new(28, Vec::from([ECB::new(3, 45), ECB::new(23, 46)])), - ECBlocks::new(30, Vec::from([ECB::new(4, 24), ECB::new(31, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(11, 15), ECB::new(31, 16)])), - ], - ), - Version::new( - 29, - Vec::from([6, 30, 54, 78, 102, 126]), - [ - ECBlocks::new(30, Vec::from([ECB::new(7, 116), ECB::new(7, 117)])), - ECBlocks::new(28, Vec::from([ECB::new(21, 45), ECB::new(7, 46)])), - ECBlocks::new(30, Vec::from([ECB::new(1, 23), ECB::new(37, 24)])), - ECBlocks::new(30, Vec::from([ECB::new(19, 15), ECB::new(26, 16)])), - ], - ), - Version::new( - 30, - Vec::from([6, 26, 52, 78, 104, 130]), - [ - ECBlocks::new(30, Vec::from([ECB::new(5, 115), ECB::new(10, 116)])), - ECBlocks::new(28, Vec::from([ECB::new(19, 47), ECB::new(10, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(15, 24), ECB::new(25, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(23, 15), ECB::new(25, 16)])), - ], - ), - Version::new( - 31, - Vec::from([6, 30, 56, 82, 108, 134]), - [ - ECBlocks::new(30, Vec::from([ECB::new(13, 115), ECB::new(3, 116)])), - ECBlocks::new(28, Vec::from([ECB::new(2, 46), ECB::new(29, 47)])), - ECBlocks::new(30, Vec::from([ECB::new(42, 24), ECB::new(1, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(23, 15), ECB::new(28, 16)])), - ], - ), - Version::new( - 32, - Vec::from([6, 34, 60, 86, 112, 138]), - [ - ECBlocks::new(30, Vec::from([ECB::new(17, 115)])), - ECBlocks::new(28, Vec::from([ECB::new(10, 46), ECB::new(23, 47)])), - ECBlocks::new(30, Vec::from([ECB::new(10, 24), ECB::new(35, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(19, 15), ECB::new(35, 16)])), - ], - ), - Version::new( - 33, - Vec::from([6, 30, 58, 86, 114, 142]), - [ - ECBlocks::new(30, Vec::from([ECB::new(17, 115), ECB::new(1, 116)])), - ECBlocks::new(28, Vec::from([ECB::new(14, 46), ECB::new(21, 47)])), - ECBlocks::new(30, Vec::from([ECB::new(29, 24), ECB::new(19, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(11, 15), ECB::new(46, 16)])), - ], - ), - Version::new( - 34, - Vec::from([6, 34, 62, 90, 118, 146]), - [ - ECBlocks::new(30, Vec::from([ECB::new(13, 115), ECB::new(6, 116)])), - ECBlocks::new(28, Vec::from([ECB::new(14, 46), ECB::new(23, 47)])), - ECBlocks::new(30, Vec::from([ECB::new(44, 24), ECB::new(7, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(59, 16), ECB::new(1, 17)])), - ], - ), - Version::new( - 35, - Vec::from([6, 30, 54, 78, 102, 126, 150]), - [ - ECBlocks::new(30, Vec::from([ECB::new(12, 121), ECB::new(7, 122)])), - ECBlocks::new(28, Vec::from([ECB::new(12, 47), ECB::new(26, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(39, 24), ECB::new(14, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(22, 15), ECB::new(41, 16)])), - ], - ), - Version::new( - 36, - Vec::from([6, 24, 50, 76, 102, 128, 154]), - [ - ECBlocks::new(30, Vec::from([ECB::new(6, 121), ECB::new(14, 122)])), - ECBlocks::new(28, Vec::from([ECB::new(6, 47), ECB::new(34, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(46, 24), ECB::new(10, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(2, 15), ECB::new(64, 16)])), - ], - ), - Version::new( - 37, - Vec::from([6, 28, 54, 80, 106, 132, 158]), - [ - ECBlocks::new(30, Vec::from([ECB::new(17, 122), ECB::new(4, 123)])), - ECBlocks::new(28, Vec::from([ECB::new(29, 46), ECB::new(14, 47)])), - ECBlocks::new(30, Vec::from([ECB::new(49, 24), ECB::new(10, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(24, 15), ECB::new(46, 16)])), - ], - ), - Version::new( - 38, - Vec::from([6, 32, 58, 84, 110, 136, 162]), - [ - ECBlocks::new(30, Vec::from([ECB::new(4, 122), ECB::new(18, 123)])), - ECBlocks::new(28, Vec::from([ECB::new(13, 46), ECB::new(32, 47)])), - ECBlocks::new(30, Vec::from([ECB::new(48, 24), ECB::new(14, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(42, 15), ECB::new(32, 16)])), - ], - ), - Version::new( - 39, - Vec::from([6, 26, 54, 82, 110, 138, 166]), - [ - ECBlocks::new(30, Vec::from([ECB::new(20, 117), ECB::new(4, 118)])), - ECBlocks::new(28, Vec::from([ECB::new(40, 47), ECB::new(7, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(43, 24), ECB::new(22, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(10, 15), ECB::new(67, 16)])), - ], - ), - Version::new( - 40, - Vec::from([6, 30, 58, 86, 114, 142, 170]), - [ - ECBlocks::new(30, Vec::from([ECB::new(19, 118), ECB::new(6, 119)])), - ECBlocks::new(28, Vec::from([ECB::new(18, 47), ECB::new(31, 48)])), - ECBlocks::new(30, Vec::from([ECB::new(34, 24), ECB::new(34, 25)])), - ECBlocks::new(30, Vec::from([ECB::new(20, 15), ECB::new(61, 16)])), - ], - ), - ]) /* - new Version(4, new int[]{6, 26}, - new ECBlocks(20, new ECB::new(1, 80)), - new ECBlocks(18, new ECB::new(2, 32)), - new ECBlocks(26, new ECB::new(2, 24)), - new ECBlocks(16, new ECB::new(4, 9))), - new Version(5, new int[]{6, 30}, - new ECBlocks(26, new ECB::new(1, 108)), - new ECBlocks(24, new ECB::new(2, 43)), - new ECBlocks(18, new ECB::new(2, 15), - new ECB::new(2, 16)), - new ECBlocks(22, new ECB::new(2, 11), - new ECB::new(2, 12))), - new Version(6, new int[]{6, 34}, - new ECBlocks(18, new ECB::new(2, 68)), - new ECBlocks(16, new ECB::new(4, 27)), - new ECBlocks(24, new ECB::new(4, 19)), - new ECBlocks(28, new ECB::new(4, 15))), - new Version(7, new int[]{6, 22, 38}, - new ECBlocks(20, new ECB::new(2, 78)), - new ECBlocks(18, new ECB::new(4, 31)), - new ECBlocks(18, new ECB::new(2, 14), - new ECB::new(4, 15)), - new ECBlocks(26, new ECB::new(4, 13), - new ECB::new(1, 14))), - new Version(8, new int[]{6, 24, 42}, - new ECBlocks(24, new ECB::new(2, 97)), - new ECBlocks(22, new ECB::new(2, 38), - new ECB::new(2, 39)), - new ECBlocks(22, new ECB::new(4, 18), - new ECB::new(2, 19)), - new ECBlocks(26, new ECB::new(4, 14), - new ECB::new(2, 15))), - new Version(9, new int[]{6, 26, 46}, - new ECBlocks(30, new ECB::new(2, 116)), - new ECBlocks(22, new ECB::new(3, 36), - new ECB::new(2, 37)), - new ECBlocks(20, new ECB::new(4, 16), - new ECB::new(4, 17)), - new ECBlocks(24, new ECB::new(4, 12), - new ECB::new(4, 13))), - new Version(10, new int[]{6, 28, 50}, - new ECBlocks(18, new ECB::new(2, 68), - new ECB::new(2, 69)), - new ECBlocks(26, new ECB::new(4, 43), - new ECB::new(1, 44)), - new ECBlocks(24, new ECB::new(6, 19), - new ECB::new(2, 20)), - new ECBlocks(28, new ECB::new(6, 15), - new ECB::new(2, 16))), - new Version(11, new int[]{6, 30, 54}, - new ECBlocks(20, new ECB::new(4, 81)), - new ECBlocks(30, new ECB::new(1, 50), - new ECB::new(4, 51)), - new ECBlocks(28, new ECB::new(4, 22), - new ECB::new(4, 23)), - new ECBlocks(24, new ECB::new(3, 12), - new ECB::new(8, 13))), - new Version(12, new int[]{6, 32, 58}, - new ECBlocks(24, new ECB::new(2, 92), - new ECB::new(2, 93)), - new ECBlocks(22, new ECB::new(6, 36), - new ECB::new(2, 37)), - new ECBlocks(26, new ECB::new(4, 20), - new ECB::new(6, 21)), - new ECBlocks(28, new ECB::new(7, 14), - new ECB::new(4, 15))), - new Version(13, new int[]{6, 34, 62}, - new ECBlocks(26, new ECB::new(4, 107)), - new ECBlocks(22, new ECB::new(8, 37), - new ECB::new(1, 38)), - new ECBlocks(24, new ECB::new(8, 20), - new ECB::new(4, 21)), - new ECBlocks(22, new ECB::new(12, 11), - new ECB::new(4, 12))), - new Version(14, new int[]{6, 26, 46, 66}, - new ECBlocks(30, new ECB::new(3, 115), - new ECB::new(1, 116)), - new ECBlocks(24, new ECB::new(4, 40), - new ECB::new(5, 41)), - new ECBlocks(20, new ECB::new(11, 16), - new ECB::new(5, 17)), - new ECBlocks(24, new ECB::new(11, 12), - new ECB::new(5, 13))), - new Version(15, new int[]{6, 26, 48, 70}, - new ECBlocks(22, new ECB::new(5, 87), - new ECB::new(1, 88)), - new ECBlocks(24, new ECB::new(5, 41), - new ECB::new(5, 42)), - new ECBlocks(30, new ECB::new(5, 24), - new ECB::new(7, 25)), - new ECBlocks(24, new ECB::new(11, 12), - new ECB::new(7, 13))), - new Version(16, new int[]{6, 26, 50, 74}, - new ECBlocks(24, new ECB::new(5, 98), - new ECB::new(1, 99)), - new ECBlocks(28, new ECB::new(7, 45), - new ECB::new(3, 46)), - new ECBlocks(24, new ECB::new(15, 19), - new ECB::new(2, 20)), - new ECBlocks(30, new ECB::new(3, 15), - new ECB::new(13, 16))), - new Version(17, new int[]{6, 30, 54, 78}, - new ECBlocks(28, new ECB::new(1, 107), - new ECB::new(5, 108)), - new ECBlocks(28, new ECB::new(10, 46), - new ECB::new(1, 47)), - new ECBlocks(28, new ECB::new(1, 22), - new ECB::new(15, 23)), - new ECBlocks(28, new ECB::new(2, 14), - new ECB::new(17, 15))), - new Version(18, new int[]{6, 30, 56, 82}, - new ECBlocks(30, new ECB::new(5, 120), - new ECB::new(1, 121)), - new ECBlocks(26, new ECB::new(9, 43), - new ECB::new(4, 44)), - new ECBlocks(28, new ECB::new(17, 22), - new ECB::new(1, 23)), - new ECBlocks(28, new ECB::new(2, 14), - new ECB::new(19, 15))), - new Version(19, new int[]{6, 30, 58, 86}, - new ECBlocks(28, new ECB::new(3, 113), - new ECB::new(4, 114)), - new ECBlocks(26, new ECB::new(3, 44), - new ECB::new(11, 45)), - new ECBlocks(26, new ECB::new(17, 21), - new ECB::new(4, 22)), - new ECBlocks(26, new ECB::new(9, 13), - new ECB::new(16, 14))), - new Version(20, new int[]{6, 34, 62, 90}, - new ECBlocks(28, new ECB::new(3, 107), - new ECB::new(5, 108)), - new ECBlocks(26, new ECB::new(3, 41), - new ECB::new(13, 42)), - new ECBlocks(30, new ECB::new(15, 24), - new ECB::new(5, 25)), - new ECBlocks(28, new ECB::new(15, 15), - new ECB::new(10, 16))), - new Version(21, new int[]{6, 28, 50, 72, 94}, - new ECBlocks(28, new ECB::new(4, 116), - new ECB::new(4, 117)), - new ECBlocks(26, new ECB::new(17, 42)), - new ECBlocks(28, new ECB::new(17, 22), - new ECB::new(6, 23)), - new ECBlocks(30, new ECB::new(19, 16), - new ECB::new(6, 17))), - new Version(22, new int[]{6, 26, 50, 74, 98}, - new ECBlocks(28, new ECB::new(2, 111), - new ECB::new(7, 112)), - new ECBlocks(28, new ECB::new(17, 46)), - new ECBlocks(30, new ECB::new(7, 24), - new ECB::new(16, 25)), - new ECBlocks(24, new ECB::new(34, 13))), - new Version(23, new int[]{6, 30, 54, 78, 102}, - new ECBlocks(30, new ECB::new(4, 121), - new ECB::new(5, 122)), - new ECBlocks(28, new ECB::new(4, 47), - new ECB::new(14, 48)), - new ECBlocks(30, new ECB::new(11, 24), - new ECB::new(14, 25)), - new ECBlocks(30, new ECB::new(16, 15), - new ECB::new(14, 16))), - new Version(24, new int[]{6, 28, 54, 80, 106}, - new ECBlocks(30, new ECB::new(6, 117), - new ECB::new(4, 118)), - new ECBlocks(28, new ECB::new(6, 45), - new ECB::new(14, 46)), - new ECBlocks(30, new ECB::new(11, 24), - new ECB::new(16, 25)), - new ECBlocks(30, new ECB::new(30, 16), - new ECB::new(2, 17))), - new Version(25, new int[]{6, 32, 58, 84, 110}, - new ECBlocks(26, new ECB::new(8, 106), - new ECB::new(4, 107)), - new ECBlocks(28, new ECB::new(8, 47), - new ECB::new(13, 48)), - new ECBlocks(30, new ECB::new(7, 24), - new ECB::new(22, 25)), - new ECBlocks(30, new ECB::new(22, 15), - new ECB::new(13, 16))), - new Version(26, new int[]{6, 30, 58, 86, 114}, - new ECBlocks(28, new ECB::new(10, 114), - new ECB::new(2, 115)), - new ECBlocks(28, new ECB::new(19, 46), - new ECB::new(4, 47)), - new ECBlocks(28, new ECB::new(28, 22), - new ECB::new(6, 23)), - new ECBlocks(30, new ECB::new(33, 16), - new ECB::new(4, 17))), - new Version(27, new int[]{6, 34, 62, 90, 118}, - new ECBlocks(30, new ECB::new(8, 122), - new ECB::new(4, 123)), - new ECBlocks(28, new ECB::new(22, 45), - new ECB::new(3, 46)), - new ECBlocks(30, new ECB::new(8, 23), - new ECB::new(26, 24)), - new ECBlocks(30, new ECB::new(12, 15), - new ECB::new(28, 16))), - new Version(28, new int[]{6, 26, 50, 74, 98, 122}, - new ECBlocks(30, new ECB::new(3, 117), - new ECB::new(10, 118)), - new ECBlocks(28, new ECB::new(3, 45), - new ECB::new(23, 46)), - new ECBlocks(30, new ECB::new(4, 24), - new ECB::new(31, 25)), - new ECBlocks(30, new ECB::new(11, 15), - new ECB::new(31, 16))), - new Version(29, new int[]{6, 30, 54, 78, 102, 126}, - new ECBlocks(30, new ECB::new(7, 116), - new ECB::new(7, 117)), - new ECBlocks(28, new ECB::new(21, 45), - new ECB::new(7, 46)), - new ECBlocks(30, new ECB::new(1, 23), - new ECB::new(37, 24)), - new ECBlocks(30, new ECB::new(19, 15), - new ECB::new(26, 16))), - new Version(30, new int[]{6, 26, 52, 78, 104, 130}, - new ECBlocks(30, new ECB::new(5, 115), - new ECB::new(10, 116)), - new ECBlocks(28, new ECB::new(19, 47), - new ECB::new(10, 48)), - new ECBlocks(30, new ECB::new(15, 24), - new ECB::new(25, 25)), - new ECBlocks(30, new ECB::new(23, 15), - new ECB::new(25, 16))), - new Version(31, new int[]{6, 30, 56, 82, 108, 134}, - new ECBlocks(30, new ECB::new(13, 115), - new ECB::new(3, 116)), - new ECBlocks(28, new ECB::new(2, 46), - new ECB::new(29, 47)), - new ECBlocks(30, new ECB::new(42, 24), - new ECB::new(1, 25)), - new ECBlocks(30, new ECB::new(23, 15), - new ECB::new(28, 16))), - new Version(32, new int[]{6, 34, 60, 86, 112, 138}, - new ECBlocks(30, new ECB::new(17, 115)), - new ECBlocks(28, new ECB::new(10, 46), - new ECB::new(23, 47)), - new ECBlocks(30, new ECB::new(10, 24), - new ECB::new(35, 25)), - new ECBlocks(30, new ECB::new(19, 15), - new ECB::new(35, 16))), - new Version(33, new int[]{6, 30, 58, 86, 114, 142}, - new ECBlocks(30, new ECB::new(17, 115), - new ECB::new(1, 116)), - new ECBlocks(28, new ECB::new(14, 46), - new ECB::new(21, 47)), - new ECBlocks(30, new ECB::new(29, 24), - new ECB::new(19, 25)), - new ECBlocks(30, new ECB::new(11, 15), - new ECB::new(46, 16))), - new Version(34, new int[]{6, 34, 62, 90, 118, 146}, - new ECBlocks(30, new ECB::new(13, 115), - new ECB::new(6, 116)), - new ECBlocks(28, new ECB::new(14, 46), - new ECB::new(23, 47)), - new ECBlocks(30, new ECB::new(44, 24), - new ECB::new(7, 25)), - new ECBlocks(30, new ECB::new(59, 16), - new ECB::new(1, 17))), - new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150}, - new ECBlocks(30, new ECB::new(12, 121), - new ECB::new(7, 122)), - new ECBlocks(28, new ECB::new(12, 47), - new ECB::new(26, 48)), - new ECBlocks(30, new ECB::new(39, 24), - new ECB::new(14, 25)), - new ECBlocks(30, new ECB::new(22, 15), - new ECB::new(41, 16))), - new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154}, - new ECBlocks(30, new ECB::new(6, 121), - new ECB::new(14, 122)), - new ECBlocks(28, new ECB::new(6, 47), - new ECB::new(34, 48)), - new ECBlocks(30, new ECB::new(46, 24), - new ECB::new(10, 25)), - new ECBlocks(30, new ECB::new(2, 15), - new ECB::new(64, 16))), - new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158}, - new ECBlocks(30, new ECB::new(17, 122), - new ECB::new(4, 123)), - new ECBlocks(28, new ECB::new(29, 46), - new ECB::new(14, 47)), - new ECBlocks(30, new ECB::new(49, 24), - new ECB::new(10, 25)), - new ECBlocks(30, new ECB::new(24, 15), - new ECB::new(46, 16))), - new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162}, - new ECBlocks(30, new ECB::new(4, 122), - new ECB::new(18, 123)), - new ECBlocks(28, new ECB::new(13, 46), - new ECB::new(32, 47)), - new ECBlocks(30, new ECB::new(48, 24), - new ECB::new(14, 25)), - new ECBlocks(30, new ECB::new(42, 15), - new ECB::new(32, 16))), - new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166}, - new ECBlocks(30, new ECB::new(20, 117), - new ECB::new(4, 118)), - new ECBlocks(28, new ECB::new(40, 47), - new ECB::new(7, 48)), - new ECBlocks(30, new ECB::new(43, 24), - new ECB::new(22, 25)), - new ECBlocks(30, new ECB::new(10, 15), - new ECB::new(67, 16))), - new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170}, - new ECBlocks(30, new ECB::new(19, 118), - new ECB::new(6, 119)), - new ECBlocks(28, new ECB::new(18, 47), - new ECB::new(31, 48)), - new ECBlocks(30, new ECB::new(34, 24), - new ECB::new(34, 25)), - new ECBlocks(30, new ECB::new(20, 15), - new ECB::new(61, 16))) - ]*/ - } - - /* - * {1, { - 7 , 1, 19, 0, 0, - 10, 1, 16, 0, 0, - 13, 1, 13, 0, 0, - 17, 1, 9 , 0, 0 - }}, - {2, { - 10, 1, 36, 0, 0, - 16, 1, 30, 0, 0, - 22, 1, 24, 0, 0, - 30, 1, 16, 0, 0, - }}, - {3, { - 15, 1, 57, 0, 0, - 28, 1, 44, 0, 0, - 36, 1, 36, 0, 0, - 48, 1, 24, 0, 0, - }}, - {4, { - 20, 1, 80, 0, 0, - 40, 1, 60, 0, 0, - 50, 1, 50, 0, 0, - 66, 1, 34, 0, 0, - }}, - {5, { - 26, 1, 108, 0, 0, - 52, 1, 82 , 0, 0, - 66, 1, 68 , 0, 0, - 88, 2, 46 , 0, 0, - }}, - {6, { - 34 , 1, 136, 0, 0, - 63 , 2, 106, 0, 0, - 84 , 2, 86 , 0, 0, - 112, 2, 58 , 0, 0, - }}, - {7, { - 42 , 1, 170, 0, 0, - 80 , 2, 132, 0, 0, - 104, 2, 108, 0, 0, - 138, 3, 72 , 0, 0, - }}, - {8, { - 48 , 2, 208, 0, 0, - 96 , 2, 160, 0, 0, - 128, 2, 128, 0, 0, - 168, 3, 87 , 0, 0, - }}, - {9, { - 60 , 2, 246, 0, 0, - 120, 2, 186, 0, 0, - 150, 3, 156, 0, 0, - 204, 3, 102, 0, 0, - }}, - {10, { - 68 , 2, 290, 0, 0, - 136, 2, 222, 0, 0, - 174, 3, 183, 0, 0, - 232, 4, 124, 0, 0, - }}, - {11, { - 80 , 2, 336, 0, 0, - 160, 4, 256, 0, 0, - 208, 4, 208, 0, 0, - 270, 5, 145, 0, 0, - }}, - {12, { - 92 , 2, 384, 0, 0, - 184, 4, 292, 0, 0, - 232, 4, 244, 0, 0, - 310, 5, 165, 0, 0, - }}, - {13, { - 108, 3, 432, 0, 0, - 208, 4, 332, 0, 0, - 264, 4, 276, 0, 0, - 348, 6, 192, 0, 0, - }}, - {14, { - 120, 3, 489, 0, 0, - 240, 4, 368, 0, 0, - 300, 5, 310, 0, 0, - 396, 6, 210, 0, 0, - }}, - }; - */ - pub fn build_model1_versions() -> Vec { - Vec::from([ - Version::new_model1( - 1, - vec![ - ECBlocks::new(7, vec![ECB::new(1, 19)]), - ECBlocks::new(10, vec![ECB::new(1, 16)]), - ECBlocks::new(13, vec![ECB::new(1, 13)]), - ECBlocks::new(17, vec![ECB::new(1, 9)]), - ], - ), - Version::new_model1( - 2, - vec![ - ECBlocks::new(10, vec![ECB::new(1, 36)]), - ECBlocks::new(16, vec![ECB::new(1, 30)]), - ECBlocks::new(22, vec![ECB::new(1, 24)]), - ECBlocks::new(30, vec![ECB::new(1, 16)]), - ], - ), - Version::new_model1( - 3, - vec![ - ECBlocks::new(15, vec![ECB::new(1, 57)]), - ECBlocks::new(28, vec![ECB::new(1, 44)]), - ECBlocks::new(36, vec![ECB::new(1, 36)]), - ECBlocks::new(48, vec![ECB::new(1, 24)]), - ], - ), - Version::new_model1( - 4, - vec![ - ECBlocks::new(20, vec![ECB::new(1, 80)]), - ECBlocks::new(40, vec![ECB::new(1, 60)]), - ECBlocks::new(50, vec![ECB::new(1, 50)]), - ECBlocks::new(66, vec![ECB::new(1, 34)]), - ], - ), - Version::new_model1( - 5, - vec![ - ECBlocks::new(26, vec![ECB::new(1, 108)]), - ECBlocks::new(52, vec![ECB::new(1, 82)]), - ECBlocks::new(66, vec![ECB::new(1, 68)]), - ECBlocks::new(88, vec![ECB::new(2, 46)]), - ], - ), - Version::new_model1( - 6, - vec![ - ECBlocks::new(34, vec![ECB::new(1, 136)]), - ECBlocks::new(63, vec![ECB::new(2, 106)]), - ECBlocks::new(84, vec![ECB::new(2, 86)]), - ECBlocks::new(112, vec![ECB::new(2, 58)]), - ], - ), - Version::new_model1( - 7, - vec![ - ECBlocks::new(42, vec![ECB::new(1, 170)]), - ECBlocks::new(80, vec![ECB::new(2, 132)]), - ECBlocks::new(104, vec![ECB::new(2, 108)]), - ECBlocks::new(138, vec![ECB::new(3, 72)]), - ], - ), - Version::new_model1( - 8, - vec![ - ECBlocks::new(48, vec![ECB::new(2, 208)]), - ECBlocks::new(96, vec![ECB::new(2, 160)]), - ECBlocks::new(128, vec![ECB::new(2, 128)]), - ECBlocks::new(168, vec![ECB::new(3, 87)]), - ], - ), - Version::new_model1( - 9, - vec![ - ECBlocks::new(60, vec![ECB::new(2, 246)]), - ECBlocks::new(120, vec![ECB::new(2, 186)]), - ECBlocks::new(150, vec![ECB::new(3, 156)]), - ECBlocks::new(204, vec![ECB::new(3, 102)]), - ], - ), - Version::new_model1( - 10, - vec![ - ECBlocks::new(68, vec![ECB::new(2, 290)]), - ECBlocks::new(136, vec![ECB::new(2, 222)]), - ECBlocks::new(174, vec![ECB::new(3, 183)]), - ECBlocks::new(232, vec![ECB::new(4, 124)]), - ], - ), - Version::new_model1( - 11, - vec![ - ECBlocks::new(80, vec![ECB::new(2, 336)]), - ECBlocks::new(160, vec![ECB::new(4, 256)]), - ECBlocks::new(208, vec![ECB::new(4, 208)]), - ECBlocks::new(270, vec![ECB::new(5, 145)]), - ], - ), - Version::new_model1( - 12, - vec![ - ECBlocks::new(92, vec![ECB::new(2, 384)]), - ECBlocks::new(184, vec![ECB::new(4, 292)]), - ECBlocks::new(232, vec![ECB::new(4, 244)]), - ECBlocks::new(310, vec![ECB::new(5, 165)]), - ], - ), - Version::new_model1( - 13, - vec![ - ECBlocks::new(108, vec![ECB::new(3, 432)]), - ECBlocks::new(208, vec![ECB::new(4, 332)]), - ECBlocks::new(264, vec![ECB::new(4, 276)]), - ECBlocks::new(348, vec![ECB::new(6, 192)]), - ], - ), - Version::new_model1( - 14, - vec![ - ECBlocks::new(120, vec![ECB::new(3, 489)]), - ECBlocks::new(240, vec![ECB::new(4, 368)]), - ECBlocks::new(300, vec![ECB::new(5, 310)]), - ECBlocks::new(396, vec![ECB::new(6, 210)]), - ], - ), - ]) - } } impl fmt::Display for Version { diff --git a/src/qrcode/decoder/version_build_versions_arrays.rs b/src/qrcode/decoder/version_build_versions_arrays.rs new file mode 100644 index 0000000..bbc6598 --- /dev/null +++ b/src/qrcode/decoder/version_build_versions_arrays.rs @@ -0,0 +1,1547 @@ +use super::{ECBlocks, Version, ECB}; + +impl Version { + pub fn build_micro_versions() -> Vec { + vec![ + Version::new_micro(1, vec![ECBlocks::new(2, vec![ECB::new(1, 3)])]), + Version::new_micro( + 2, + vec![ + ECBlocks::new(5, vec![ECB::new(1, 5)]), + ECBlocks::new(6, vec![ECB::new(1, 4)]), + ], + ), + Version::new_micro( + 3, + vec![ + ECBlocks::new(6, vec![ECB::new(1, 11)]), + ECBlocks::new(8, vec![ECB::new(1, 9)]), + ], + ), + Version::new_micro( + 4, + vec![ + ECBlocks::new(8, vec![ECB::new(1, 16)]), + ECBlocks::new(10, vec![ECB::new(1, 14)]), + ECBlocks::new(14, vec![ECB::new(1, 10)]), + ], + ), + ] + // static const Version allVersions[] = { + // {1, {2, 1, 3, 0, 0}}, + // {2, {5, 1, 5, 0, 0, 6, 1, 4, 0, 0}}, + // {3, {6, 1, 11, 0, 0, 8, 1, 9, 0, 0}}, + // {4, {8, 1, 16, 0, 0, 10, 1, 14, 0, 0, 14, 1, 10, 0, 0}}}; + } + + /** + * See ISO 18004:2006 6.5.1 Table 9 + */ + pub fn buildVersions() -> Vec { + Vec::from([ + Version::new( + 1, + Vec::from([]), + [ + ECBlocks::new(7, Vec::from([ECB::new(1, 19)])), + ECBlocks::new(10, Vec::from([ECB::new(1, 16)])), + ECBlocks::new(13, Vec::from([ECB::new(1, 13)])), + ECBlocks::new(17, Vec::from([ECB::new(1, 9)])), + ], + ), + Version::new( + 2, + Vec::from([6, 18]), + [ + ECBlocks::new(10, Vec::from([ECB::new(1, 34)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 28)])), + ECBlocks::new(22, Vec::from([ECB::new(1, 22)])), + ECBlocks::new(28, Vec::from([ECB::new(1, 16)])), + ], + ), + Version::new( + 3, + Vec::from([6, 22]), + [ + ECBlocks::new(15, Vec::from([ECB::new(1, 55)])), + ECBlocks::new(26, Vec::from([ECB::new(1, 44)])), + ECBlocks::new(18, Vec::from([ECB::new(2, 17)])), + ECBlocks::new(22, Vec::from([ECB::new(2, 13)])), + ], + ), + Version::new( + 4, + Vec::from([6, 26]), + [ + ECBlocks::new(20, Vec::from([ECB::new(1, 80)])), + ECBlocks::new(18, Vec::from([ECB::new(2, 32)])), + ECBlocks::new(26, Vec::from([ECB::new(2, 24)])), + ECBlocks::new(16, Vec::from([ECB::new(4, 9)])), + ], + ), + Version::new( + 5, + Vec::from([6, 30]), + [ + ECBlocks::new(26, Vec::from([ECB::new(1, 108)])), + ECBlocks::new(24, Vec::from([ECB::new(2, 43)])), + ECBlocks::new(18, Vec::from([ECB::new(2, 15), ECB::new(2, 16)])), + ECBlocks::new(22, Vec::from([ECB::new(2, 11), ECB::new(2, 12)])), + ], + ), + Version::new( + 6, + Vec::from([6, 34]), + [ + ECBlocks::new(18, Vec::from([ECB::new(2, 68)])), + ECBlocks::new(16, Vec::from([ECB::new(4, 27)])), + ECBlocks::new(24, Vec::from([ECB::new(4, 19)])), + ECBlocks::new(28, Vec::from([ECB::new(4, 15)])), + ], + ), + Version::new( + 7, + Vec::from([6, 22, 38]), + [ + ECBlocks::new(20, Vec::from([ECB::new(2, 78)])), + ECBlocks::new(18, Vec::from([ECB::new(4, 31)])), + ECBlocks::new(18, Vec::from([ECB::new(2, 14), ECB::new(4, 15)])), + ECBlocks::new(26, Vec::from([ECB::new(4, 13), ECB::new(1, 14)])), + ], + ), + Version::new( + 8, + Vec::from([6, 24, 42]), + [ + ECBlocks::new(24, Vec::from([ECB::new(2, 97)])), + ECBlocks::new(22, Vec::from([ECB::new(2, 38), ECB::new(2, 39)])), + ECBlocks::new(22, Vec::from([ECB::new(4, 18), ECB::new(2, 19)])), + ECBlocks::new(26, Vec::from([ECB::new(4, 14), ECB::new(2, 15)])), + ], + ), + Version::new( + 9, + Vec::from([6, 26, 46]), + [ + ECBlocks::new(30, Vec::from([ECB::new(2, 116)])), + ECBlocks::new(22, Vec::from([ECB::new(3, 36), ECB::new(2, 37)])), + ECBlocks::new(20, Vec::from([ECB::new(4, 16), ECB::new(4, 17)])), + ECBlocks::new(24, Vec::from([ECB::new(4, 12), ECB::new(4, 13)])), + ], + ), + Version::new( + 10, + Vec::from([6, 28, 50]), + [ + ECBlocks::new(18, Vec::from([ECB::new(2, 68), ECB::new(2, 69)])), + ECBlocks::new(26, Vec::from([ECB::new(4, 43), ECB::new(1, 44)])), + ECBlocks::new(24, Vec::from([ECB::new(6, 19), ECB::new(2, 20)])), + ECBlocks::new(28, Vec::from([ECB::new(6, 15), ECB::new(2, 16)])), + ], + ), + Version::new( + 11, + Vec::from([6, 30, 54]), + [ + ECBlocks::new(20, Vec::from([ECB::new(4, 81)])), + ECBlocks::new(30, Vec::from([ECB::new(1, 50), ECB::new(4, 51)])), + ECBlocks::new(28, Vec::from([ECB::new(4, 22), ECB::new(4, 23)])), + ECBlocks::new(24, Vec::from([ECB::new(3, 12), ECB::new(8, 13)])), + ], + ), + Version::new( + 12, + Vec::from([6, 32, 58]), + [ + ECBlocks::new(24, Vec::from([ECB::new(2, 92), ECB::new(2, 93)])), + ECBlocks::new(22, Vec::from([ECB::new(6, 36), ECB::new(2, 37)])), + ECBlocks::new(26, Vec::from([ECB::new(4, 20), ECB::new(6, 21)])), + ECBlocks::new(28, Vec::from([ECB::new(7, 14), ECB::new(4, 15)])), + ], + ), + Version::new( + 13, + Vec::from([6, 34, 62]), + [ + ECBlocks::new(26, Vec::from([ECB::new(4, 107)])), + ECBlocks::new(22, Vec::from([ECB::new(8, 37), ECB::new(1, 38)])), + ECBlocks::new(24, Vec::from([ECB::new(8, 20), ECB::new(4, 21)])), + ECBlocks::new(22, Vec::from([ECB::new(12, 11), ECB::new(4, 12)])), + ], + ), + Version::new( + 14, + Vec::from([6, 26, 46, 66]), + [ + ECBlocks::new(30, Vec::from([ECB::new(3, 115), ECB::new(1, 116)])), + ECBlocks::new(24, Vec::from([ECB::new(4, 40), ECB::new(5, 41)])), + ECBlocks::new(20, Vec::from([ECB::new(11, 16), ECB::new(5, 17)])), + ECBlocks::new(24, Vec::from([ECB::new(11, 12), ECB::new(5, 13)])), + ], + ), + Version::new( + 15, + Vec::from([6, 26, 48, 70]), + [ + ECBlocks::new(22, Vec::from([ECB::new(5, 87), ECB::new(1, 88)])), + ECBlocks::new(24, Vec::from([ECB::new(5, 41), ECB::new(5, 42)])), + ECBlocks::new(30, Vec::from([ECB::new(5, 24), ECB::new(7, 25)])), + ECBlocks::new(24, Vec::from([ECB::new(11, 12), ECB::new(7, 13)])), + ], + ), + Version::new( + 16, + Vec::from([6, 26, 50, 74]), + [ + ECBlocks::new(24, Vec::from([ECB::new(5, 98), ECB::new(1, 99)])), + ECBlocks::new(28, Vec::from([ECB::new(7, 45), ECB::new(3, 46)])), + ECBlocks::new(24, Vec::from([ECB::new(15, 19), ECB::new(2, 20)])), + ECBlocks::new(30, Vec::from([ECB::new(3, 15), ECB::new(13, 16)])), + ], + ), + Version::new( + 17, + Vec::from([6, 30, 54, 78]), + [ + ECBlocks::new(28, Vec::from([ECB::new(1, 107), ECB::new(5, 108)])), + ECBlocks::new(28, Vec::from([ECB::new(10, 46), ECB::new(1, 47)])), + ECBlocks::new(28, Vec::from([ECB::new(1, 22), ECB::new(15, 23)])), + ECBlocks::new(28, Vec::from([ECB::new(2, 14), ECB::new(17, 15)])), + ], + ), + Version::new( + 18, + Vec::from([6, 30, 56, 82]), + [ + ECBlocks::new(30, Vec::from([ECB::new(5, 120), ECB::new(1, 121)])), + ECBlocks::new(26, Vec::from([ECB::new(9, 43), ECB::new(4, 44)])), + ECBlocks::new(28, Vec::from([ECB::new(17, 22), ECB::new(1, 23)])), + ECBlocks::new(28, Vec::from([ECB::new(2, 14), ECB::new(19, 15)])), + ], + ), + Version::new( + 19, + Vec::from([6, 30, 58, 86]), + [ + ECBlocks::new(28, Vec::from([ECB::new(3, 113), ECB::new(4, 114)])), + ECBlocks::new(26, Vec::from([ECB::new(3, 44), ECB::new(11, 45)])), + ECBlocks::new(26, Vec::from([ECB::new(17, 21), ECB::new(4, 22)])), + ECBlocks::new(26, Vec::from([ECB::new(9, 13), ECB::new(16, 14)])), + ], + ), + Version::new( + 20, + Vec::from([6, 34, 62, 90]), + [ + ECBlocks::new(28, Vec::from([ECB::new(3, 107), ECB::new(5, 108)])), + ECBlocks::new(26, Vec::from([ECB::new(3, 41), ECB::new(13, 42)])), + ECBlocks::new(30, Vec::from([ECB::new(15, 24), ECB::new(5, 25)])), + ECBlocks::new(28, Vec::from([ECB::new(15, 15), ECB::new(10, 16)])), + ], + ), + Version::new( + 21, + Vec::from([6, 28, 50, 72, 94]), + [ + ECBlocks::new(28, Vec::from([ECB::new(4, 116), ECB::new(4, 117)])), + ECBlocks::new(26, Vec::from([ECB::new(17, 42)])), + ECBlocks::new(28, Vec::from([ECB::new(17, 22), ECB::new(6, 23)])), + ECBlocks::new(30, Vec::from([ECB::new(19, 16), ECB::new(6, 17)])), + ], + ), + Version::new( + 22, + Vec::from([6, 26, 50, 74, 98]), + [ + ECBlocks::new(28, Vec::from([ECB::new(2, 111), ECB::new(7, 112)])), + ECBlocks::new(28, Vec::from([ECB::new(17, 46)])), + ECBlocks::new(30, Vec::from([ECB::new(7, 24), ECB::new(16, 25)])), + ECBlocks::new(24, Vec::from([ECB::new(34, 13)])), + ], + ), + Version::new( + 23, + Vec::from([6, 30, 54, 78, 102]), + [ + ECBlocks::new(30, Vec::from([ECB::new(4, 121), ECB::new(5, 122)])), + ECBlocks::new(28, Vec::from([ECB::new(4, 47), ECB::new(14, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(11, 24), ECB::new(14, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(16, 15), ECB::new(14, 16)])), + ], + ), + Version::new( + 24, + Vec::from([6, 28, 54, 80, 106]), + [ + ECBlocks::new(30, Vec::from([ECB::new(6, 117), ECB::new(4, 118)])), + ECBlocks::new(28, Vec::from([ECB::new(6, 45), ECB::new(14, 46)])), + ECBlocks::new(30, Vec::from([ECB::new(11, 24), ECB::new(16, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(30, 16), ECB::new(2, 17)])), + ], + ), + Version::new( + 25, + Vec::from([6, 32, 58, 84, 110]), + [ + ECBlocks::new(26, Vec::from([ECB::new(8, 106), ECB::new(4, 107)])), + ECBlocks::new(28, Vec::from([ECB::new(8, 47), ECB::new(13, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(7, 24), ECB::new(22, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(22, 15), ECB::new(13, 16)])), + ], + ), + Version::new( + 26, + Vec::from([6, 30, 58, 86, 114]), + [ + ECBlocks::new(28, Vec::from([ECB::new(10, 114), ECB::new(2, 115)])), + ECBlocks::new(28, Vec::from([ECB::new(19, 46), ECB::new(4, 47)])), + ECBlocks::new(28, Vec::from([ECB::new(28, 22), ECB::new(6, 23)])), + ECBlocks::new(30, Vec::from([ECB::new(33, 16), ECB::new(4, 17)])), + ], + ), + Version::new( + 27, + Vec::from([6, 34, 62, 90, 118]), + [ + ECBlocks::new(30, Vec::from([ECB::new(8, 122), ECB::new(4, 123)])), + ECBlocks::new(28, Vec::from([ECB::new(22, 45), ECB::new(3, 46)])), + ECBlocks::new(30, Vec::from([ECB::new(8, 23), ECB::new(26, 24)])), + ECBlocks::new(30, Vec::from([ECB::new(12, 15), ECB::new(28, 16)])), + ], + ), + Version::new( + 28, + Vec::from([6, 26, 50, 74, 98, 122]), + [ + ECBlocks::new(30, Vec::from([ECB::new(3, 117), ECB::new(10, 118)])), + ECBlocks::new(28, Vec::from([ECB::new(3, 45), ECB::new(23, 46)])), + ECBlocks::new(30, Vec::from([ECB::new(4, 24), ECB::new(31, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(11, 15), ECB::new(31, 16)])), + ], + ), + Version::new( + 29, + Vec::from([6, 30, 54, 78, 102, 126]), + [ + ECBlocks::new(30, Vec::from([ECB::new(7, 116), ECB::new(7, 117)])), + ECBlocks::new(28, Vec::from([ECB::new(21, 45), ECB::new(7, 46)])), + ECBlocks::new(30, Vec::from([ECB::new(1, 23), ECB::new(37, 24)])), + ECBlocks::new(30, Vec::from([ECB::new(19, 15), ECB::new(26, 16)])), + ], + ), + Version::new( + 30, + Vec::from([6, 26, 52, 78, 104, 130]), + [ + ECBlocks::new(30, Vec::from([ECB::new(5, 115), ECB::new(10, 116)])), + ECBlocks::new(28, Vec::from([ECB::new(19, 47), ECB::new(10, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(15, 24), ECB::new(25, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(23, 15), ECB::new(25, 16)])), + ], + ), + Version::new( + 31, + Vec::from([6, 30, 56, 82, 108, 134]), + [ + ECBlocks::new(30, Vec::from([ECB::new(13, 115), ECB::new(3, 116)])), + ECBlocks::new(28, Vec::from([ECB::new(2, 46), ECB::new(29, 47)])), + ECBlocks::new(30, Vec::from([ECB::new(42, 24), ECB::new(1, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(23, 15), ECB::new(28, 16)])), + ], + ), + Version::new( + 32, + Vec::from([6, 34, 60, 86, 112, 138]), + [ + ECBlocks::new(30, Vec::from([ECB::new(17, 115)])), + ECBlocks::new(28, Vec::from([ECB::new(10, 46), ECB::new(23, 47)])), + ECBlocks::new(30, Vec::from([ECB::new(10, 24), ECB::new(35, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(19, 15), ECB::new(35, 16)])), + ], + ), + Version::new( + 33, + Vec::from([6, 30, 58, 86, 114, 142]), + [ + ECBlocks::new(30, Vec::from([ECB::new(17, 115), ECB::new(1, 116)])), + ECBlocks::new(28, Vec::from([ECB::new(14, 46), ECB::new(21, 47)])), + ECBlocks::new(30, Vec::from([ECB::new(29, 24), ECB::new(19, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(11, 15), ECB::new(46, 16)])), + ], + ), + Version::new( + 34, + Vec::from([6, 34, 62, 90, 118, 146]), + [ + ECBlocks::new(30, Vec::from([ECB::new(13, 115), ECB::new(6, 116)])), + ECBlocks::new(28, Vec::from([ECB::new(14, 46), ECB::new(23, 47)])), + ECBlocks::new(30, Vec::from([ECB::new(44, 24), ECB::new(7, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(59, 16), ECB::new(1, 17)])), + ], + ), + Version::new( + 35, + Vec::from([6, 30, 54, 78, 102, 126, 150]), + [ + ECBlocks::new(30, Vec::from([ECB::new(12, 121), ECB::new(7, 122)])), + ECBlocks::new(28, Vec::from([ECB::new(12, 47), ECB::new(26, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(39, 24), ECB::new(14, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(22, 15), ECB::new(41, 16)])), + ], + ), + Version::new( + 36, + Vec::from([6, 24, 50, 76, 102, 128, 154]), + [ + ECBlocks::new(30, Vec::from([ECB::new(6, 121), ECB::new(14, 122)])), + ECBlocks::new(28, Vec::from([ECB::new(6, 47), ECB::new(34, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(46, 24), ECB::new(10, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(2, 15), ECB::new(64, 16)])), + ], + ), + Version::new( + 37, + Vec::from([6, 28, 54, 80, 106, 132, 158]), + [ + ECBlocks::new(30, Vec::from([ECB::new(17, 122), ECB::new(4, 123)])), + ECBlocks::new(28, Vec::from([ECB::new(29, 46), ECB::new(14, 47)])), + ECBlocks::new(30, Vec::from([ECB::new(49, 24), ECB::new(10, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(24, 15), ECB::new(46, 16)])), + ], + ), + Version::new( + 38, + Vec::from([6, 32, 58, 84, 110, 136, 162]), + [ + ECBlocks::new(30, Vec::from([ECB::new(4, 122), ECB::new(18, 123)])), + ECBlocks::new(28, Vec::from([ECB::new(13, 46), ECB::new(32, 47)])), + ECBlocks::new(30, Vec::from([ECB::new(48, 24), ECB::new(14, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(42, 15), ECB::new(32, 16)])), + ], + ), + Version::new( + 39, + Vec::from([6, 26, 54, 82, 110, 138, 166]), + [ + ECBlocks::new(30, Vec::from([ECB::new(20, 117), ECB::new(4, 118)])), + ECBlocks::new(28, Vec::from([ECB::new(40, 47), ECB::new(7, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(43, 24), ECB::new(22, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(10, 15), ECB::new(67, 16)])), + ], + ), + Version::new( + 40, + Vec::from([6, 30, 58, 86, 114, 142, 170]), + [ + ECBlocks::new(30, Vec::from([ECB::new(19, 118), ECB::new(6, 119)])), + ECBlocks::new(28, Vec::from([ECB::new(18, 47), ECB::new(31, 48)])), + ECBlocks::new(30, Vec::from([ECB::new(34, 24), ECB::new(34, 25)])), + ECBlocks::new(30, Vec::from([ECB::new(20, 15), ECB::new(61, 16)])), + ], + ), + ]) /* + new Version(4, new int[]{6, 26}, + new ECBlocks(20, new ECB::new(1, 80)), + new ECBlocks(18, new ECB::new(2, 32)), + new ECBlocks(26, new ECB::new(2, 24)), + new ECBlocks(16, new ECB::new(4, 9))), + new Version(5, new int[]{6, 30}, + new ECBlocks(26, new ECB::new(1, 108)), + new ECBlocks(24, new ECB::new(2, 43)), + new ECBlocks(18, new ECB::new(2, 15), + new ECB::new(2, 16)), + new ECBlocks(22, new ECB::new(2, 11), + new ECB::new(2, 12))), + new Version(6, new int[]{6, 34}, + new ECBlocks(18, new ECB::new(2, 68)), + new ECBlocks(16, new ECB::new(4, 27)), + new ECBlocks(24, new ECB::new(4, 19)), + new ECBlocks(28, new ECB::new(4, 15))), + new Version(7, new int[]{6, 22, 38}, + new ECBlocks(20, new ECB::new(2, 78)), + new ECBlocks(18, new ECB::new(4, 31)), + new ECBlocks(18, new ECB::new(2, 14), + new ECB::new(4, 15)), + new ECBlocks(26, new ECB::new(4, 13), + new ECB::new(1, 14))), + new Version(8, new int[]{6, 24, 42}, + new ECBlocks(24, new ECB::new(2, 97)), + new ECBlocks(22, new ECB::new(2, 38), + new ECB::new(2, 39)), + new ECBlocks(22, new ECB::new(4, 18), + new ECB::new(2, 19)), + new ECBlocks(26, new ECB::new(4, 14), + new ECB::new(2, 15))), + new Version(9, new int[]{6, 26, 46}, + new ECBlocks(30, new ECB::new(2, 116)), + new ECBlocks(22, new ECB::new(3, 36), + new ECB::new(2, 37)), + new ECBlocks(20, new ECB::new(4, 16), + new ECB::new(4, 17)), + new ECBlocks(24, new ECB::new(4, 12), + new ECB::new(4, 13))), + new Version(10, new int[]{6, 28, 50}, + new ECBlocks(18, new ECB::new(2, 68), + new ECB::new(2, 69)), + new ECBlocks(26, new ECB::new(4, 43), + new ECB::new(1, 44)), + new ECBlocks(24, new ECB::new(6, 19), + new ECB::new(2, 20)), + new ECBlocks(28, new ECB::new(6, 15), + new ECB::new(2, 16))), + new Version(11, new int[]{6, 30, 54}, + new ECBlocks(20, new ECB::new(4, 81)), + new ECBlocks(30, new ECB::new(1, 50), + new ECB::new(4, 51)), + new ECBlocks(28, new ECB::new(4, 22), + new ECB::new(4, 23)), + new ECBlocks(24, new ECB::new(3, 12), + new ECB::new(8, 13))), + new Version(12, new int[]{6, 32, 58}, + new ECBlocks(24, new ECB::new(2, 92), + new ECB::new(2, 93)), + new ECBlocks(22, new ECB::new(6, 36), + new ECB::new(2, 37)), + new ECBlocks(26, new ECB::new(4, 20), + new ECB::new(6, 21)), + new ECBlocks(28, new ECB::new(7, 14), + new ECB::new(4, 15))), + new Version(13, new int[]{6, 34, 62}, + new ECBlocks(26, new ECB::new(4, 107)), + new ECBlocks(22, new ECB::new(8, 37), + new ECB::new(1, 38)), + new ECBlocks(24, new ECB::new(8, 20), + new ECB::new(4, 21)), + new ECBlocks(22, new ECB::new(12, 11), + new ECB::new(4, 12))), + new Version(14, new int[]{6, 26, 46, 66}, + new ECBlocks(30, new ECB::new(3, 115), + new ECB::new(1, 116)), + new ECBlocks(24, new ECB::new(4, 40), + new ECB::new(5, 41)), + new ECBlocks(20, new ECB::new(11, 16), + new ECB::new(5, 17)), + new ECBlocks(24, new ECB::new(11, 12), + new ECB::new(5, 13))), + new Version(15, new int[]{6, 26, 48, 70}, + new ECBlocks(22, new ECB::new(5, 87), + new ECB::new(1, 88)), + new ECBlocks(24, new ECB::new(5, 41), + new ECB::new(5, 42)), + new ECBlocks(30, new ECB::new(5, 24), + new ECB::new(7, 25)), + new ECBlocks(24, new ECB::new(11, 12), + new ECB::new(7, 13))), + new Version(16, new int[]{6, 26, 50, 74}, + new ECBlocks(24, new ECB::new(5, 98), + new ECB::new(1, 99)), + new ECBlocks(28, new ECB::new(7, 45), + new ECB::new(3, 46)), + new ECBlocks(24, new ECB::new(15, 19), + new ECB::new(2, 20)), + new ECBlocks(30, new ECB::new(3, 15), + new ECB::new(13, 16))), + new Version(17, new int[]{6, 30, 54, 78}, + new ECBlocks(28, new ECB::new(1, 107), + new ECB::new(5, 108)), + new ECBlocks(28, new ECB::new(10, 46), + new ECB::new(1, 47)), + new ECBlocks(28, new ECB::new(1, 22), + new ECB::new(15, 23)), + new ECBlocks(28, new ECB::new(2, 14), + new ECB::new(17, 15))), + new Version(18, new int[]{6, 30, 56, 82}, + new ECBlocks(30, new ECB::new(5, 120), + new ECB::new(1, 121)), + new ECBlocks(26, new ECB::new(9, 43), + new ECB::new(4, 44)), + new ECBlocks(28, new ECB::new(17, 22), + new ECB::new(1, 23)), + new ECBlocks(28, new ECB::new(2, 14), + new ECB::new(19, 15))), + new Version(19, new int[]{6, 30, 58, 86}, + new ECBlocks(28, new ECB::new(3, 113), + new ECB::new(4, 114)), + new ECBlocks(26, new ECB::new(3, 44), + new ECB::new(11, 45)), + new ECBlocks(26, new ECB::new(17, 21), + new ECB::new(4, 22)), + new ECBlocks(26, new ECB::new(9, 13), + new ECB::new(16, 14))), + new Version(20, new int[]{6, 34, 62, 90}, + new ECBlocks(28, new ECB::new(3, 107), + new ECB::new(5, 108)), + new ECBlocks(26, new ECB::new(3, 41), + new ECB::new(13, 42)), + new ECBlocks(30, new ECB::new(15, 24), + new ECB::new(5, 25)), + new ECBlocks(28, new ECB::new(15, 15), + new ECB::new(10, 16))), + new Version(21, new int[]{6, 28, 50, 72, 94}, + new ECBlocks(28, new ECB::new(4, 116), + new ECB::new(4, 117)), + new ECBlocks(26, new ECB::new(17, 42)), + new ECBlocks(28, new ECB::new(17, 22), + new ECB::new(6, 23)), + new ECBlocks(30, new ECB::new(19, 16), + new ECB::new(6, 17))), + new Version(22, new int[]{6, 26, 50, 74, 98}, + new ECBlocks(28, new ECB::new(2, 111), + new ECB::new(7, 112)), + new ECBlocks(28, new ECB::new(17, 46)), + new ECBlocks(30, new ECB::new(7, 24), + new ECB::new(16, 25)), + new ECBlocks(24, new ECB::new(34, 13))), + new Version(23, new int[]{6, 30, 54, 78, 102}, + new ECBlocks(30, new ECB::new(4, 121), + new ECB::new(5, 122)), + new ECBlocks(28, new ECB::new(4, 47), + new ECB::new(14, 48)), + new ECBlocks(30, new ECB::new(11, 24), + new ECB::new(14, 25)), + new ECBlocks(30, new ECB::new(16, 15), + new ECB::new(14, 16))), + new Version(24, new int[]{6, 28, 54, 80, 106}, + new ECBlocks(30, new ECB::new(6, 117), + new ECB::new(4, 118)), + new ECBlocks(28, new ECB::new(6, 45), + new ECB::new(14, 46)), + new ECBlocks(30, new ECB::new(11, 24), + new ECB::new(16, 25)), + new ECBlocks(30, new ECB::new(30, 16), + new ECB::new(2, 17))), + new Version(25, new int[]{6, 32, 58, 84, 110}, + new ECBlocks(26, new ECB::new(8, 106), + new ECB::new(4, 107)), + new ECBlocks(28, new ECB::new(8, 47), + new ECB::new(13, 48)), + new ECBlocks(30, new ECB::new(7, 24), + new ECB::new(22, 25)), + new ECBlocks(30, new ECB::new(22, 15), + new ECB::new(13, 16))), + new Version(26, new int[]{6, 30, 58, 86, 114}, + new ECBlocks(28, new ECB::new(10, 114), + new ECB::new(2, 115)), + new ECBlocks(28, new ECB::new(19, 46), + new ECB::new(4, 47)), + new ECBlocks(28, new ECB::new(28, 22), + new ECB::new(6, 23)), + new ECBlocks(30, new ECB::new(33, 16), + new ECB::new(4, 17))), + new Version(27, new int[]{6, 34, 62, 90, 118}, + new ECBlocks(30, new ECB::new(8, 122), + new ECB::new(4, 123)), + new ECBlocks(28, new ECB::new(22, 45), + new ECB::new(3, 46)), + new ECBlocks(30, new ECB::new(8, 23), + new ECB::new(26, 24)), + new ECBlocks(30, new ECB::new(12, 15), + new ECB::new(28, 16))), + new Version(28, new int[]{6, 26, 50, 74, 98, 122}, + new ECBlocks(30, new ECB::new(3, 117), + new ECB::new(10, 118)), + new ECBlocks(28, new ECB::new(3, 45), + new ECB::new(23, 46)), + new ECBlocks(30, new ECB::new(4, 24), + new ECB::new(31, 25)), + new ECBlocks(30, new ECB::new(11, 15), + new ECB::new(31, 16))), + new Version(29, new int[]{6, 30, 54, 78, 102, 126}, + new ECBlocks(30, new ECB::new(7, 116), + new ECB::new(7, 117)), + new ECBlocks(28, new ECB::new(21, 45), + new ECB::new(7, 46)), + new ECBlocks(30, new ECB::new(1, 23), + new ECB::new(37, 24)), + new ECBlocks(30, new ECB::new(19, 15), + new ECB::new(26, 16))), + new Version(30, new int[]{6, 26, 52, 78, 104, 130}, + new ECBlocks(30, new ECB::new(5, 115), + new ECB::new(10, 116)), + new ECBlocks(28, new ECB::new(19, 47), + new ECB::new(10, 48)), + new ECBlocks(30, new ECB::new(15, 24), + new ECB::new(25, 25)), + new ECBlocks(30, new ECB::new(23, 15), + new ECB::new(25, 16))), + new Version(31, new int[]{6, 30, 56, 82, 108, 134}, + new ECBlocks(30, new ECB::new(13, 115), + new ECB::new(3, 116)), + new ECBlocks(28, new ECB::new(2, 46), + new ECB::new(29, 47)), + new ECBlocks(30, new ECB::new(42, 24), + new ECB::new(1, 25)), + new ECBlocks(30, new ECB::new(23, 15), + new ECB::new(28, 16))), + new Version(32, new int[]{6, 34, 60, 86, 112, 138}, + new ECBlocks(30, new ECB::new(17, 115)), + new ECBlocks(28, new ECB::new(10, 46), + new ECB::new(23, 47)), + new ECBlocks(30, new ECB::new(10, 24), + new ECB::new(35, 25)), + new ECBlocks(30, new ECB::new(19, 15), + new ECB::new(35, 16))), + new Version(33, new int[]{6, 30, 58, 86, 114, 142}, + new ECBlocks(30, new ECB::new(17, 115), + new ECB::new(1, 116)), + new ECBlocks(28, new ECB::new(14, 46), + new ECB::new(21, 47)), + new ECBlocks(30, new ECB::new(29, 24), + new ECB::new(19, 25)), + new ECBlocks(30, new ECB::new(11, 15), + new ECB::new(46, 16))), + new Version(34, new int[]{6, 34, 62, 90, 118, 146}, + new ECBlocks(30, new ECB::new(13, 115), + new ECB::new(6, 116)), + new ECBlocks(28, new ECB::new(14, 46), + new ECB::new(23, 47)), + new ECBlocks(30, new ECB::new(44, 24), + new ECB::new(7, 25)), + new ECBlocks(30, new ECB::new(59, 16), + new ECB::new(1, 17))), + new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150}, + new ECBlocks(30, new ECB::new(12, 121), + new ECB::new(7, 122)), + new ECBlocks(28, new ECB::new(12, 47), + new ECB::new(26, 48)), + new ECBlocks(30, new ECB::new(39, 24), + new ECB::new(14, 25)), + new ECBlocks(30, new ECB::new(22, 15), + new ECB::new(41, 16))), + new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154}, + new ECBlocks(30, new ECB::new(6, 121), + new ECB::new(14, 122)), + new ECBlocks(28, new ECB::new(6, 47), + new ECB::new(34, 48)), + new ECBlocks(30, new ECB::new(46, 24), + new ECB::new(10, 25)), + new ECBlocks(30, new ECB::new(2, 15), + new ECB::new(64, 16))), + new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158}, + new ECBlocks(30, new ECB::new(17, 122), + new ECB::new(4, 123)), + new ECBlocks(28, new ECB::new(29, 46), + new ECB::new(14, 47)), + new ECBlocks(30, new ECB::new(49, 24), + new ECB::new(10, 25)), + new ECBlocks(30, new ECB::new(24, 15), + new ECB::new(46, 16))), + new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162}, + new ECBlocks(30, new ECB::new(4, 122), + new ECB::new(18, 123)), + new ECBlocks(28, new ECB::new(13, 46), + new ECB::new(32, 47)), + new ECBlocks(30, new ECB::new(48, 24), + new ECB::new(14, 25)), + new ECBlocks(30, new ECB::new(42, 15), + new ECB::new(32, 16))), + new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166}, + new ECBlocks(30, new ECB::new(20, 117), + new ECB::new(4, 118)), + new ECBlocks(28, new ECB::new(40, 47), + new ECB::new(7, 48)), + new ECBlocks(30, new ECB::new(43, 24), + new ECB::new(22, 25)), + new ECBlocks(30, new ECB::new(10, 15), + new ECB::new(67, 16))), + new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170}, + new ECBlocks(30, new ECB::new(19, 118), + new ECB::new(6, 119)), + new ECBlocks(28, new ECB::new(18, 47), + new ECB::new(31, 48)), + new ECBlocks(30, new ECB::new(34, 24), + new ECB::new(34, 25)), + new ECBlocks(30, new ECB::new(20, 15), + new ECB::new(61, 16))) + ]*/ + } + + /* + * {1, { + 7 , 1, 19, 0, 0, + 10, 1, 16, 0, 0, + 13, 1, 13, 0, 0, + 17, 1, 9 , 0, 0 + }}, + {2, { + 10, 1, 36, 0, 0, + 16, 1, 30, 0, 0, + 22, 1, 24, 0, 0, + 30, 1, 16, 0, 0, + }}, + {3, { + 15, 1, 57, 0, 0, + 28, 1, 44, 0, 0, + 36, 1, 36, 0, 0, + 48, 1, 24, 0, 0, + }}, + {4, { + 20, 1, 80, 0, 0, + 40, 1, 60, 0, 0, + 50, 1, 50, 0, 0, + 66, 1, 34, 0, 0, + }}, + {5, { + 26, 1, 108, 0, 0, + 52, 1, 82 , 0, 0, + 66, 1, 68 , 0, 0, + 88, 2, 46 , 0, 0, + }}, + {6, { + 34 , 1, 136, 0, 0, + 63 , 2, 106, 0, 0, + 84 , 2, 86 , 0, 0, + 112, 2, 58 , 0, 0, + }}, + {7, { + 42 , 1, 170, 0, 0, + 80 , 2, 132, 0, 0, + 104, 2, 108, 0, 0, + 138, 3, 72 , 0, 0, + }}, + {8, { + 48 , 2, 208, 0, 0, + 96 , 2, 160, 0, 0, + 128, 2, 128, 0, 0, + 168, 3, 87 , 0, 0, + }}, + {9, { + 60 , 2, 246, 0, 0, + 120, 2, 186, 0, 0, + 150, 3, 156, 0, 0, + 204, 3, 102, 0, 0, + }}, + {10, { + 68 , 2, 290, 0, 0, + 136, 2, 222, 0, 0, + 174, 3, 183, 0, 0, + 232, 4, 124, 0, 0, + }}, + {11, { + 80 , 2, 336, 0, 0, + 160, 4, 256, 0, 0, + 208, 4, 208, 0, 0, + 270, 5, 145, 0, 0, + }}, + {12, { + 92 , 2, 384, 0, 0, + 184, 4, 292, 0, 0, + 232, 4, 244, 0, 0, + 310, 5, 165, 0, 0, + }}, + {13, { + 108, 3, 432, 0, 0, + 208, 4, 332, 0, 0, + 264, 4, 276, 0, 0, + 348, 6, 192, 0, 0, + }}, + {14, { + 120, 3, 489, 0, 0, + 240, 4, 368, 0, 0, + 300, 5, 310, 0, 0, + 396, 6, 210, 0, 0, + }}, + }; + */ + pub fn build_model1_versions() -> Vec { + Vec::from([ + Version::new_model1( + 1, + vec![ + ECBlocks::new(7, vec![ECB::new(1, 19)]), + ECBlocks::new(10, vec![ECB::new(1, 16)]), + ECBlocks::new(13, vec![ECB::new(1, 13)]), + ECBlocks::new(17, vec![ECB::new(1, 9)]), + ], + ), + Version::new_model1( + 2, + vec![ + ECBlocks::new(10, vec![ECB::new(1, 36)]), + ECBlocks::new(16, vec![ECB::new(1, 30)]), + ECBlocks::new(22, vec![ECB::new(1, 24)]), + ECBlocks::new(30, vec![ECB::new(1, 16)]), + ], + ), + Version::new_model1( + 3, + vec![ + ECBlocks::new(15, vec![ECB::new(1, 57)]), + ECBlocks::new(28, vec![ECB::new(1, 44)]), + ECBlocks::new(36, vec![ECB::new(1, 36)]), + ECBlocks::new(48, vec![ECB::new(1, 24)]), + ], + ), + Version::new_model1( + 4, + vec![ + ECBlocks::new(20, vec![ECB::new(1, 80)]), + ECBlocks::new(40, vec![ECB::new(1, 60)]), + ECBlocks::new(50, vec![ECB::new(1, 50)]), + ECBlocks::new(66, vec![ECB::new(1, 34)]), + ], + ), + Version::new_model1( + 5, + vec![ + ECBlocks::new(26, vec![ECB::new(1, 108)]), + ECBlocks::new(52, vec![ECB::new(1, 82)]), + ECBlocks::new(66, vec![ECB::new(1, 68)]), + ECBlocks::new(88, vec![ECB::new(2, 46)]), + ], + ), + Version::new_model1( + 6, + vec![ + ECBlocks::new(34, vec![ECB::new(1, 136)]), + ECBlocks::new(63, vec![ECB::new(2, 106)]), + ECBlocks::new(84, vec![ECB::new(2, 86)]), + ECBlocks::new(112, vec![ECB::new(2, 58)]), + ], + ), + Version::new_model1( + 7, + vec![ + ECBlocks::new(42, vec![ECB::new(1, 170)]), + ECBlocks::new(80, vec![ECB::new(2, 132)]), + ECBlocks::new(104, vec![ECB::new(2, 108)]), + ECBlocks::new(138, vec![ECB::new(3, 72)]), + ], + ), + Version::new_model1( + 8, + vec![ + ECBlocks::new(48, vec![ECB::new(2, 208)]), + ECBlocks::new(96, vec![ECB::new(2, 160)]), + ECBlocks::new(128, vec![ECB::new(2, 128)]), + ECBlocks::new(168, vec![ECB::new(3, 87)]), + ], + ), + Version::new_model1( + 9, + vec![ + ECBlocks::new(60, vec![ECB::new(2, 246)]), + ECBlocks::new(120, vec![ECB::new(2, 186)]), + ECBlocks::new(150, vec![ECB::new(3, 156)]), + ECBlocks::new(204, vec![ECB::new(3, 102)]), + ], + ), + Version::new_model1( + 10, + vec![ + ECBlocks::new(68, vec![ECB::new(2, 290)]), + ECBlocks::new(136, vec![ECB::new(2, 222)]), + ECBlocks::new(174, vec![ECB::new(3, 183)]), + ECBlocks::new(232, vec![ECB::new(4, 124)]), + ], + ), + Version::new_model1( + 11, + vec![ + ECBlocks::new(80, vec![ECB::new(2, 336)]), + ECBlocks::new(160, vec![ECB::new(4, 256)]), + ECBlocks::new(208, vec![ECB::new(4, 208)]), + ECBlocks::new(270, vec![ECB::new(5, 145)]), + ], + ), + Version::new_model1( + 12, + vec![ + ECBlocks::new(92, vec![ECB::new(2, 384)]), + ECBlocks::new(184, vec![ECB::new(4, 292)]), + ECBlocks::new(232, vec![ECB::new(4, 244)]), + ECBlocks::new(310, vec![ECB::new(5, 165)]), + ], + ), + Version::new_model1( + 13, + vec![ + ECBlocks::new(108, vec![ECB::new(3, 432)]), + ECBlocks::new(208, vec![ECB::new(4, 332)]), + ECBlocks::new(264, vec![ECB::new(4, 276)]), + ECBlocks::new(348, vec![ECB::new(6, 192)]), + ], + ), + Version::new_model1( + 14, + vec![ + ECBlocks::new(120, vec![ECB::new(3, 489)]), + ECBlocks::new(240, vec![ECB::new(4, 368)]), + ECBlocks::new(300, vec![ECB::new(5, 310)]), + ECBlocks::new(396, vec![ECB::new(6, 210)]), + ], + ), + ]) + } + + pub fn build_rmqr_versions() -> Vec { + Vec::from([ + Version::new( + 1, + Vec::from([21]), + [ + // R7x43 + // 4 `ECBlocks`, one for each `ecLevel` - rMQR only uses M & H but using 2 dummies to keep `ecLevel` index same as QR Code + // Each begins with no. of error correction codewords divided by no. of error correction blocks, followed by 2 `ECBlock`s + // Each `ECBlock` begins with no. of error correction blocks followed by no. of data codewords per block + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), // L (dummy) - also used to differentiate rMQR from Model2 in `Version::Version()` + ECBlocks::new(7, Vec::from([ECB::new(1, 6)])), // M + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), // Q (dummy) + ECBlocks::new(10, Vec::from([ECB::new(1, 3)])), // H + ], + ), + Version::new( + 2, + Vec::from([19, 39]), + [ + // R7x59 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(9, Vec::from([ECB::new(1, 12)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(14, Vec::from([ECB::new(1, 7)])), + ], + ), + Version::new( + 3, + Vec::from([25, 51]), + [ + // R7x77 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(12, Vec::from([ECB::new(1, 20)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(1, 10)])), + ], + ), + Version::new( + 4, + Vec::from([23, 49, 75]), + [ + // R7x99 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 28)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(30, Vec::from([ECB::new(1, 14)])), + ], + ), + Version::new( + 5, + Vec::from([27, 55, 83, 111]), + [ + // R7x139 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(1, 44)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(2, 12)])), + ], + ), + Version::new( + 6, + Vec::from([21]), + [ + // R9x43 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(9, Vec::from([ECB::new(1, 12)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(14, Vec::from([ECB::new(1, 7)])), + ], + ), + Version::new( + 7, + Vec::from([19, 39]), + [ + // R9x59 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(12, Vec::from([ECB::new(1, 21)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(1, 11)])), + ], + ), + Version::new( + 8, + Vec::from([25, 51]), + [ + // R9x77 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(18, Vec::from([ECB::new(1, 31)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 8), ECB::new(1, 9)])), + ], + ), + Version::new( + 9, + Vec::from([23, 49, 75]), + [ + // R9x99 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(1, 42)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(2, 11)])), + ], + ), + Version::new( + 10, + Vec::from([27, 55, 83, 111]), + [ + // R9x139 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(18, Vec::from([ECB::new(1, 31), ECB::new(1, 32)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(3, 11)])), + ], + ), + Version::new( + 11, + Vec::from([]), + [ + // R11x27 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(8, Vec::from([ECB::new(1, 7)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(10, Vec::from([ECB::new(1, 5)])), + ], + ), + Version::new( + 12, + Vec::from([21]), + [ + // R11x43 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(12, Vec::from([ECB::new(1, 19)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(1, 11)])), + ], + ), + Version::new( + 13, + Vec::from([19, 39]), + [ + // R11x59 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 31)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 7), ECB::new(1, 8)])), + ], + ), + Version::new( + 14, + Vec::from([25, 51]), + [ + // R11x77 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(1, 43)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(1, 11), ECB::new(1, 12)])), + ], + ), + Version::new( + 15, + Vec::from([23, 49, 75]), + [ + // R11x99 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 28), ECB::new(1, 29)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(30, Vec::from([ECB::new(1, 14), ECB::new(1, 15)])), + ], + ), + Version::new( + 16, + Vec::from([27, 55, 83, 111]), + [ + // R11x139 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(2, 42)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(30, Vec::from([ECB::new(3, 14)])), + ], + ), + Version::new( + 17, + Vec::from([]), + [ + // R13x27 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(9, Vec::from([ECB::new(1, 12)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(14, Vec::from([ECB::new(1, 7)])), + ], + ), + Version::new( + 18, + Vec::from([21]), + [ + // R13x43 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(14, Vec::from([ECB::new(1, 27)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(28, Vec::from([ECB::new(1, 13)])), + ], + ), + Version::new( + 19, + Vec::from([19, 39]), + [ + // R13x59 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(1, 38)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(2, 10)])), + ], + ), + Version::new( + 20, + Vec::from([25, 51]), + [ + // R13x77 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(1, 26), ECB::new(1, 27)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(28, Vec::from([ECB::new(1, 14), ECB::new(1, 15)])), + ], + ), + Version::new( + 21, + Vec::from([23, 49, 75]), + [ + // R13x99 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(1, 36), ECB::new(1, 37)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(26, Vec::from([ECB::new(1, 11), ECB::new(2, 12)])), + ], + ), + Version::new( + 22, + Vec::from([27, 55, 83, 111]), + [ + // R13x139 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(2, 35), ECB::new(1, 36)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(28, Vec::from([ECB::new(2, 13), ECB::new(2, 14)])), + ], + ), + Version::new( + 23, + Vec::from([21]), + [ + // R15x43 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(18, Vec::from([ECB::new(1, 33)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(18, Vec::from([ECB::new(1, 7), ECB::new(1, 8)])), + ], + ), + Version::new( + 24, + Vec::from([19, 39]), + [ + // R15x59 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(26, Vec::from([ECB::new(1, 48)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(2, 13)])), + ], + ), + Version::new( + 25, + Vec::from([25, 51]), + [ + // R15x77 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(18, Vec::from([ECB::new(1, 33), ECB::new(1, 34)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(2, 10), ECB::new(1, 11)])), + ], + ), + Version::new( + 26, + Vec::from([23, 49, 75]), + [ + // R15x99 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(2, 44)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(4, 12)])), + ], + ), + Version::new( + 27, + Vec::from([27, 55, 83, 111]), + [ + // R15x139 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(24, Vec::from([ECB::new(2, 42), ECB::new(1, 43)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(26, Vec::from([ECB::new(1, 13), ECB::new(4, 14)])), + ], + ), + Version::new( + 28, + Vec::from([21]), + [ + // R17x43 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(1, 39)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(1, 10), ECB::new(1, 11)])), + ], + ), + Version::new( + 29, + Vec::from([19, 39]), + [ + // R17x59 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(16, Vec::from([ECB::new(2, 28)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(30, Vec::from([ECB::new(2, 14)])), + ], + ), + Version::new( + 30, + Vec::from([25, 51]), + [ + // R17x77 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(22, Vec::from([ECB::new(2, 39)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(28, Vec::from([ECB::new(1, 12), ECB::new(2, 13)])), + ], + ), + Version::new( + 31, + Vec::from([23, 49, 75]), + [ + // R17x99 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(2, 33), ECB::new(1, 34)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(26, Vec::from([ECB::new(4, 14)])), + ], + ), + Version::new( + 32, + Vec::from([27, 55, 83, 111]), + [ + // R17x139 + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(20, Vec::from([ECB::new(4, 38)])), + ECBlocks::new(0, Vec::from([ECB::new(0, 0)])), + ECBlocks::new(26, Vec::from([ECB::new(2, 12), ECB::new(4, 13)])), + ], + ), + ]) + } +} + +/* + +/** + * See ISO/IEC 23941:2022 Annex D, Table D.1 - Column coordinates of centre module of alignment patterns + * See ISO/IEC 23941:2022 7.5.1, Table 8 - Error correction characteristics for rMQR + */ + static const Version allVersions[] = { + // Version number, alignment pattern centres, `ECBlocks` + { 1, {21}, { // R7x43 + // 4 `ECBlocks`, one for each `ecLevel` - rMQR only uses M & H but using 2 dummies to keep `ecLevel` index same as QR Code + // Each begins with no. of error correction codewords divided by no. of error correction blocks, followed by 2 `ECBlock`s + // Each `ECBlock` begins with no. of error correction blocks followed by no. of data codewords per block + 0, 0, 0, 0, 0, // L (dummy) - also used to differentiate rMQR from Model2 in `Version::Version()` + 7, 1, 6, 0, 0, // M + 0, 0, 0, 0, 0, // Q (dummy) + 10, 1, 3, 0, 0, // H + }}, + { 2, {19, 39}, { // R7x59 + 0, 0, 0, 0, 0, + 9, 1, 12, 0, 0, + 0, 0, 0, 0, 0, + 14, 1, 7, 0, 0, + }}, + { 3, {25, 51}, { // R7x77 + 0, 0, 0, 0, 0, + 12, 1, 20, 0, 0, + 0, 0, 0, 0, 0, + 22, 1, 10, 0, 0, + }}, + { 4, {23, 49, 75}, { // R7x99 + 0, 0, 0, 0, 0, + 16, 1, 28, 0, 0, + 0, 0, 0, 0, 0, + 30, 1, 14, 0, 0, + }}, + { 5, {27, 55, 83, 111}, { // R7x139 + 0, 0, 0, 0, 0, + 24, 1, 44, 0, 0, + 0, 0, 0, 0, 0, + 22, 2, 12, 0, 0, + }}, + { 6, {21}, { // R9x43 + 0, 0, 0, 0, 0, + 9, 1, 12, 0, 0, + 0, 0, 0, 0, 0, + 14, 1, 7, 0, 0, + }}, + { 7, {19, 39}, { // R9x59 + 0, 0, 0, 0, 0, + 12, 1, 21, 0, 0, + 0, 0, 0, 0, 0, + 22, 1, 11, 0, 0, + }}, + { 8, {25, 51}, { // R9x77 + 0, 0, 0, 0, 0, + 18, 1, 31, 0, 0, + 0, 0, 0, 0, 0, + 16, 1, 8, 1, 9, + }}, + { 9, {23, 49, 75}, { // R9x99 + 0, 0, 0, 0, 0, + 24, 1, 42, 0, 0, + 0, 0, 0, 0, 0, + 22, 2, 11, 0, 0, + }}, + {10, {27, 55, 83, 111}, { // R9x139 + 0, 0, 0, 0, 0, + 18, 1, 31, 1, 32, + 0, 0, 0, 0, 0, + 22, 3, 11, 0, 0, + }}, + {11, {}, { // R11x27 + 0, 0, 0, 0, 0, + 8, 1, 7, 0, 0, + 0, 0, 0, 0, 0, + 10, 1, 5, 0, 0, + }}, + {12, {21}, { // R11x43 + 0, 0, 0, 0, 0, + 12, 1, 19, 0, 0, + 0, 0, 0, 0, 0, + 20, 1, 11, 0, 0, + }}, + {13, {19, 39}, { // R11x59 + 0, 0, 0, 0, 0, + 16, 1, 31, 0, 0, + 0, 0, 0, 0, 0, + 16, 1, 7, 1, 8, + }}, + {14, {25, 51}, { // R11x77 + 0, 0, 0, 0, 0, + 24, 1, 43, 0, 0, + 0, 0, 0, 0, 0, + 22, 1, 11, 1, 12, + }}, + {15, {23, 49, 75}, { // R11x99 + 0, 0, 0, 0, 0, + 16, 1, 28, 1, 29, + 0, 0, 0, 0, 0, + 30, 1, 14, 1, 15, + }}, + {16, {27, 55, 83, 111}, { // R11x139 + 0, 0, 0, 0, 0, + 24, 2, 42, 0, 0, + 0, 0, 0, 0, 0, + 30, 3, 14, 0, 0, + }}, + {17, {}, { // R13x27 + 0, 0, 0, 0, 0, + 9, 1, 12, 0, 0, + 0, 0, 0, 0, 0, + 14, 1, 7, 0, 0, + }}, + {18, {21}, { // R13x43 + 0, 0, 0, 0, 0, + 14, 1, 27, 0, 0, + 0, 0, 0, 0, 0, + 28, 1, 13, 0, 0, + }}, + {19, {19, 39}, { // R13x59 + 0, 0, 0, 0, 0, + 22, 1, 38, 0, 0, + 0, 0, 0, 0, 0, + 20, 2, 10, 0, 0, + }}, + {20, {25, 51}, { // R13x77 + 0, 0, 0, 0, 0, + 16, 1, 26, 1, 27, + 0, 0, 0, 0, 0, + 28, 1, 14, 1, 15, + }}, + {21, {23, 49, 75}, { // R13x99 + 0, 0, 0, 0, 0, + 20, 1, 36, 1, 37, + 0, 0, 0, 0, 0, + 26, 1, 11, 2, 12, + }}, + {22, {27, 55, 83, 111}, { // R13x139 + 0, 0, 0, 0, 0, + 20, 2, 35, 1, 36, + 0, 0, 0, 0, 0, + 28, 2, 13, 2, 14, + }}, + {23, {21}, { // R15x43 + 0, 0, 0, 0, 0, + 18, 1, 33, 0, 0, + 0, 0, 0, 0, 0, + 18, 1, 7, 1, 8, + }}, + {24, {19, 39}, { // R15x59 + 0, 0, 0, 0, 0, + 26, 1, 48, 0, 0, + 0, 0, 0, 0, 0, + 24, 2, 13, 0, 0, + }}, + {25, {25, 51}, { // R15x77 + 0, 0, 0, 0, 0, + 18, 1, 33, 1, 34, + 0, 0, 0, 0, 0, + 24, 2, 10, 1, 11, + }}, + {26, {23, 49, 75}, { // R15x99 + 0, 0, 0, 0, 0, + 24, 2, 44, 0, 0, + 0, 0, 0, 0, 0, + 22, 4, 12, 0, 0, + }}, + {27, {27, 55, 83, 111}, { // R15x139 + 0, 0, 0, 0, 0, + 24, 2, 42, 1, 43, + 0, 0, 0, 0, 0, + 26, 1, 13, 4, 14, + }}, + {28, {21}, { // R17x43 + 0, 0, 0, 0, 0, + 22, 1, 39, 0, 0, + 0, 0, 0, 0, 0, + 20, 1, 10, 1, 11, + }}, + {29, {19, 39}, { // R17x59 + 0, 0, 0, 0, 0, + 16, 2, 28, 0, 0, + 0, 0, 0, 0, 0, + 30, 2, 14, 0, 0, + }}, + {30, {25, 51}, { // R17x77 + 0, 0, 0, 0, 0, + 22, 2, 39, 0, 0, + 0, 0, 0, 0, 0, + 28, 1, 12, 2, 13, + }}, + {31, {23, 49, 75}, { // R17x99 + 0, 0, 0, 0, 0, + 20, 2, 33, 1, 34, + 0, 0, 0, 0, 0, + 26, 4, 14, 0, 0, + }}, + {32, {27, 55, 83, 111}, { // R17x139 + 0, 0, 0, 0, 0, + 20, 4, 38, 0, 0, + 0, 0, 0, 0, 0, + 26, 2, 12, 4, 13, + }}, + }; + + if (number < 1 || number > Size(allVersions)) + return nullptr; + return allVersions + number - 1; + +*/ diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R17x139.png b/test_resources/blackbox/cpp/rmqrcode-1/R17x139.png new file mode 100644 index 0000000000000000000000000000000000000000..19d17e352d281b395125499f8cf3b857e8154553 GIT binary patch literal 1113 zcmV-f1g86mP)HzuAx7|-I<6Y&ur@hU;`m@gM=?fE=T^>i}EXsxg7 zx<+o>w(lvWob$uOB6&S+99tTDTF=`yraLtYWrY&SNXk{HDAb;^P;;YhX)T1hr^Wc% z)c67_1GDyU#g$>NQ%VFgmX_mHjA0xHCeje|ym(bwUeoJTjbVOhc_d@O@P4QOSXZFx zIQ&VNj01WNDt=18p{3Cn0LS8WZ~++hG|E4X2h5tqs&J#0V_uF69LsrPH6%@6TNz2# zCrXpvMGD2eo(wy+@USXw+uNqJx3E^eG-ApIpvSTXr~rw927o?*$G+4HBRVk8j{wxC zgeS_yVux){Q!?3PO|Q9nsVBy%8k$VqDBXVnnSex-p=G0}U>)n*rXXSEdI3!CW$7Ul z8U^IT$l@&2r(LKFKwpm;UsI}JBGbv_44voxHO4UQtS5kZ(Ui{Mq*+uTNt#e<6^iM5 z)a9kr_#!HQ<5yjNOzC6R(F+xB+Zp;$0WM5ZPXhwl&(5n@rZ8ekq{*0PExn@HX>XP> z$aug)9xV;Pol-wdoqiQHJ*O6-TpD4%pw?K<)AkLDy-?StRsfh zyMU$#6w45XeP=aU(!6vAi-GBFQ~H}!`3XuwlSLI5!~zYqCa(uv48XM46Oh@Y#b0`7 z;GU4O$&Ei2sk+-L-UG#qxl_TldRD`1*dUVLwiWKR@yJ-3HD6#cY-vg$a|V`U@h(`m zZRtHwChPVYswi%0#7T5$uf3DGVyMqL0~|~9dpmr(k{Js|ya!68>&i9uJ8?4AkXrM; zoyj^RW&@eFCF}444YWhAE@09rm3i|4?NU6@}5fD5J zb)awOp$>hLA@rfY+|Do=3;W&KMOHkc?EjNmI>+LQ3!{w4iKbDt!7*=Q_&)cW-J(gfqtoR)IkOJP@-2Kr~7pOmOwaQxiS*>J$Oqdct zL)|EET`U;tbem+P+a4BfW5tL>1FhL7nebjH=6U`Xhfe0%p%ZzhUT_rlT2xDV@v$M9 fJ`P|%;#J}=iPu^Vu(t@k00000NkvXXu0mjfK}-rG literal 0 HcmV?d00001 diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R17x139.txt b/test_resources/blackbox/cpp/rmqrcode-1/R17x139.txt new file mode 100644 index 0000000..ea78e30 --- /dev/null +++ b/test_resources/blackbox/cpp/rmqrcode-1/R17x139.txt @@ -0,0 +1 @@ +3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081 \ No newline at end of file diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.metadata.txt b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.metadata.txt new file mode 100644 index 0000000..f29c889 --- /dev/null +++ b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.metadata.txt @@ -0,0 +1 @@ +symbologyIdentifier=]Q1 diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.png b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.png new file mode 100644 index 0000000000000000000000000000000000000000..d62eb47c6e911eea478fcd4862accd088fb8660e GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^5kM@!$P6S`1Zb53DYgKg5ZC|z{{xw!hc4FvDb50q z$YKTtZeb8+WSBKa0w@^e>Eal|aXt3jLeT>XJg$LRo18^gyZ$}Xu+X)%O)HFfvSgI- zt11rv*q=@_tau#Q8s40lvg_n#bG7t~DTQ+?cjs)>@b+GR>5;`NiLDxQW+uIq%4fCw e&!2QISYF*QczN8m#nC`p7(8A5T-G@yGywp#cRbes literal 0 HcmV?d00001 diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.txt b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.txt new file mode 100644 index 0000000..8b22b22 --- /dev/null +++ b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H.txt @@ -0,0 +1 @@ +,, \ No newline at end of file diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt new file mode 100644 index 0000000..da82284 --- /dev/null +++ b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt @@ -0,0 +1 @@ +isInverted=true diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.png b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.png new file mode 100644 index 0000000000000000000000000000000000000000..138a06562d78574cda2c96553df80df78ca5228f GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^VL;5s$P6U=T`To~6kC8#h%1o(|NsBeoAd7iS-zew zjv*Y^W6y2mJYc}%>ew9Z*!GbjD)zy*#xTwue;1S-EGvJyRY-wh>)pg`t^0zB?UPm4 zFZ=hRNJug}ar)jpr>tf8KHGg{Ec;= Date: Sat, 13 Jan 2024 13:56:43 -0600 Subject: [PATCH 06/15] chore: convert bitwise is_odd check to is_odd() --- src/common/cpp_essentials/base_extentions/qrcode_version.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index 2ba7059..039fbf3 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -5,6 +5,8 @@ */ // SPDX-License-Identifier: Apache-2.0 +use num::Integer; + use crate::common::{BitMatrix, Result}; use crate::qrcode::cpp_port::Type; use crate::qrcode::decoder::{ @@ -183,8 +185,8 @@ impl Version { let width = bitMatrix.width() as i32; let height = bitMatrix.height() as i32; if width != height - && (width & 1 != 0) - && (height & 1 != 0) + && width.is_odd() + && height.is_odd() && width >= 27 && width <= 139 && height >= 7 From 6a06762fd8a2bf526b74852a1aff45578fb8a30a Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 14:02:02 -0600 Subject: [PATCH 07/15] chore: clippy cleanup --- .../base_extentions/qr_formatinformation.rs | 6 ++-- .../base_extentions/qrcode_version.rs | 14 ++++---- src/qrcode/cpp_port/bitmatrix_parser.rs | 6 ++-- src/qrcode/cpp_port/detector.rs | 33 +++++++++---------- src/qrcode/cpp_port/qr_cpp_reader.rs | 8 ++--- src/qrcode/cpp_port/test/RMQRDecoderTest.rs | 2 +- src/qrcode/decoder/mode.rs | 18 +++++----- src/qrcode/decoder/version.rs | 4 +-- 8 files changed, 41 insertions(+), 50 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 533a6c3..bee5c63 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -103,7 +103,7 @@ impl FormatInformation { pub fn DecodeRMQR(formatInfoBits1: u32, formatInfoBits2: u32) -> Self { //FormatInformation fi; let mirror18Bits = |bits: u32| bits.reverse_bits() >> 14; - let mut fi = if (formatInfoBits2 != 0) { + let mut fi = if formatInfoBits2 != 0 { Self::FindBestFormatInfoRMQR( &[formatInfoBits1, mirror18Bits(formatInfoBits1)], &[formatInfoBits2, mirror18Bits(formatInfoBits2)], @@ -218,7 +218,7 @@ impl FormatInformation { let pattern = l_pattern ^ mask; // Find the pattern with fewest bits differing let hammingDist = ((bits[bitsIndex] ^ mask) ^ pattern).count_ones(); - if (hammingDist < fi.hammingDistance) { + if hammingDist < fi.hammingDistance { fi.mask = mask; // store the used mask to discriminate between types/models fi.data = pattern >> 12; // drop the 12 BCH error correction bits fi.hammingDistance = hammingDist; @@ -229,7 +229,7 @@ impl FormatInformation { }; best(bits, &MASKED_PATTERNS, FORMAT_INFO_MASK_RMQR); - if (!subbits.is_empty()) + if !subbits.is_empty() // TODO probably remove if `sampleRMQR()` done properly { best(subbits, &MASKED_PATTERNS_SUB, FORMAT_INFO_MASK_RMQR_SUB); diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index 039fbf3..0bdb1a2 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -77,10 +77,10 @@ impl Version { pub fn rMQR(version_number: u32) -> Result { let version_number = version_number as usize; - if (version_number < 1 || version_number > (RMQR_VERSIONS.len())) { + if version_number < 1 || version_number > (RMQR_VERSIONS.len()) { Err(Exceptions::ILLEGAL_ARGUMENT) } else { - Ok(&RMQR_VERSIONS[version_number as usize - 1]) + Ok(&RMQR_VERSIONS[version_number - 1]) } } @@ -146,7 +146,7 @@ impl Version { pub fn HasMicroSize(bitMatrix: &BitMatrix) -> bool { let size = bitMatrix.height(); - size == bitMatrix.width() && size >= 11 && size <= 17 && (size % 2) == 1 + size == bitMatrix.width() && (11..=17).contains(&size) && (size % 2) == 1 } pub fn HasRMQRSize(bitMatrix: &BitMatrix) -> bool { @@ -187,10 +187,8 @@ impl Version { if width != height && width.is_odd() && height.is_odd() - && width >= 27 - && width <= 139 - && height >= 7 - && height <= 17 + && (27..=139).contains(&width) + && (7..=17).contains(&height) { for i in 0..dimsVersionRMQR.len() { // for (int i = 0; i < Size(dimsVersionRMQR); i++){ @@ -199,6 +197,6 @@ impl Version { } } } - return -1; + -1 } } diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index a0c9f31..9417f4b 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -52,7 +52,7 @@ pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result return Ok(FormatInformation::DecodeMQR(formatInfoBits as u32)); } - if (Version::HasRMQRSize(bitMatrix)) { + if Version::HasRMQRSize(bitMatrix) { // Read top-left format info bits let mut formatInfoBits1 = 0; for y in (1..=3).rev() { @@ -378,7 +378,7 @@ pub fn ReadRMQRCodewords( // for (int col = 0; col < 2; col++) { let xx = x - col; // Ignore bits covered by the function pattern - if (!functionPattern.get(xx as u32, y)) { + if !functionPattern.get(xx as u32, y) { // Read a bit AppendBit( &mut currentByte, @@ -399,7 +399,7 @@ pub fn ReadRMQRCodewords( x -= 2 } - if ((result.len()) != version.getTotalCodewords() as usize) { + if (result.len()) != version.getTotalCodewords() as usize { return Err(Exceptions::FORMAT); } diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index a43fb52..91c4f26 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -5,12 +5,12 @@ use crate::{ }, DefaultGridSampler, GridSampler, Result, SamplerControl, }, - point, point_i, + point_i, qrcode::{ decoder::{FormatInformation, Version, VersionRef}, detector::QRCodeDetectorResult, }, - Exceptions, PointF, + Exceptions, }; use multimap::MultiMap; use num::Integer; @@ -24,7 +24,7 @@ use crate::{ }, BitMatrix, PerspectiveTransform, Quadrilateral, }, - point_f, Point, PointI, + point_f, Point, }; #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] @@ -914,8 +914,7 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result { let moduleSize: f32 = (fpWidth as f32) / 7.0; let dimension = (width as f32 / moduleSize).floor() as u32; - if dimension < MIN_MODULES - || dimension > MAX_MODULES + if !(MIN_MODULES..=MAX_MODULES).contains(&dimension) || !image.is_in(point_f( left as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, top as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, @@ -969,7 +968,7 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); - if (!found) { + if !found { return Err(Exceptions::NOT_FOUND); } let right = left + width - 1; @@ -994,7 +993,7 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { let mut subdiagonal: SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)) .readPatternFromBlack(1, None) .ok_or(Exceptions::ILLEGAL_STATE)?; - if (subdiagonal.len() == 5 && subdiagonal[4] > subdiagonal[3]) { + if subdiagonal.len() == 5 && subdiagonal[4] > subdiagonal[3] { // Sub pattern has no separator so can run off along the diagonal subdiagonal[4] = subdiagonal[3]; // Hack it back to previous } @@ -1024,23 +1023,21 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { let dimW = (width as f32 / moduleSize as f32).floor() as u32; let dimH = (height as f32 / moduleSize as f32).floor() as u32; - if (dimW == dimH + if dimW == dimH || dimW.is_even() || dimH.is_even() - || dimW < MIN_MODULES_W - || dimW > MAX_MODULES_W - || dimH < MIN_MODULES_H - || dimH > MAX_MODULES_H + || !(MIN_MODULES_W..=MAX_MODULES_W).contains(&dimW) + || !(MIN_MODULES_H..=MAX_MODULES_H).contains(&dimH) || !image.is_in(point_f( left as f32 + moduleSize / 2.0 + (dimW as f32 - 1.0) * moduleSize, top as f32 + moduleSize / 2.0 + (dimH as f32 - 1.0) * moduleSize, - ))) + )) { return Err(Exceptions::NOT_FOUND); } // Vertical corner finder patterns - if (dimH > 7) { + if dimH > 7 { // None for R7 let corner: CornerEdgePattern = EdgeTracer::new(image, tr, point_i(0, 1)) .readPatternFromBlack(1, None) @@ -1050,7 +1047,7 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { return Err(Exceptions::NOT_FOUND); } - if (dimH > 9) { + if dimH > 9 { // No bottom left for R9 let corner: CornerEdgePattern = EdgeTracer::new(image, bl, point_i(0, -1)) .readPatternFromBlack(1, None) @@ -1241,7 +1238,7 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result= 0x00 && bits <= 0x05) || (bits >= 0x07 && bits <= 0x09) || bits == 0x0d) { - return Mode::try_from(bits as u32); - } + } else if (0x00..=0x05).contains(&bits) || (0x07..=0x09).contains(&bits) || bits == 0x0d { + return Mode::try_from(bits as u32); } Err(Exceptions::format_with("Invalid codec mode")) @@ -196,7 +194,7 @@ impl Mode { } } - if (version.isRMQR()) { + if version.isRMQR() { // See ISO/IEC 23941:2022 7.4.1, Table 3 - Number of bits of character count indicator const numeric: [u32; 32] = [ 4, 5, 6, 7, 7, 5, 6, 7, 7, 8, 4, 6, 7, 7, 8, 8, 5, 6, 7, 7, 8, 8, 7, 7, 8, 8, 9, 7, @@ -214,7 +212,7 @@ impl Mode { 2, 3, 4, 5, 5, 3, 4, 5, 5, 6, 2, 4, 5, 5, 6, 6, 3, 5, 5, 6, 6, 7, 5, 5, 6, 6, 7, 5, 6, 6, 6, 7, ]; - match (self) { + match self { Mode::NUMERIC => return numeric[number - 1], Mode::ALPHANUMERIC => return alphanum[number - 1], Mode::BYTE => return byte[number - 1], diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index 1b0a8ff..f5d00f1 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -201,7 +201,7 @@ impl Version { * See ISO 18004:2006 Annex E */ pub fn buildFunctionPattern(&self) -> Result { - if (self.isRMQR()) { + if self.isRMQR() { let dimension = Version::DimensionOfVersionRMQR(self.versionNumber); let mut bitMatrix = BitMatrix::new(dimension.x as u32, dimension.y as u32)?; @@ -240,7 +240,7 @@ impl Version { // Top right corner finder bitMatrix.set((dimension.x - 2) as u32, 1); - if (dimension.y > 9) { + if dimension.y > 9 { // Bottom left corner finder bitMatrix.set(1, (dimension.y - 2) as u32); } From c7422198d6151dd4e28e25d939bb3a0d2a5ab343 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 15:20:15 -0600 Subject: [PATCH 08/15] port: QRCode: restructure Format and Version code after rRMQ addition https://github.com/zxing-cpp/zxing-cpp/commit/f27106c78c78287bf74d22958c83a09da6986e92 --- .../base_extentions/qr_formatinformation.rs | 12 +- .../base_extentions/qrcode_version.rs | 129 +++++++++------ src/qrcode/cpp_port/bitmatrix_parser.rs | 4 +- src/qrcode/cpp_port/detector.rs | 152 ++++++------------ src/qrcode/cpp_port/test/QRVersionTest.rs | 4 +- src/qrcode/decoder/format_information.rs | 3 - src/qrcode/decoder/version.rs | 32 ++-- src/rxing_result_point.rs | 14 +- 8 files changed, 170 insertions(+), 180 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index bee5c63..2dd548d 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -102,23 +102,23 @@ impl FormatInformation { */ pub fn DecodeRMQR(formatInfoBits1: u32, formatInfoBits2: u32) -> Self { //FormatInformation fi; - let mirror18Bits = |bits: u32| bits.reverse_bits() >> 14; + let mut fi = if formatInfoBits2 != 0 { Self::FindBestFormatInfoRMQR( - &[formatInfoBits1, mirror18Bits(formatInfoBits1)], - &[formatInfoBits2, mirror18Bits(formatInfoBits2)], + &[formatInfoBits1], + &[formatInfoBits2], ) } else { // TODO probably remove if `sampleRMQR()` done properly - Self::FindBestFormatInfoRMQR(&[formatInfoBits1, mirror18Bits(formatInfoBits1)], &[]) + Self::FindBestFormatInfoRMQR(&[formatInfoBits1], &[]) }; // Bit 6 is error correction (M/H), and bits 0-5 version. fi.error_correction_level = ErrorCorrectionLevel::ECLevelFromBits(((fi.data >> 5) as u8 & 1) << 1, false); // Shift to match QRCode M/H fi.data_mask = 4; // ((y / 2) + (x / 3)) % 2 == 0 - fi.rMQRVersion = fi.data as u8 & 0x1F; - fi.isMirrored = fi.bitsIndex > 1; + fi.microVersion = (fi.data & 0x1F) + 1; + fi.isMirrored = false; // TODO: implement mirrored format bit reading fi } diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index 0bdb1a2..b6b7084 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -15,7 +15,7 @@ use crate::qrcode::decoder::{ }; use crate::{point, Exceptions, PointI}; -const dimsVersionRMQR: [PointI; 32] = [ +const RMQR_SIZES: [PointI; 32] = [ point(43, 7), point(59, 7), point(77, 7), @@ -144,59 +144,96 @@ impl Version { Type::const_eq(self.qr_type, Type::RectMicro) } - pub fn HasMicroSize(bitMatrix: &BitMatrix) -> bool { - let size = bitMatrix.height(); - size == bitMatrix.width() && (11..=17).contains(&size) && (size % 2) == 1 + pub fn SymbolSize(version: u32, qr_type: Type) -> PointI { + let version = version as i32; + + let square = |s: i32| point(s, s); + let valid = |v: i32, max: i32| v >= 1 && v <= max; + + match (qr_type) { + Type::Model1 => { + if valid(version, 32) { + square(17 + 4 * version) + } else { + PointI::default() + } + } + Type::Model2 => { + if valid(version, 40) { + square(17 + 4 * version) + } else { + PointI::default() + } + } + Type::Micro => { + if valid(version, 4) { + square(9 + 2 * version) + } else { + PointI::default() + } + } + Type::RectMicro => { + if valid(version, 32) { + RMQR_SIZES[(version - 1) as usize] + } else { + PointI::default() + } + } + } } - pub fn HasRMQRSize(bitMatrix: &BitMatrix) -> bool { - Self::getVersionRMQR(bitMatrix) != -1 + pub fn IsValidSize(size: PointI, qr_type: Type) -> bool { + match (qr_type) { + Type::Model1 => size.x == size.y && size.x >= 21 && size.x <= 145 && (size.x % 4 == 1), + Type::Model2 => size.x == size.y && size.x >= 21 && size.x <= 177 && (size.x % 4 == 1), + Type::Micro => size.x == size.y && size.x >= 11 && size.x <= 17 && (size.x % 2 == 1), + Type::RectMicro => { + size.x != size.y + && size.x.is_odd() + && size.y.is_odd() + && size.x >= 27 + && size.x <= 139 + && size.y >= 7 + && size.y <= 17 + && Self::IndexOf(&RMQR_SIZES, size) != -1 + } + } + } + pub fn HasValidSizeType(bitMatrix: &BitMatrix, qr_type: Type) -> bool { + return Self::IsValidSize( + point(bitMatrix.width() as i32, bitMatrix.height() as i32), + qr_type, + ); } - pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool { - let size = bitMatrix.height(); - if bitMatrix.width() != size { - Self::HasRMQRSize(bitMatrix) + pub fn HasValidSize(matrix: &BitMatrix) -> bool { + return Self::HasValidSizeType(matrix, Type::Model1) + || Self::HasValidSizeType(matrix, Type::Model2) + || Self::HasValidSizeType(matrix, Type::Micro) + || Self::HasValidSizeType(matrix, Type::RectMicro); + } + + fn IndexOf(points: &[PointI], search: PointI) -> i32 { + RMQR_SIZES + .iter() + .position(|p| *p == search) + .and_then(|x| Some(x as i32)) + .unwrap_or(-1) + } + + pub fn NumberPoint(size: PointI) -> u32 { + if (size.x != size.y) { + return (Self::IndexOf(&RMQR_SIZES, size) + 1) as u32; + } else if (Self::IsValidSize(size, Type::Model2)) { + return ((size.x as i32 - 17) / 4) as u32; + } else if (Self::IsValidSize(size, Type::Micro)) { + return ((size.x as i32 - 9) / 2) as u32; } else { - Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1) + return 0; } } pub fn Number(bitMatrix: &BitMatrix) -> u32 { - if bitMatrix.width() != bitMatrix.height() { - Self::getVersionRMQR(bitMatrix) as u32 + 1 - } else if !Self::HasValidSize(bitMatrix) { - 0 - } else { - let isMicro = Self::HasMicroSize(bitMatrix); - (bitMatrix.height() - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro) - } - } - - pub fn DimensionOfVersionRMQR(version_number: u32) -> PointI { - if version_number < 1 || version_number as usize > dimsVersionRMQR.len() { - point(0, 0) - } else { - dimsVersionRMQR[version_number as usize - 1] - } - } - - fn getVersionRMQR(bitMatrix: &BitMatrix) -> i32 { - let width = bitMatrix.width() as i32; - let height = bitMatrix.height() as i32; - if width != height - && width.is_odd() - && height.is_odd() - && (27..=139).contains(&width) - && (7..=17).contains(&height) - { - for i in 0..dimsVersionRMQR.len() { - // for (int i = 0; i < Size(dimsVersionRMQR); i++){ - if width == dimsVersionRMQR[i].x && height == dimsVersionRMQR[i].y { - return i as i32; - } - } - } - -1 + return Self::NumberPoint(point(bitMatrix.width() as i32, bitMatrix.height() as i32)); } } diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs index 9417f4b..b625a02 100644 --- a/src/qrcode/cpp_port/bitmatrix_parser.rs +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -37,7 +37,7 @@ pub fn ReadVersion(bitMatrix: &BitMatrix, qr_type: Type) -> Result { } pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result { - if Version::HasMicroSize(bitMatrix) { + if Version::HasValidSizeType(bitMatrix, Type::Micro) { // Read top-left format info bits let mut formatInfoBits = 0; for x in 1..9 { @@ -52,7 +52,7 @@ pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result return Ok(FormatInformation::DecodeMQR(formatInfoBits as u32)); } - if Version::HasRMQRSize(bitMatrix) { + if Version::HasValidSizeType(bitMatrix, Type::RectMicro) { // Read top-left format info bits let mut formatInfoBits1 = 0; for y in (1..=3).rev() { diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 91c4f26..e330e52 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -10,7 +10,7 @@ use crate::{ decoder::{FormatInformation, Version, VersionRef}, detector::QRCodeDetectorResult, }, - Exceptions, + Exceptions, point, }; use multimap::MultiMap; use num::Integer; @@ -27,6 +27,8 @@ use crate::{ point_f, Point, }; +use super::Type; + #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct FinderPatternSet { pub bl: ConcentricPattern, @@ -40,8 +42,6 @@ pub type FinderPatternSets = Vec; const LEN: usize = 5; const SUM: usize = 7; const PATTERN: FixedPattern = FixedPattern::new([1, 1, 3, 1, 1]); -const SUBPATTERN_RMQR: FixedPattern<5, 5, false> = FixedPattern::new([1, 1, 1, 1, 1]); -const CORNER_EDGE_RMQR: FixedPattern<2, 4, false> = FixedPattern::new([3, 1]); const E2E: bool = true; fn FindPattern(view: PatternView<'_>) -> Result> { @@ -570,7 +570,7 @@ pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result= Version::DimensionOfVersion(7, false) as i32 { + if dimension >= Version::SymbolSize(7, Type::Model2).x as i32 { let version = ReadVersion(image, dimension as u32, mod2Pix); // if the version bits are garbage -> discard the detection @@ -799,10 +799,9 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result { // SaveAsPBM(image, "weg.pbm"); // #endif - let MIN_MODULES: u32 = Version::DimensionOfVersion(1, false); - let MAX_MODULES: u32 = Version::DimensionOfVersion(40, false); + let MIN_MODULES : i32 = Version::SymbolSize(1, Type::Model2).x; - let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); + let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32); if !found || (width as i32 - height as i32).abs() > 1 { return Err(Exceptions::NOT_FOUND); @@ -846,8 +845,7 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result { .dim; let moduleSize: f32 = ((width) as f32) / dimension as f32; - if dimension < MIN_MODULES as i32 - || dimension > MAX_MODULES as i32 + if !Version::IsValidSize(point(dimension, dimension), Type::Model2) || !image.is_in(point_f( left as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, top as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, @@ -888,10 +886,9 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result { pub fn DetectPureMQR(image: &BitMatrix) -> Result { type Pattern = [PatternType; 5]; - const MIN_MODULES: u32 = Version::DimensionOfVersion(1, true); - const MAX_MODULES: u32 = Version::DimensionOfVersion(4, true); + let MIN_MODULES : i32= Version::SymbolSize(1, Type::Micro).x; - let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); + let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32); // int left, top, width, height; if !found || (width as i32 - height as i32).abs() > 1 { @@ -914,7 +911,7 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result { let moduleSize: f32 = (fpWidth as f32) / 7.0; let dimension = (width as f32 / moduleSize).floor() as u32; - if !(MIN_MODULES..=MAX_MODULES).contains(&dimension) + if !Version::IsValidSize(point(dimension as i32, dimension as i32), Type::Micro) || !image.is_in(point_f( left as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, top as f32 + moduleSize / 2.0 + (dimension - 1) as f32 * moduleSize, @@ -952,23 +949,25 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result { } pub fn DetectPureRMQR(image: &BitMatrix) -> Result { + const SUBPATTERN : FixedPattern<4,4> = FixedPattern::new([1, 1, 1, 1]); + const TIMINGPATTERN : FixedPattern<10,10>= FixedPattern::new([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); + type Pattern = [PatternType; 5]; //std::array; - type SubPattern = [PatternType; 5]; //std::array; - type CornerEdgePattern = [PatternType; 2]; //std::array; + // type SubPattern = [PatternType; 5]; //std::array; + // type CornerEdgePattern = [PatternType; 2]; //std::array; + + type SubPattern = [PatternType;4]; + type TimingPattern = [PatternType;10]; // #ifdef PRINT_DEBUG // SaveAsPBM(image, "weg.pbm"); // #endif - const MIN_MODULES: u32 = 7; - const MIN_MODULES_W: u32 = 27; - const MIN_MODULES_H: u32 = 7; - const MAX_MODULES_W: u32 = 139; - const MAX_MODULES_H: u32 = 17; + let MIN_MODULES : i32 = Version::SymbolSize(1, Type::RectMicro).y; - let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES); + let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32); - if !found { + if !found || height >= width{ return Err(Exceptions::NOT_FOUND); } let right = left + width - 1; @@ -989,76 +988,37 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { return Err(Exceptions::NOT_FOUND); } - // Finder sub pattern - let mut subdiagonal: SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)) - .readPatternFromBlack(1, None) - .ok_or(Exceptions::ILLEGAL_STATE)?; - if subdiagonal.len() == 5 && subdiagonal[4] > subdiagonal[3] { - // Sub pattern has no separator so can run off along the diagonal - subdiagonal[4] = subdiagonal[3]; // Hack it back to previous - } - let subdiagonal_hld = subdiagonal.to_vec().into(); - let view = PatternView::new(&subdiagonal_hld); - if !(IsPattern::(&view, &SUBPATTERN_RMQR, None, 0.0, 0.0) != 0.0) { - return Err(Exceptions::NOT_FOUND); - } - - // Horizontal corner finder patterns (for vertical ones see below) - for (p, d) in [(tr, point_i(-1, 0)), (bl, point_i(1, 0))] { - // for (auto [p, d] : {std::pair(tr, PointI{-1, 0}), {bl, {1, 0}}}) { - let corner: CornerEdgePattern = EdgeTracer::new(image, p, d) - .readPatternFromBlack(1, None) - .ok_or(Exceptions::ILLEGAL_STATE)?; - let corner_hld = corner.to_vec().into(); - let view = PatternView::new(&corner_hld); - if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { - { - return Err(Exceptions::NOT_FOUND); - } - } - } + let fpWidth = (diagonal).into_iter().sum::(); let moduleSize = (fpWidth as f32) / 7.0; let dimW = (width as f32 / moduleSize as f32).floor() as u32; let dimH = (height as f32 / moduleSize as f32).floor() as u32; - if dimW == dimH - || dimW.is_even() - || dimH.is_even() - || !(MIN_MODULES_W..=MAX_MODULES_W).contains(&dimW) - || !(MIN_MODULES_H..=MAX_MODULES_H).contains(&dimH) - || !image.is_in(point_f( - left as f32 + moduleSize / 2.0 + (dimW as f32 - 1.0) * moduleSize, - top as f32 + moduleSize / 2.0 + (dimH as f32 - 1.0) * moduleSize, - )) - { - return Err(Exceptions::NOT_FOUND); - } + if (!Version::IsValidSize(point(dimW as i32, dimH as i32), Type::RectMicro)) + {return Err(Exceptions::NOT_FOUND);} + + // Finder sub pattern + let subdiagonal : SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)).readPatternFromBlack(1,None).ok_or(Exceptions::ILLEGAL_STATE)?; + let subdiagonal_hld = diagonal.to_vec().into(); + let view = PatternView::new(&subdiagonal_hld); + if IsPattern::(&view, &SUBPATTERN, None, 0.0, 0.0) != 0.0 + {return Err(Exceptions::NOT_FOUND);} // Vertical corner finder patterns - if dimH > 7 { - // None for R7 - let corner: CornerEdgePattern = EdgeTracer::new(image, tr, point_i(0, 1)) - .readPatternFromBlack(1, None) - .ok_or(Exceptions::ILLEGAL_STATE)?; - let corner_hld = corner.to_vec().into(); - let view = PatternView::new(&corner_hld); - if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { - return Err(Exceptions::NOT_FOUND); + // Horizontal timing patterns + for (p, d) in [(tr, point(-1, 0)), (bl, point(1, 0)), (tl, point(1, 0)), (br, point(-1, 0))] { + let mut cur = EdgeTracer::new(image, p, d.into()); + // skip corner / finder / sub pattern edge + cur.stepToEdge(Some(2 + i32::from(cur.isWhite())), None, None); + let timing : TimingPattern = cur.readPattern(None).ok_or(Exceptions::ILLEGAL_STATE)?; + let timing_hld = diagonal.to_vec().into(); + let view = PatternView::new(&timing_hld); + if !(IsPattern::(&view, &TIMINGPATTERN, None, 0.0, 0.0) != 0.0) + {return Err(Exceptions::NOT_FOUND);} } - if dimH > 9 { - // No bottom left for R9 - let corner: CornerEdgePattern = EdgeTracer::new(image, bl, point_i(0, -1)) - .readPatternFromBlack(1, None) - .ok_or(Exceptions::ILLEGAL_STATE)?; - let corner_hld = corner.to_vec().into(); - let view = PatternView::new(&corner_hld); - if !(IsPattern::(&view, &CORNER_EDGE_RMQR, None, 0.0, 0.0) != 0.0) { - return Err(Exceptions::NOT_FOUND); - } - } - } + + // #ifdef PRINT_DEBUG // LogMatrix log; @@ -1159,7 +1119,7 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result Result Result { if self.isRMQR() { - let dimension = Version::DimensionOfVersionRMQR(self.versionNumber); - let mut bitMatrix = BitMatrix::new(dimension.x as u32, dimension.y as u32)?; + let size = Version::SymbolSize(self.versionNumber, Type::RectMicro); + let mut bitMatrix = BitMatrix::new(size.x as u32, size.y as u32)?; // Set edge timing patterns - bitMatrix.setRegion(0, 0, dimension.x as u32, 1)?; // Top - bitMatrix.setRegion(0, (dimension.y - 1) as u32, dimension.x as u32, 1)?; // Bottom - bitMatrix.setRegion(0, 1, 1, (dimension.y - 2) as u32)?; // Left - bitMatrix.setRegion((dimension.x - 1) as u32, 1, 1, (dimension.y - 2) as u32)?; // Right + bitMatrix.setRegion(0, 0, size.x as u32, 1)?; // Top + bitMatrix.setRegion(0, (size.y - 1) as u32, size.x as u32, 1)?; // Bottom + bitMatrix.setRegion(0, 1, 1, (size.y - 2) as u32)?; // Left + bitMatrix.setRegion((size.x - 1) as u32, 1, 1, (size.y - 2) as u32)?; // Right // Set vertical timing and alignment patterns let max = self.alignmentPatternCenters.len(); // Same as vertical timing column @@ -217,32 +217,32 @@ impl Version { // for (size_t x = 0; x < max; ++x) { let cx = self.alignmentPatternCenters[x]; bitMatrix.setRegion(cx - 1, 1, 3, 2)?; // Top alignment pattern - bitMatrix.setRegion(cx - 1, (dimension.y - 3) as u32, 3, 2)?; // Bottom alignment pattern - bitMatrix.setRegion(cx, 3, 1, (dimension.y - 6) as u32)?; // Vertical timing pattern + bitMatrix.setRegion(cx - 1, (size.y - 3) as u32, 3, 2)?; // Bottom alignment pattern + bitMatrix.setRegion(cx, 3, 1, (size.y - 6) as u32)?; // Vertical timing pattern } // Top left finder pattern + separator - bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(dimension.y == 7))?; // R7 finder bottom flush with edge + bitMatrix.setRegion(1, 1, 8 - 1, 8 - 1 - u32::from(size.y == 7))?; // R7 finder bottom flush with edge // Top left format bitMatrix.setRegion(8, 1, 3, 5)?; bitMatrix.setRegion(11, 1, 1, 3)?; // Bottom right finder subpattern bitMatrix.setRegion( - (dimension.x - 5) as u32, - (dimension.y - 5) as u32, + (size.x - 5) as u32, + (size.y - 5) as u32, 5 - 1, 5 - 1, )?; // Bottom right format - bitMatrix.setRegion((dimension.x - 8) as u32, (dimension.y - 6) as u32, 3, 5)?; - bitMatrix.setRegion((dimension.x - 5) as u32, (dimension.y - 6) as u32, 3, 1)?; + bitMatrix.setRegion((size.x - 8) as u32, (size.y - 6) as u32, 3, 5)?; + bitMatrix.setRegion((size.x - 5) as u32, (size.y - 6) as u32, 3, 1)?; // Top right corner finder - bitMatrix.set((dimension.x - 2) as u32, 1); - if dimension.y > 9 { + bitMatrix.set((size.x - 2) as u32, 1); + if size.y > 9 { // Bottom left corner finder - bitMatrix.set(1, (dimension.y - 2) as u32); + bitMatrix.set(1, (size.y - 2) as u32); } return Ok(bitMatrix); diff --git a/src/rxing_result_point.rs b/src/rxing_result_point.rs index 205dfa1..c0d4450 100644 --- a/src/rxing_result_point.rs +++ b/src/rxing_result_point.rs @@ -90,12 +90,22 @@ impl Hash for Point { } } -impl PartialEq for Point { +// impl PartialEq for Point { +// fn eq(&self, other: &Self) -> bool { +// self.x == other.x && self.y == other.y +// } +// } +// impl Eq for Point {} + +impl PartialEq for PointT +where + T: PartialEq, +{ fn eq(&self, other: &Self) -> bool { self.x == other.x && self.y == other.y } } -impl Eq for Point {} +impl Eq for PointT where T: PartialEq {} impl PointT where From d87ebc5f73cf097ac4f2176a215864fe19ef24f9 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 18:11:55 -0600 Subject: [PATCH 09/15] chore: clippy cleanup --- .../base_extentions/qrcode_version.rs | 33 +++++++++---------- src/qrcode/cpp_port/detector.rs | 10 +++--- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index b6b7084..0216f14 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -150,7 +150,7 @@ impl Version { let square = |s: i32| point(s, s); let valid = |v: i32, max: i32| v >= 1 && v <= max; - match (qr_type) { + match qr_type { Type::Model1 => { if valid(version, 32) { square(17 + 4 * version) @@ -183,7 +183,7 @@ impl Version { } pub fn IsValidSize(size: PointI, qr_type: Type) -> bool { - match (qr_type) { + match qr_type { Type::Model1 => size.x == size.y && size.x >= 21 && size.x <= 145 && (size.x % 4 == 1), Type::Model2 => size.x == size.y && size.x >= 21 && size.x <= 177 && (size.x % 4 == 1), Type::Micro => size.x == size.y && size.x >= 11 && size.x <= 17 && (size.x % 2 == 1), @@ -200,40 +200,39 @@ impl Version { } } pub fn HasValidSizeType(bitMatrix: &BitMatrix, qr_type: Type) -> bool { - return Self::IsValidSize( + Self::IsValidSize( point(bitMatrix.width() as i32, bitMatrix.height() as i32), qr_type, - ); + ) } pub fn HasValidSize(matrix: &BitMatrix) -> bool { - return Self::HasValidSizeType(matrix, Type::Model1) + Self::HasValidSizeType(matrix, Type::Model1) || Self::HasValidSizeType(matrix, Type::Model2) || Self::HasValidSizeType(matrix, Type::Micro) - || Self::HasValidSizeType(matrix, Type::RectMicro); + || Self::HasValidSizeType(matrix, Type::RectMicro) } - fn IndexOf(points: &[PointI], search: PointI) -> i32 { + fn IndexOf(_points: &[PointI], search: PointI) -> i32 { RMQR_SIZES .iter() - .position(|p| *p == search) - .and_then(|x| Some(x as i32)) + .position(|p| *p == search).map(|x| x as i32) .unwrap_or(-1) } pub fn NumberPoint(size: PointI) -> u32 { - if (size.x != size.y) { - return (Self::IndexOf(&RMQR_SIZES, size) + 1) as u32; - } else if (Self::IsValidSize(size, Type::Model2)) { - return ((size.x as i32 - 17) / 4) as u32; - } else if (Self::IsValidSize(size, Type::Micro)) { - return ((size.x as i32 - 9) / 2) as u32; + if size.x != size.y { + (Self::IndexOf(&RMQR_SIZES, size) + 1) as u32 + } else if Self::IsValidSize(size, Type::Model2) { + ((size.x - 17) / 4) as u32 + } else if Self::IsValidSize(size, Type::Micro) { + ((size.x - 9) / 2) as u32 } else { - return 0; + 0 } } pub fn Number(bitMatrix: &BitMatrix) -> u32 { - return Self::NumberPoint(point(bitMatrix.width() as i32, bitMatrix.height() as i32)); + Self::NumberPoint(point(bitMatrix.width() as i32, bitMatrix.height() as i32)) } } diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index e330e52..93d7ed7 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -13,7 +13,7 @@ use crate::{ Exceptions, point, }; use multimap::MultiMap; -use num::Integer; + use crate::{ common::{ @@ -570,7 +570,7 @@ pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result= Version::SymbolSize(7, Type::Model2).x as i32 { + if dimension >= Version::SymbolSize(7, Type::Model2).x { let version = ReadVersion(image, dimension as u32, mod2Pix); // if the version bits are garbage -> discard the detection @@ -995,12 +995,12 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { let dimW = (width as f32 / moduleSize as f32).floor() as u32; let dimH = (height as f32 / moduleSize as f32).floor() as u32; - if (!Version::IsValidSize(point(dimW as i32, dimH as i32), Type::RectMicro)) + if !Version::IsValidSize(point(dimW as i32, dimH as i32), Type::RectMicro) {return Err(Exceptions::NOT_FOUND);} // Finder sub pattern let subdiagonal : SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)).readPatternFromBlack(1,None).ok_or(Exceptions::ILLEGAL_STATE)?; - let subdiagonal_hld = diagonal.to_vec().into(); + let subdiagonal_hld = subdiagonal.to_vec().into(); let view = PatternView::new(&subdiagonal_hld); if IsPattern::(&view, &SUBPATTERN, None, 0.0, 0.0) != 0.0 {return Err(Exceptions::NOT_FOUND);} @@ -1012,7 +1012,7 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { // skip corner / finder / sub pattern edge cur.stepToEdge(Some(2 + i32::from(cur.isWhite())), None, None); let timing : TimingPattern = cur.readPattern(None).ok_or(Exceptions::ILLEGAL_STATE)?; - let timing_hld = diagonal.to_vec().into(); + let timing_hld = timing.to_vec().into(); let view = PatternView::new(&timing_hld); if !(IsPattern::(&view, &TIMINGPATTERN, None, 0.0, 0.0) != 0.0) {return Err(Exceptions::NOT_FOUND);} From 3d07788f2b10fbe8165d5a7e33f379e0bd225965 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 18:12:23 -0600 Subject: [PATCH 10/15] chore: cargo fmt --- .../base_extentions/qr_formatinformation.rs | 7 +- .../base_extentions/qrcode_version.rs | 11 +-- src/qrcode/cpp_port/detector.rs | 89 +++++++++++-------- src/qrcode/cpp_port/test/QRVersionTest.rs | 5 +- src/qrcode/decoder/format_information.rs | 6 +- src/qrcode/decoder/version.rs | 9 +- 6 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 2dd548d..1e2ca2d 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -102,12 +102,9 @@ impl FormatInformation { */ pub fn DecodeRMQR(formatInfoBits1: u32, formatInfoBits2: u32) -> Self { //FormatInformation fi; - + let mut fi = if formatInfoBits2 != 0 { - Self::FindBestFormatInfoRMQR( - &[formatInfoBits1], - &[formatInfoBits2], - ) + Self::FindBestFormatInfoRMQR(&[formatInfoBits1], &[formatInfoBits2]) } else { // TODO probably remove if `sampleRMQR()` done properly Self::FindBestFormatInfoRMQR(&[formatInfoBits1], &[]) diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index 0216f14..4700b1e 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -144,7 +144,7 @@ impl Version { Type::const_eq(self.qr_type, Type::RectMicro) } - pub fn SymbolSize(version: u32, qr_type: Type) -> PointI { + pub fn SymbolSize(version: u32, qr_type: Type) -> PointI { let version = version as i32; let square = |s: i32| point(s, s); @@ -216,7 +216,8 @@ impl Version { fn IndexOf(_points: &[PointI], search: PointI) -> i32 { RMQR_SIZES .iter() - .position(|p| *p == search).map(|x| x as i32) + .position(|p| *p == search) + .map(|x| x as i32) .unwrap_or(-1) } @@ -224,11 +225,11 @@ impl Version { if size.x != size.y { (Self::IndexOf(&RMQR_SIZES, size) + 1) as u32 } else if Self::IsValidSize(size, Type::Model2) { - ((size.x - 17) / 4) as u32 + ((size.x - 17) / 4) as u32 } else if Self::IsValidSize(size, Type::Micro) { - ((size.x - 9) / 2) as u32 + ((size.x - 9) / 2) as u32 } else { - 0 + 0 } } diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 93d7ed7..dfd6118 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -5,16 +5,15 @@ use crate::{ }, DefaultGridSampler, GridSampler, Result, SamplerControl, }, - point_i, + point, point_i, qrcode::{ decoder::{FormatInformation, Version, VersionRef}, detector::QRCodeDetectorResult, }, - Exceptions, point, + Exceptions, }; use multimap::MultiMap; - use crate::{ common::{ cpp_essentials::{ @@ -799,7 +798,7 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result { // SaveAsPBM(image, "weg.pbm"); // #endif - let MIN_MODULES : i32 = Version::SymbolSize(1, Type::Model2).x; + let MIN_MODULES: i32 = Version::SymbolSize(1, Type::Model2).x; let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32); @@ -886,7 +885,7 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result { pub fn DetectPureMQR(image: &BitMatrix) -> Result { type Pattern = [PatternType; 5]; - let MIN_MODULES : i32= Version::SymbolSize(1, Type::Micro).x; + let MIN_MODULES: i32 = Version::SymbolSize(1, Type::Micro).x; let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32); @@ -949,25 +948,25 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result { } pub fn DetectPureRMQR(image: &BitMatrix) -> Result { - const SUBPATTERN : FixedPattern<4,4> = FixedPattern::new([1, 1, 1, 1]); - const TIMINGPATTERN : FixedPattern<10,10>= FixedPattern::new([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); + const SUBPATTERN: FixedPattern<4, 4> = FixedPattern::new([1, 1, 1, 1]); + const TIMINGPATTERN: FixedPattern<10, 10> = FixedPattern::new([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); type Pattern = [PatternType; 5]; //std::array; - // type SubPattern = [PatternType; 5]; //std::array; - // type CornerEdgePattern = [PatternType; 2]; //std::array; + // type SubPattern = [PatternType; 5]; //std::array; + // type CornerEdgePattern = [PatternType; 2]; //std::array; - type SubPattern = [PatternType;4]; - type TimingPattern = [PatternType;10]; + type SubPattern = [PatternType; 4]; + type TimingPattern = [PatternType; 10]; // #ifdef PRINT_DEBUG // SaveAsPBM(image, "weg.pbm"); // #endif - let MIN_MODULES : i32 = Version::SymbolSize(1, Type::RectMicro).y; + let MIN_MODULES: i32 = Version::SymbolSize(1, Type::RectMicro).y; let (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32); - if !found || height >= width{ + if !found || height >= width { return Err(Exceptions::NOT_FOUND); } let right = left + width - 1; @@ -988,37 +987,43 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { return Err(Exceptions::NOT_FOUND); } - - let fpWidth = (diagonal).into_iter().sum::(); let moduleSize = (fpWidth as f32) / 7.0; let dimW = (width as f32 / moduleSize as f32).floor() as u32; let dimH = (height as f32 / moduleSize as f32).floor() as u32; - if !Version::IsValidSize(point(dimW as i32, dimH as i32), Type::RectMicro) - {return Err(Exceptions::NOT_FOUND);} + if !Version::IsValidSize(point(dimW as i32, dimH as i32), Type::RectMicro) { + return Err(Exceptions::NOT_FOUND); + } - // Finder sub pattern - let subdiagonal : SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)).readPatternFromBlack(1,None).ok_or(Exceptions::ILLEGAL_STATE)?; + // Finder sub pattern + let subdiagonal: SubPattern = EdgeTracer::new(image, br, point_i(-1, -1)) + .readPatternFromBlack(1, None) + .ok_or(Exceptions::ILLEGAL_STATE)?; let subdiagonal_hld = subdiagonal.to_vec().into(); let view = PatternView::new(&subdiagonal_hld); - if IsPattern::(&view, &SUBPATTERN, None, 0.0, 0.0) != 0.0 - {return Err(Exceptions::NOT_FOUND);} + if IsPattern::(&view, &SUBPATTERN, None, 0.0, 0.0) != 0.0 { + return Err(Exceptions::NOT_FOUND); + } // Vertical corner finder patterns // Horizontal timing patterns - for (p, d) in [(tr, point(-1, 0)), (bl, point(1, 0)), (tl, point(1, 0)), (br, point(-1, 0))] { - let mut cur = EdgeTracer::new(image, p, d.into()); - // skip corner / finder / sub pattern edge - cur.stepToEdge(Some(2 + i32::from(cur.isWhite())), None, None); - let timing : TimingPattern = cur.readPattern(None).ok_or(Exceptions::ILLEGAL_STATE)?; + for (p, d) in [ + (tr, point(-1, 0)), + (bl, point(1, 0)), + (tl, point(1, 0)), + (br, point(-1, 0)), + ] { + let mut cur = EdgeTracer::new(image, p, d.into()); + // skip corner / finder / sub pattern edge + cur.stepToEdge(Some(2 + i32::from(cur.isWhite())), None, None); + let timing: TimingPattern = cur.readPattern(None).ok_or(Exceptions::ILLEGAL_STATE)?; let timing_hld = timing.to_vec().into(); - let view = PatternView::new(&timing_hld); - if !(IsPattern::(&view, &TIMINGPATTERN, None, 0.0, 0.0) != 0.0) - {return Err(Exceptions::NOT_FOUND);} + let view = PatternView::new(&timing_hld); + if !(IsPattern::(&view, &TIMINGPATTERN, None, 0.0, 0.0) != 0.0) { + return Err(Exceptions::NOT_FOUND); } - - + } // #ifdef PRINT_DEBUG // LogMatrix log; @@ -1162,10 +1167,24 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Date: Sun, 14 Jan 2024 09:48:22 -0600 Subject: [PATCH 11/15] port: rMQR: improve pure detection of large symbols https://github.com/zxing-cpp/zxing-cpp/commit/c581d8b0bbbe5862f72eda07ecb50fed5cc6bb83 --- src/qrcode/cpp_port/detector.rs | 31 +++++++++++--------- tests/common/abstract_black_box_test_case.rs | 2 ++ tests/cpp_qr_code_blackbox_tests.rs | 4 ++- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index dfd6118..56b2bde 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -983,16 +983,7 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { .ok_or(Exceptions::ILLEGAL_STATE)?; let diag_hld = diagonal.to_vec().into(); let view = PatternView::new(&diag_hld); - if !(IsPattern::(&view, &PATTERN, None, 0.0, 0.0) != 0.0) { - return Err(Exceptions::NOT_FOUND); - } - - let fpWidth = (diagonal).into_iter().sum::(); - let moduleSize = (fpWidth as f32) / 7.0; - let dimW = (width as f32 / moduleSize as f32).floor() as u32; - let dimH = (height as f32 / moduleSize as f32).floor() as u32; - - if !Version::IsValidSize(point(dimW as i32, dimH as i32), Type::RectMicro) { + if IsPattern::(&view, &PATTERN, None, 0.0, 0.0) == 0.0 { return Err(Exceptions::NOT_FOUND); } @@ -1002,10 +993,13 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { .ok_or(Exceptions::ILLEGAL_STATE)?; let subdiagonal_hld = subdiagonal.to_vec().into(); let view = PatternView::new(&subdiagonal_hld); - if IsPattern::(&view, &SUBPATTERN, None, 0.0, 0.0) != 0.0 { + if IsPattern::(&view, &SUBPATTERN, None, 0.0, 0.0) == 0.0 { return Err(Exceptions::NOT_FOUND); } + let mut moduleSize: f32 = + (diagonal.iter().sum::() + subdiagonal.iter().sum::()) as f32; + // Vertical corner finder patterns // Horizontal timing patterns for (p, d) in [ @@ -1020,9 +1014,18 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { let timing: TimingPattern = cur.readPattern(None).ok_or(Exceptions::ILLEGAL_STATE)?; let timing_hld = timing.to_vec().into(); let view = PatternView::new(&timing_hld); - if !(IsPattern::(&view, &TIMINGPATTERN, None, 0.0, 0.0) != 0.0) { + if IsPattern::(&view, &TIMINGPATTERN, None, 0.0, 0.0) == 0.0 { return Err(Exceptions::NOT_FOUND); } + moduleSize += timing.iter().sum::() as f32; + } + + moduleSize /= (7 + 4 + 4 * 10) as f32; // fp + sub + 4 x timing + let dimW = (width as f32 / moduleSize).round() as i32; + let dimH = (height as f32 / moduleSize).round() as i32; + + if !Version::IsValidSize(point(dimW, dimH), Type::RectMicro) { + return Err(Exceptions::NOT_FOUND); } // #ifdef PRINT_DEBUG @@ -1036,8 +1039,8 @@ pub fn DetectPureRMQR(image: &BitMatrix) -> Result { // Now just read off the bits (this is a crop + subsample) Ok(QRCodeDetectorResult::new( image.Deflate( - dimW, - dimH, + dimW as u32, + dimH as u32, top as f32 + moduleSize / 2.0, left as f32 + moduleSize / 2.0, moduleSize, diff --git a/tests/common/abstract_black_box_test_case.rs b/tests/common/abstract_black_box_test_case.rs index 26cbb08..8bd8a31 100644 --- a/tests/common/abstract_black_box_test_case.rs +++ b/tests/common/abstract_black_box_test_case.rs @@ -464,8 +464,10 @@ impl AbstractBlackBoxTestCase { ); result = if let Ok(res) = self.barcode_reader.decode_with_hints(source, &pure_hints) { + log::fine(format!("{suffix} - read pure barcode")); Some(res) } else { + // log::fine(format!("{suffix} - could not read pure barcode")); None }; } diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index 6ea0892..2be9caf 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -319,10 +319,12 @@ fn cpp_rmqr_blackbox_test_case() { QrReader, BarcodeFormat::RECTANGULAR_MICRO_QR_CODE, ); - tester.add_test(1, 1, 0.0); + tester.add_test(2, 2, 0.0); tester.add_test(1, 1, 90.0); tester.add_test(1, 1, 180.0); tester.add_test(1, 1, 270.0); tester.test_black_box(); } + +// From 30f9ed8976a774a2b62a4e9fad47bb736a546258 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sun, 14 Jan 2024 17:14:55 -0600 Subject: [PATCH 12/15] port: rMQR: improve detection rate by using finder sub pattern https://github.com/zxing-cpp/zxing-cpp/commit/677fc21c7d6f9e43e6e1852ab801d16e27d4c563 --- src/qrcode/cpp_port/detector.rs | 118 ++++++++++++++++++ ...ata.txt => R7x43-H_inverted._metadata.txt} | 0 tests/common/abstract_black_box_test_case.rs | 9 -- tests/cpp_qr_code_blackbox_tests.rs | 20 ++- 4 files changed, 132 insertions(+), 15 deletions(-) rename test_resources/blackbox/cpp/rmqrcode-1/{R7x43-H_inverted.metadata.txt => R7x43-H_inverted._metadata.txt} (100%) diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 56b2bde..ee7615a 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -1232,6 +1232,124 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result { + let tl = a.center(); + let br = b.center(); + // rotate points such that topLeft of a is furthest away from b and topLeft of b is closest to a + // let dist2B = /*[c = br]*/| &a, &b| { Some(Point::distance(a, br).partial_cmp(&Point::distance(b, br))) }; + + let offsetATarget = + a.0.iter() + .max_by(|a, b| { + Point::distance(**a, br) + .partial_cmp(&Point::distance(**b, br)) + .unwrap_or(std::cmp::Ordering::Less) + }) + .ok_or(Exceptions::FORMAT)?; + let offsetA = + a.0.iter() + .position(|x| x == offsetATarget) + .ok_or(Exceptions::FORMAT)? as i32; + // let offsetA = std::max_element(a.begin(), a.end(), dist2B) - a.begin(); + // let dist2A = /*[c = tl]*/| a, b| { Point::distance(a, tl) < Point::distance(b, tl) }; + let offsetBTarget = + b.0.iter() + .min_by(|a, b| { + Point::distance(**a, tl) + .partial_cmp(&Point::distance(**b, tl)) + .unwrap_or(std::cmp::Ordering::Less) + }) + .ok_or(Exceptions::FORMAT)?; + let offsetB = + b.0.iter() + .position(|x| x == offsetBTarget) + .ok_or(Exceptions::FORMAT)? as i32; + // let offsetB = std::min_element(b.begin(), b.end(), dist2A) - b.begin(); + + let a = a.rotated_corners(Some(offsetA), None); + let b = b.rotated_corners(Some(offsetB), None); + // a = RotatedCorners(a, offsetA); + // b = RotatedCorners(b, offsetB); + let tr = (RegressionLine::intersect( + &RegressionLine::with_two_points(a[0], a[1]), + &RegressionLine::with_two_points(b[1], b[2]), + ) + .ok_or(Exceptions::FORMAT)? + + RegressionLine::intersect( + &RegressionLine::with_two_points(a[3], a[2]), + &RegressionLine::with_two_points(b[0], b[3]), + ) + .ok_or(Exceptions::FORMAT)?) + / 2.0; + + // let tr = (intersect(RegressionLine(a[0], a[1]), RegressionLine(b[1], b[2])) + // + intersect(RegressionLine(a[3], a[2]), RegressionLine(b[0], b[3]))) + // / 2; + let bl = (RegressionLine::intersect( + &RegressionLine::with_two_points(a[0], a[3]), + &RegressionLine::with_two_points(b[2], b[3]), + ) + .ok_or(Exceptions::FORMAT)? + + RegressionLine::intersect( + &RegressionLine::with_two_points(a[1], a[2]), + &RegressionLine::with_two_points(b[0], b[1]), + ) + .ok_or(Exceptions::FORMAT)?) + / 2.0; + // let bl = (intersect(RegressionLine(a[0], a[3]), RegressionLine(b[2], b[3])) + // + intersect(RegressionLine(a[1], a[2]), RegressionLine(b[0], b[1]))) + // / 2; + + // log(tr, 2); + // log(bl, 2); + + Ok(Quadrilateral::from([tl, tr, br, bl])) + }; + + if let Some(found) = LocateAlignmentPattern( + image, + fp.size / 7, + bestPT.transform_point(Into::::into(dim) - point_f(3.0, 3.0)), + ) { + // if ( found ) { + // log(*found, 2); + if let Some(spQuad) = FindConcentricPatternCorners(image, found, fp.size / 2, 1) { + // if (auto spQuad = FindConcentricPatternCorners(image, *found, fp.size / 2, 1)) { + let mut dest = intersectQuads(&fpQuad, &spQuad)?; + if (dim.y <= 9) { + bestPT = PerspectiveTransform::quadrilateralToQuadrilateral( + Quadrilateral::from([ + point(6.5, 0.5), + point(dim.x as f32 - 1.5, dim.y as f32 - 3.5), + point(dim.x as f32 - 1.5, dim.y as f32 - 1.5), + point(6.5, 6.5), + ]), + Quadrilateral::from([ + *fpQuad.top_right(), + *spQuad.top_right(), + *spQuad.bottom_right(), + *fpQuad.bottom_right(), + ]), + )?; + // bestPT = PerspectiveTransform({{6.5, 0.5}, {dim.x - 1.5, dim.y - 3.5}, {dim.x - 1.5, dim.y - 1.5}, {6.5, 6.5}}, + // {fpQuad->topRight(), spQuad->topRight(), spQuad->bottomRight(), fpQuad->bottomRight()}); + } else { + dest[0] = fp.p; + dest[2] = found; + bestPT = PerspectiveTransform::quadrilateralToQuadrilateral( + Quadrilateral::from([ + point(3.5, 3.5), + point(dim.x as f32 - 2.5, 3.5), + point(dim.x as f32 - 2.5, dim.y as f32 - 2.5), + point(3.5, dim.y as f32 - 2.5), + ]), + dest, + )?; + } + } + } + let grid_sampler = DefaultGridSampler; let (sample, rps) = grid_sampler.sample_grid( image, diff --git a/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt b/test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted._metadata.txt similarity index 100% rename from test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted.metadata.txt rename to test_resources/blackbox/cpp/rmqrcode-1/R7x43-H_inverted._metadata.txt diff --git a/tests/common/abstract_black_box_test_case.rs b/tests/common/abstract_black_box_test_case.rs index 8bd8a31..951592a 100644 --- a/tests/common/abstract_black_box_test_case.rs +++ b/tests/common/abstract_black_box_test_case.rs @@ -309,15 +309,6 @@ impl AbstractBlackBoxTestCase { log::fine(format!("could not read at rotation {rotation} w/TH: {e:?}")); } } - // try { - // if (decode(bitmap, rotation, expectedText, expectedMetadata, true)) { - // tryHarderCounts[x]+=1; - // } else { - // tryHarderMisreadCounts[x]+=1; - // } - // } catch (ReaderException ignored) { - // log::fine(format!("could not read at rotation {} w/TH", rotation)); - // } } } diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index 2be9caf..7a811fd 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -14,7 +14,9 @@ * limitations under the License. */ -use rxing::{qrcode::cpp_port::QrReader, BarcodeFormat, MultiUseMultiFormatReader}; +use rxing::{ + qrcode::cpp_port::QrReader, BarcodeFormat, MultiFormatReader, MultiUseMultiFormatReader, +}; mod common; @@ -316,13 +318,19 @@ fn cpp_qrcode_black_box7_test_case() { fn cpp_rmqr_blackbox_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( "test_resources/blackbox/cpp/rmqrcode-1", - QrReader, + MultiFormatReader::default(), BarcodeFormat::RECTANGULAR_MICRO_QR_CODE, ); - tester.add_test(2, 2, 0.0); - tester.add_test(1, 1, 90.0); - tester.add_test(1, 1, 180.0); - tester.add_test(1, 1, 270.0); + // tester.ignore_pure = true; + tester.add_test(3, 3, 0.0); + tester.add_test(3, 3, 90.0); + tester.add_test(3, 3, 180.0); + tester.add_test(3, 3, 270.0); + + tester.add_hint( + rxing::DecodeHintType::ALSO_INVERTED, + rxing::DecodeHintValue::AlsoInverted(true), + ); tester.test_black_box(); } From 4136a310aa173a0b55f055530df54f0693d34986 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sun, 14 Jan 2024 17:16:10 -0600 Subject: [PATCH 13/15] chore: clippy cleanup --- src/qrcode/cpp_port/detector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index ee7615a..a7af60d 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -1317,7 +1317,7 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Date: Sun, 14 Jan 2024 17:28:00 -0600 Subject: [PATCH 14/15] port: QRCode: fix crash reported in cpp issue 700 https://github.com/zxing-cpp/zxing-cpp/commit/d1c34452b0a0a9a4936666e969c88f774f399c78 --- src/qrcode/cpp_port/detector.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index a7af60d..5efa786 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -1,7 +1,7 @@ use crate::{ common::{ cpp_essentials::{ - CenterOfRing, DMRegressionLine, FindConcentricPatternCorners, FindLeftGuardBy, Matrix, + CenterOfRing, DMRegressionLine, FindConcentricPatternCorners, FindLeftGuardBy, Matrix, Value, }, DefaultGridSampler, GridSampler, Result, SamplerControl, }, @@ -1087,6 +1087,7 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result Result 2 * dim / 3 { @@ -1192,6 +1193,7 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result Result Date: Sun, 14 Jan 2024 17:31:28 -0600 Subject: [PATCH 15/15] chore: clippy fixes --- src/qrcode/cpp_port/detector.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/qrcode/cpp_port/detector.rs b/src/qrcode/cpp_port/detector.rs index 5efa786..0bcc48f 100644 --- a/src/qrcode/cpp_port/detector.rs +++ b/src/qrcode/cpp_port/detector.rs @@ -1,7 +1,8 @@ use crate::{ common::{ cpp_essentials::{ - CenterOfRing, DMRegressionLine, FindConcentricPatternCorners, FindLeftGuardBy, Matrix, Value, + CenterOfRing, DMRegressionLine, FindConcentricPatternCorners, FindLeftGuardBy, Matrix, + Value, }, DefaultGridSampler, GridSampler, Result, SamplerControl, }, @@ -1193,7 +1194,7 @@ pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result Result Result