From bef115af819ce70dfabe3fe57bf86e44b2bd68b0 Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Thu, 11 Jan 2024 00:30:42 -0500 Subject: [PATCH 01/34] Gate tests based on feature macros --- tests/aztec_blackbox_tests.rs | 1 + tests/codabar_blackbox_1_test_case.rs | 1 + tests/code_128_blackbox.rs | 2 ++ tests/code_39_blackbox_reader.rs | 2 ++ tests/code_93_blackbox_1_testcase.rs | 1 + tests/cpp_qr_code_blackbox_tests.rs | 1 + tests/datamatrix_black_box_test_cases.rs | 1 + tests/ean_13_blackbox_tests.rs | 1 + tests/ean_8_black_box_1_testcase.rs | 1 + tests/github_issues.rs | 5 +++-- tests/inverted_data_matrix_black_box_testcase.rs | 1 + tests/itf_blackbox.rs | 1 + tests/maxicode_blackbox_tests.rs | 1 + tests/pdf_417_black_box_4_testcase.rs | 2 ++ tests/pdf_417_blackbox_tests.rs | 2 ++ tests/qr_code_blackbox_tests.rs | 1 + tests/rss_14_blackbox.rs | 1 + tests/rss_expanded_blackbox.rs | 1 + tests/telepen_blackbox_1_test_case.rs | 1 + tests/upc_a_blackbox_tests.rs | 1 + tests/upc_e_blackbox_tests.rs | 1 + tests/upc_ean_extension_black_box_1_testcase.rs | 1 + 22 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/aztec_blackbox_tests.rs b/tests/aztec_blackbox_tests.rs index 81204077..de0cbaec 100644 --- a/tests/aztec_blackbox_tests.rs +++ b/tests/aztec_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{aztec::AztecReader, BarcodeFormat}; diff --git a/tests/codabar_blackbox_1_test_case.rs b/tests/codabar_blackbox_1_test_case.rs index e7c3e81d..3f3a1582 100644 --- a/tests/codabar_blackbox_1_test_case.rs +++ b/tests/codabar_blackbox_1_test_case.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{oned::CodaBarReader, BarcodeFormat}; diff --git a/tests/code_128_blackbox.rs b/tests/code_128_blackbox.rs index dcf17e5d..bcf3c928 100644 --- a/tests/code_128_blackbox.rs +++ b/tests/code_128_blackbox.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "image")] + use rxing::{BarcodeFormat, MultiFormatReader}; mod common; diff --git a/tests/code_39_blackbox_reader.rs b/tests/code_39_blackbox_reader.rs index ce70a403..6abc014b 100644 --- a/tests/code_39_blackbox_reader.rs +++ b/tests/code_39_blackbox_reader.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "image")] + /** * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/tests/code_93_blackbox_1_testcase.rs b/tests/code_93_blackbox_1_testcase.rs index db2feb7c..04e7c55f 100644 --- a/tests/code_93_blackbox_1_testcase.rs +++ b/tests/code_93_blackbox_1_testcase.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index d0686846..6fe72973 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{qrcode::cpp_port::QrReader, BarcodeFormat, MultiUseMultiFormatReader}; diff --git a/tests/datamatrix_black_box_test_cases.rs b/tests/datamatrix_black_box_test_cases.rs index 94836bac..37bb1f75 100644 --- a/tests/datamatrix_black_box_test_cases.rs +++ b/tests/datamatrix_black_box_test_cases.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::MultiFormatReader; diff --git a/tests/ean_13_blackbox_tests.rs b/tests/ean_13_blackbox_tests.rs index 52e64bd3..e794ea5b 100644 --- a/tests/ean_13_blackbox_tests.rs +++ b/tests/ean_13_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/ean_8_black_box_1_testcase.rs b/tests/ean_8_black_box_1_testcase.rs index 1cf30d76..f21aaca4 100644 --- a/tests/ean_8_black_box_1_testcase.rs +++ b/tests/ean_8_black_box_1_testcase.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/github_issues.rs b/tests/github_issues.rs index 0a6db834..164a550c 100644 --- a/tests/github_issues.rs +++ b/tests/github_issues.rs @@ -1,7 +1,5 @@ use std::io::Read; -use rxing::DecodingHintDictionary; - #[test] fn issue_27_part_2() { let mut data = Vec::new(); @@ -13,8 +11,11 @@ fn issue_27_part_2() { rxing::helpers::detect_multiple_in_luma(data, 720, 618).unwrap_or_default(); } +#[cfg(feature = "image")] #[test] fn issue_28() { + use rxing::DecodingHintDictionary; + let mut hints: DecodingHintDictionary = DecodingHintDictionary::new(); hints.insert( rxing::DecodeHintType::TRY_HARDER, diff --git a/tests/inverted_data_matrix_black_box_testcase.rs b/tests/inverted_data_matrix_black_box_testcase.rs index a6efffd3..9b52a5c8 100644 --- a/tests/inverted_data_matrix_black_box_testcase.rs +++ b/tests/inverted_data_matrix_black_box_testcase.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{DecodeHintType, DecodeHintValue, MultiFormatReader}; diff --git a/tests/itf_blackbox.rs b/tests/itf_blackbox.rs index 9bfbb0f8..6e05ac45 100644 --- a/tests/itf_blackbox.rs +++ b/tests/itf_blackbox.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/maxicode_blackbox_tests.rs b/tests/maxicode_blackbox_tests.rs index fe2bbe0c..e27a7b90 100644 --- a/tests/maxicode_blackbox_tests.rs +++ b/tests/maxicode_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, DecodeHintType, MultiFormatReader}; diff --git a/tests/pdf_417_black_box_4_testcase.rs b/tests/pdf_417_black_box_4_testcase.rs index d36c12e3..0ea8ccc5 100644 --- a/tests/pdf_417_black_box_4_testcase.rs +++ b/tests/pdf_417_black_box_4_testcase.rs @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] + mod common; use rxing::pdf417::PDF417Reader; diff --git a/tests/pdf_417_blackbox_tests.rs b/tests/pdf_417_blackbox_tests.rs index 59e6eaeb..7dd6bf32 100644 --- a/tests/pdf_417_blackbox_tests.rs +++ b/tests/pdf_417_blackbox_tests.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "image")] + use rxing::MultiFormatReader; mod common; diff --git a/tests/qr_code_blackbox_tests.rs b/tests/qr_code_blackbox_tests.rs index 1952f056..93b9a693 100644 --- a/tests/qr_code_blackbox_tests.rs +++ b/tests/qr_code_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{qrcode::QRCodeReader, BarcodeFormat, MultiFormatReader}; diff --git a/tests/rss_14_blackbox.rs b/tests/rss_14_blackbox.rs index 56a7c87c..3372fe08 100644 --- a/tests/rss_14_blackbox.rs +++ b/tests/rss_14_blackbox.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/rss_expanded_blackbox.rs b/tests/rss_expanded_blackbox.rs index e3dad0aa..2f0f3923 100644 --- a/tests/rss_expanded_blackbox.rs +++ b/tests/rss_expanded_blackbox.rs @@ -23,6 +23,7 @@ * * http://www.piramidepse.com/ */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/telepen_blackbox_1_test_case.rs b/tests/telepen_blackbox_1_test_case.rs index 9d635852..97a8bc1f 100644 --- a/tests/telepen_blackbox_1_test_case.rs +++ b/tests/telepen_blackbox_1_test_case.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{oned::TelepenReader, BarcodeFormat}; diff --git a/tests/upc_a_blackbox_tests.rs b/tests/upc_a_blackbox_tests.rs index d206ff2f..6e250369 100644 --- a/tests/upc_a_blackbox_tests.rs +++ b/tests/upc_a_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/upc_e_blackbox_tests.rs b/tests/upc_e_blackbox_tests.rs index f8a29e9f..ce52b184 100644 --- a/tests/upc_e_blackbox_tests.rs +++ b/tests/upc_e_blackbox_tests.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; diff --git a/tests/upc_ean_extension_black_box_1_testcase.rs b/tests/upc_ean_extension_black_box_1_testcase.rs index 1e58a0db..f9e6beee 100644 --- a/tests/upc_ean_extension_black_box_1_testcase.rs +++ b/tests/upc_ean_extension_black_box_1_testcase.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![cfg(feature = "image")] use rxing::{BarcodeFormat, MultiFormatReader}; From 7cb00fafd1dd8361ca40cbb4e5b7f66e71ab91da Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Thu, 11 Jan 2024 00:32:16 -0500 Subject: [PATCH 02/34] Test --no-default-features in CI --- .github/workflows/rust.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b0a17566..e6a7db43 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,5 +20,7 @@ jobs: run: cargo check --workspace --release --verbose - name: Build run: cargo build --workspace --release --verbose - - name: Run tests + - name: Run tests (defaults) run: cargo test --workspace --release --verbose + - name: Run tests (minimal) + run: cargo test --workspace --release --verbose --no-default-features From 169f2f6073a8151b1281c8cf28c51f617b6ea7a0 Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Thu, 11 Jan 2024 12:38:01 -0500 Subject: [PATCH 03/34] Allow disabling image features --- Cargo.toml | 25 ++++++++++++++--- src/maxicode/detector.rs | 8 ++++++ src/multi/multi_test_case.rs | 2 ++ src/multi/qrcode/qr_code_multi_reader.rs | 1 + src/oned/rss/expanded/expanded_pair.rs | 2 +- src/oned/rss/expanded/expanded_row.rs | 2 +- .../rss_expanded_image_2_binary_test_case.rs | 18 +++++++++++++ .../rss_expanded_image_2_string_test_case.rs | 27 +++++++++++++++++++ .../rss_expanded_internal_test_case.rs | 4 +++ ...rss_expanded_stacked_internal_test_case.rs | 2 ++ src/oned/rss/finder_pattern.rs | 2 +- src/qrcode/QRCodeWriterTestCase.rs | 1 + tests/aztec_blackbox_tests.rs | 2 ++ tests/codabar_blackbox_1_test_case.rs | 1 + tests/code_128_blackbox.rs | 3 +++ tests/code_39_blackbox_reader.rs | 3 +++ tests/code_93_blackbox_1_testcase.rs | 1 + tests/cpp_qr_code_blackbox_tests.rs | 13 +++++++++ tests/datamatrix_black_box_test_cases.rs | 3 +++ tests/ean_13_blackbox_tests.rs | 5 ++++ tests/ean_8_black_box_1_testcase.rs | 1 + ...inverted_data_matrix_black_box_testcase.rs | 1 + tests/itf_blackbox.rs | 2 ++ tests/maxicode_blackbox_tests.rs | 2 ++ tests/pdf_417_black_box_4_testcase.rs | 1 + tests/pdf_417_blackbox_tests.rs | 3 +++ tests/qr_code_blackbox_tests.rs | 6 +++++ tests/rss_14_blackbox.rs | 2 ++ tests/rss_expanded_blackbox.rs | 5 ++++ tests/telepen_blackbox_1_test_case.rs | 2 ++ tests/upc_a_blackbox_tests.rs | 6 +++++ tests/upc_e_blackbox_tests.rs | 3 +++ .../upc_ean_extension_black_box_1_testcase.rs | 1 + 33 files changed, 154 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad5d6f1f..7b5b2f36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ urlencoding = "2.1.3" uriparse = "0.6.4" chrono = "0.4.31" chrono-tz = "0.8" -image = {version = "0.24", optional = true} +image = {version = "0.24", optional = true, default-features = false} imageproc = {version = "0.23", optional = true} unicode-segmentation = "1.10" codepage-437 = "0.1.0" @@ -41,9 +41,28 @@ rand = "0.8.5" criterion = "0.5" [features] -default = ["image", "client_support"] +default = ["image", "client_support", "image-formats"] #/// Enable features required for image manipulation and reading. image = ["dep:image", "dep:imageproc"] +image-formats = [ + "image", + "image/gif", + "image/jpeg", + "image/ico", + "image/png", + "image/pnm", + "image/tga", + "image/tiff", + "image/webp", + "image/bmp", + "image/hdr", + "image/dxt", + "image/dds", + "image/farbfeld", + "image/jpeg_rayon", + "image/openexr", + "image/qoi" +] #/// Allows the ability to force ISO/IED 18004 compliance. #/// Leave disabled unless specificially needed. @@ -83,4 +102,4 @@ harness = false [profile.release] codegen-units = 1 lto = true -opt-level = 3 \ No newline at end of file +opt-level = 3 diff --git a/src/maxicode/detector.rs b/src/maxicode/detector.rs index 25b66c5d..b58c031b 100644 --- a/src/maxicode/detector.rs +++ b/src/maxicode/detector.rs @@ -1054,6 +1054,7 @@ mod detector_test { Binarizer, BufferedImageLuminanceSource, }; + #[cfg(feature = "image-formats")] #[test] fn mode_1() { finder_test( @@ -1062,6 +1063,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mode_2() { finder_test( @@ -1070,6 +1072,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mode_2_rot90() { finder_test( @@ -1078,6 +1081,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mode3() { finder_test( @@ -1086,6 +1090,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mixed_sets() { finder_test( @@ -1094,6 +1099,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mode4() { finder_test( @@ -1102,6 +1108,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mode5() { finder_test( @@ -1110,6 +1117,7 @@ mod detector_test { ) } + #[cfg(feature = "image-formats")] #[test] fn mode6() { finder_test( diff --git a/src/multi/multi_test_case.rs b/src/multi/multi_test_case.rs index 41f44eb1..b429c6db 100644 --- a/src/multi/multi_test_case.rs +++ b/src/multi/multi_test_case.rs @@ -27,6 +27,7 @@ use super::{GenericMultipleBarcodeReader, MultipleBarcodeReader}; * Tests {@link MultipleBarcodeReader}. */ +#[cfg(feature = "image-formats")] #[test] fn testMulti() { // Very basic test for now @@ -54,6 +55,7 @@ fn testMulti() { assert_eq!(&BarcodeFormat::QR_CODE, results[1].getBarcodeFormat()); } +#[cfg(feature = "image-formats")] #[test] fn testMultiQR() { // Very basic test for now diff --git a/src/multi/qrcode/qr_code_multi_reader.rs b/src/multi/qrcode/qr_code_multi_reader.rs index 2f1aae29..08e16446 100644 --- a/src/multi/qrcode/qr_code_multi_reader.rs +++ b/src/multi/qrcode/qr_code_multi_reader.rs @@ -237,6 +237,7 @@ mod multi_qr_code_test_case { * Tests {@link QRCodeMultiReader}. */ + #[cfg(feature = "image-formats")] #[test] fn testMultiQRCodes() { // Very basic test for now diff --git a/src/oned/rss/expanded/expanded_pair.rs b/src/oned/rss/expanded/expanded_pair.rs index fe5ab6e4..5ba9460e 100644 --- a/src/oned/rss/expanded/expanded_pair.rs +++ b/src/oned/rss/expanded/expanded_pair.rs @@ -63,7 +63,7 @@ impl ExpandedPair { &self.finderPattern } - #[cfg(test)] + #[cfg(all(test, feature = "image"))] pub(crate) fn getFinderPatternMut(&mut self) -> &mut Option { &mut self.finderPattern } diff --git a/src/oned/rss/expanded/expanded_row.rs b/src/oned/rss/expanded/expanded_row.rs index e17f684c..1223c69e 100644 --- a/src/oned/rss/expanded/expanded_row.rs +++ b/src/oned/rss/expanded/expanded_row.rs @@ -35,7 +35,7 @@ impl ExpandedRow { &self.pairs } - #[cfg(test)] + #[cfg(all(test, feature = "image"))] pub(crate) fn getPairsMut(&mut self) -> &mut [ExpandedPair] { &mut self.pairs } diff --git a/src/oned/rss/expanded/rss_expanded_image_2_binary_test_case.rs b/src/oned/rss/expanded/rss_expanded_image_2_binary_test_case.rs index 665c6dde..22132b5a 100644 --- a/src/oned/rss/expanded/rss_expanded_image_2_binary_test_case.rs +++ b/src/oned/rss/expanded/rss_expanded_image_2_binary_test_case.rs @@ -35,6 +35,7 @@ use crate::{ use super::bit_array_builder; +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary1() { // (11)100224(17)110224(3102)000100 @@ -42,6 +43,7 @@ fn testDecodeRow2binary1() { " ...X...X .X....X. .XX...X. X..X...X ...XX.X. ..X.X... ..X.X..X ...X..X. X.X....X .X....X. .....X.. X...X..."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary2() { // (01)90012345678908(3103)001750 @@ -51,12 +53,14 @@ fn testDecodeRow2binary2() { ); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary3() { // (10)12A assertCorrectImage2binary("3.png", " .......X ..XX..X. X.X....X .......X ...."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary4() { // (01)98898765432106(3202)012345(15)991231 @@ -64,6 +68,7 @@ fn testDecodeRow2binary4() { "4.png", " ..XXXX.X XX.XXXX. .XXX.XX. XX..X... .XXXXX.. XX.X..X. ..XX..XX XX.X.XXX X..XX..X .X.XXXXX XXXX"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary5() { // (01)90614141000015(3202)000150 @@ -73,6 +78,7 @@ fn testDecodeRow2binary5() { ); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary10() { // (01)98898765432106(15)991231(3103)001750(10)12A(422)123(21)123456(423)0123456789012 @@ -80,6 +86,7 @@ fn testDecodeRow2binary10() { " .X.XX..X XX.XXXX. .XXX.XX. XX..X... .XXXXX.. XX.X..X. ..XX...X XX.X.... X.X.X.X. X.X..X.X .X....X. XX...X.. ...XX.X. .XXXXXX. .X..XX.. X.X.X... .X...... XXXX.... XX.XX... XXXXX.X. ...XXXXX .....X.X ...X.... X.XXX..X X.X.X... XX.XX..X .X..X..X .X.X.X.X X.XX...X .XX.XXX. XXX.X.XX ..X."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary11() { // (01)98898765432106(15)991231(3103)001750(10)12A(422)123(21)123456 @@ -87,6 +94,7 @@ fn testDecodeRow2binary11() { " .X.XX..X XX.XXXX. .XXX.XX. XX..X... .XXXXX.. XX.X..X. ..XX...X XX.X.... X.X.X.X. X.X..X.X .X....X. XX...X.. ...XX.X. .XXXXXX. .X..XX.. X.X.X... .X...... XXXX.... XX.XX... XXXXX.X. ...XXXXX .....X.X ...X.... X.XXX..X X.X.X... ...."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary12() { // (01)98898765432106(3103)001750 @@ -96,6 +104,7 @@ fn testDecodeRow2binary12() { ); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary13() { // (01)90012345678908(3922)795 @@ -105,6 +114,7 @@ fn testDecodeRow2binary13() { ); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary14() { // (01)90012345678908(3932)0401234 @@ -112,6 +122,7 @@ fn testDecodeRow2binary14() { "14.png", " ..XX.X.. ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. X.....X. X.....X. X.X.X.XX .X...... X..."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary15() { // (01)90012345678908(3102)001750(11)100312 @@ -119,6 +130,7 @@ fn testDecodeRow2binary15() { "15.png", " ..XXX... ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary16() { // (01)90012345678908(3202)001750(11)100312 @@ -126,6 +138,7 @@ fn testDecodeRow2binary16() { "16.png", " ..XXX..X ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary17() { // (01)90012345678908(3102)001750(13)100312 @@ -133,6 +146,7 @@ fn testDecodeRow2binary17() { "17.png", " ..XXX.X. ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary18() { // (01)90012345678908(3202)001750(13)100312 @@ -140,6 +154,7 @@ fn testDecodeRow2binary18() { "18.png", " ..XXX.XX ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary19() { // (01)90012345678908(3102)001750(15)100312 @@ -147,6 +162,7 @@ fn testDecodeRow2binary19() { "19.png", " ..XXXX.. ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary20() { // (01)90012345678908(3202)001750(15)100312 @@ -154,6 +170,7 @@ fn testDecodeRow2binary20() { "20.png", " ..XXXX.X ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary21() { // (01)90012345678908(3102)001750(17)100312 @@ -161,6 +178,7 @@ fn testDecodeRow2binary21() { "21.png", " ..XXXXX. ........ .X..XXX. X.X.X... XX.XXXXX .XXXX.X. ..XX...X .X.....X .XX..... XXXX.X.. XX.."); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2binary22() { // (01)90012345678908(3202)001750(17)100312 diff --git a/src/oned/rss/expanded/rss_expanded_image_2_string_test_case.rs b/src/oned/rss/expanded/rss_expanded_image_2_string_test_case.rs index 72b937db..8618a76c 100644 --- a/src/oned/rss/expanded/rss_expanded_image_2_string_test_case.rs +++ b/src/oned/rss/expanded/rss_expanded_image_2_string_test_case.rs @@ -37,36 +37,43 @@ use crate::{ * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) */ +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string1() { assertCorrectImage2string("1.png", "(11)100224(17)110224(3102)000100"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string2() { assertCorrectImage2string("2.png", "(01)90012345678908(3103)001750"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string3() { assertCorrectImage2string("3.png", "(10)12A"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string4() { assertCorrectImage2string("4.png", "(01)98898765432106(3202)012345(15)991231"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string5() { assertCorrectImage2string("5.png", "(01)90614141000015(3202)000150"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string7() { assertCorrectImage2string("7.png", "(10)567(11)010101"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string10() { let expected = @@ -74,6 +81,7 @@ fn testDecodeRow2string10() { assertCorrectImage2string("10.png", expected); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string11() { assertCorrectImage2string( @@ -82,96 +90,115 @@ fn testDecodeRow2string11() { ); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string12() { assertCorrectImage2string("12.png", "(01)98898765432106(3103)001750"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string13() { assertCorrectImage2string("13.png", "(01)90012345678908(3922)795"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string14() { assertCorrectImage2string("14.png", "(01)90012345678908(3932)0401234"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string15() { assertCorrectImage2string("15.png", "(01)90012345678908(3102)001750(11)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string16() { assertCorrectImage2string("16.png", "(01)90012345678908(3202)001750(11)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string17() { assertCorrectImage2string("17.png", "(01)90012345678908(3102)001750(13)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string18() { assertCorrectImage2string("18.png", "(01)90012345678908(3202)001750(13)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string19() { assertCorrectImage2string("19.png", "(01)90012345678908(3102)001750(15)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string20() { assertCorrectImage2string("20.png", "(01)90012345678908(3202)001750(15)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string21() { assertCorrectImage2string("21.png", "(01)90012345678908(3102)001750(17)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string22() { assertCorrectImage2string("22.png", "(01)90012345678908(3202)001750(17)100312"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string25() { assertCorrectImage2string("25.png", "(10)123"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string26() { assertCorrectImage2string("26.png", "(10)5678(11)010101"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string27() { assertCorrectImage2string("27.png", "(10)1098-1234"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string28() { assertCorrectImage2string("28.png", "(10)1098/1234"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string29() { assertCorrectImage2string("29.png", "(10)1098.1234"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string30() { assertCorrectImage2string("30.png", "(10)1098*1234"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string31() { assertCorrectImage2string("31.png", "(10)1098,1234"); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeRow2string32() { assertCorrectImage2string( diff --git a/src/oned/rss/expanded/rss_expanded_internal_test_case.rs b/src/oned/rss/expanded/rss_expanded_internal_test_case.rs index 4e4771c1..4e4a7488 100644 --- a/src/oned/rss/expanded/rss_expanded_internal_test_case.rs +++ b/src/oned/rss/expanded/rss_expanded_internal_test_case.rs @@ -37,6 +37,7 @@ use super::RSSExpandedReader; * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) */ +#[cfg(feature = "image-formats")] #[test] fn testFindFinderPatterns() { let image = readImage("2.png"); @@ -83,6 +84,7 @@ fn testFindFinderPatterns() { // } } +#[cfg(feature = "image-formats")] #[test] fn testRetrieveNextPairPatterns() { let image = readImage("3.png"); @@ -111,6 +113,7 @@ fn testRetrieveNextPairPatterns() { assert_eq!(0, finderPattern.getValue()); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeCheckCharacter() { let image = readImage("3.png"); @@ -139,6 +142,7 @@ fn testDecodeCheckCharacter() { assert_eq!(98, dataCharacter.getValue()); } +#[cfg(feature = "image-formats")] #[test] fn testDecodeDataCharacter() { let image = readImage("3.png"); diff --git a/src/oned/rss/expanded/rss_expanded_stacked_internal_test_case.rs b/src/oned/rss/expanded/rss_expanded_stacked_internal_test_case.rs index e6bb8e95..5a2d3a83 100644 --- a/src/oned/rss/expanded/rss_expanded_stacked_internal_test_case.rs +++ b/src/oned/rss/expanded/rss_expanded_stacked_internal_test_case.rs @@ -32,6 +32,7 @@ use super::{test_case_util, RSSExpandedReader}; * Tests {@link RSSExpandedReader} handling of stacked RSS barcodes. */ +#[cfg(feature = "image-formats")] #[test] fn testDecodingRowByRow() { let mut rssExpandedReader = RSSExpandedReader::new(); @@ -87,6 +88,7 @@ fn testDecodingRowByRow() { assert_eq!("(01)98898765432106(3202)012345(15)991231", result.getText()); } +#[cfg(feature = "image-formats")] #[test] fn testCompleteDecode() { let mut rssExpandedReader = RSSExpandedReader::new(); diff --git a/src/oned/rss/finder_pattern.rs b/src/oned/rss/finder_pattern.rs index 5e58d77d..675de702 100644 --- a/src/oned/rss/finder_pattern.rs +++ b/src/oned/rss/finder_pattern.rs @@ -48,7 +48,7 @@ impl FinderPattern { &self.startEnd } - #[cfg(test)] + #[cfg(all(test, feature = "image"))] pub(crate) fn getStartEndMut(&mut self) -> &mut [usize] { &mut self.startEnd } diff --git a/src/qrcode/QRCodeWriterTestCase.rs b/src/qrcode/QRCodeWriterTestCase.rs index 3a1a55db..989085bc 100644 --- a/src/qrcode/QRCodeWriterTestCase.rs +++ b/src/qrcode/QRCodeWriterTestCase.rs @@ -159,6 +159,7 @@ fn compareToGoldenFile( // Golden images are generated with "qrcode_sample.cc". The images are checked with both eye balls // and cell phones. We expect pixel-perfect results, because the error correction level is known, // and the pixel dimensions matches exactly. +#[cfg(feature = "image-formats")] #[test] fn testRegressionTest() { compareToGoldenFile( diff --git a/tests/aztec_blackbox_tests.rs b/tests/aztec_blackbox_tests.rs index de0cbaec..cb7ae0fd 100644 --- a/tests/aztec_blackbox_tests.rs +++ b/tests/aztec_blackbox_tests.rs @@ -22,6 +22,7 @@ mod common; /** * @author David Olivier */ +#[cfg(feature = "image-formats")] #[test] fn aztec_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -44,6 +45,7 @@ fn aztec_black_box1_test_case() { * * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn aztec_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/codabar_blackbox_1_test_case.rs b/tests/codabar_blackbox_1_test_case.rs index 3f3a1582..6b438cf7 100644 --- a/tests/codabar_blackbox_1_test_case.rs +++ b/tests/codabar_blackbox_1_test_case.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn codabar_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/code_128_blackbox.rs b/tests/code_128_blackbox.rs index bcf3c928..a8e20f44 100644 --- a/tests/code_128_blackbox.rs +++ b/tests/code_128_blackbox.rs @@ -7,6 +7,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn code128_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -25,6 +26,7 @@ fn code128_black_box1_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn code128_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -43,6 +45,7 @@ fn code128_black_box2_test_case() { /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn code128_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/code_39_blackbox_reader.rs b/tests/code_39_blackbox_reader.rs index 6abc014b..8e475fb1 100644 --- a/tests/code_39_blackbox_reader.rs +++ b/tests/code_39_blackbox_reader.rs @@ -14,6 +14,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn code39_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -34,6 +35,7 @@ fn code39_black_box1_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn code39_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -52,6 +54,7 @@ fn code39_black_box3_test_case() { /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn code39_extended_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/code_93_blackbox_1_testcase.rs b/tests/code_93_blackbox_1_testcase.rs index 04e7c55f..b398bb52 100644 --- a/tests/code_93_blackbox_1_testcase.rs +++ b/tests/code_93_blackbox_1_testcase.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn code93_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index 6fe72973..96deb081 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -23,6 +23,7 @@ mod common; * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -65,6 +66,7 @@ fn qrcode_black_box1_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -86,6 +88,7 @@ fn qrcode_black_box3_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -110,6 +113,7 @@ fn qrcode_black_box4_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box5_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -130,6 +134,7 @@ fn qrcode_black_box5_test_case() { * rotation, which was a weak spot. */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box6_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -145,6 +150,7 @@ fn qrcode_black_box6_test_case() { tester.test_black_box(); } +#[cfg(feature = "image-formats")] #[test] fn mqr_black_box_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -170,6 +176,7 @@ fn mqr_black_box_test_case() { * be a function of the barcode. */ +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -190,6 +197,7 @@ fn cpp_qrcode_black_box1_test_case() { * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -218,6 +226,7 @@ fn cpp_qrcode_black_box2_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -240,6 +249,7 @@ fn cpp_qrcode_black_box3_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -264,6 +274,7 @@ fn cpp_qrcode_black_box4_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box5_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -284,6 +295,7 @@ fn cpp_qrcode_black_box5_test_case() { * rotation, which was a weak spot. */ +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box6_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -299,6 +311,7 @@ fn cpp_qrcode_black_box6_test_case() { tester.test_black_box(); } +#[cfg(feature = "image-formats")] #[test] fn cpp_qrcode_black_box7_test_case() { let mut tester = common::MultiImageSpanAbstractBlackBoxTestCase::new( diff --git a/tests/datamatrix_black_box_test_cases.rs b/tests/datamatrix_black_box_test_cases.rs index 37bb1f75..f2b1da76 100644 --- a/tests/datamatrix_black_box_test_cases.rs +++ b/tests/datamatrix_black_box_test_cases.rs @@ -22,6 +22,7 @@ mod common; /** * @author bbrown@google.com (Brian Brown) */ +#[cfg(feature = "image-formats")] #[test] fn data_matrix_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -41,6 +42,7 @@ fn data_matrix_black_box1_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn data_matrix_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -60,6 +62,7 @@ fn data_matrix_black_box2_test_case() { /** * @author gitlost */ +#[cfg(feature = "image-formats")] #[test] fn data_matrix_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/ean_13_blackbox_tests.rs b/tests/ean_13_blackbox_tests.rs index e794ea5b..17c93502 100644 --- a/tests/ean_13_blackbox_tests.rs +++ b/tests/ean_13_blackbox_tests.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn ean13_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -43,6 +44,7 @@ fn ean13_black_box1_test_case() { * * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn ean13_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -62,6 +64,7 @@ fn ean13_black_box2_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn ean13_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -82,6 +85,7 @@ fn ean13_black_box3_test_case() { * A very difficult set of images taken with extreme shadows and highlights. * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn ean13_black_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -101,6 +105,7 @@ fn ean13_black_box4_test_case() { * A set of blurry images taken with a fixed-focus device. * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn ean13_black_box5_blurry_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/ean_8_black_box_1_testcase.rs b/tests/ean_8_black_box_1_testcase.rs index f21aaca4..9f66f926 100644 --- a/tests/ean_8_black_box_1_testcase.rs +++ b/tests/ean_8_black_box_1_testcase.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn ean8_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/inverted_data_matrix_black_box_testcase.rs b/tests/inverted_data_matrix_black_box_testcase.rs index 9b52a5c8..405a784c 100644 --- a/tests/inverted_data_matrix_black_box_testcase.rs +++ b/tests/inverted_data_matrix_black_box_testcase.rs @@ -22,6 +22,7 @@ mod common; /** * Inverted barcodes */ +#[cfg(feature = "image-formats")] #[test] fn inverted_data_matrix_black_box_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/itf_blackbox.rs b/tests/itf_blackbox.rs index 6e05ac45..41716ef8 100644 --- a/tests/itf_blackbox.rs +++ b/tests/itf_blackbox.rs @@ -22,6 +22,7 @@ mod common; /** * @author kevin.osullivan@sita.aero */ +#[cfg(feature = "image-formats")] #[test] fn itfblack_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -40,6 +41,7 @@ fn itfblack_box1_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn itfblack_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/maxicode_blackbox_tests.rs b/tests/maxicode_blackbox_tests.rs index e27a7b90..f61e02b4 100644 --- a/tests/maxicode_blackbox_tests.rs +++ b/tests/maxicode_blackbox_tests.rs @@ -22,6 +22,7 @@ mod common; /** * Tests {@link MaxiCodeReader} against a fixed set of test images. */ +#[cfg(feature = "image-formats")] #[test] fn maxicode1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -43,6 +44,7 @@ fn maxicode1_test_case() { * @author Daniel Gredler * @see Defect 1543 */ +#[cfg(feature = "image-formats")] #[test] fn maxi_code_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/pdf_417_black_box_4_testcase.rs b/tests/pdf_417_black_box_4_testcase.rs index 0ea8ccc5..dfe9ec54 100644 --- a/tests/pdf_417_black_box_4_testcase.rs +++ b/tests/pdf_417_black_box_4_testcase.rs @@ -25,6 +25,7 @@ use rxing::pdf417::PDF417Reader; * * @author Guenther Grau */ +#[cfg(feature = "image-formats")] #[test] fn pdf417_black_box4_test_case() { let mut tester = common::MultiImageSpanAbstractBlackBoxTestCase::new( diff --git a/tests/pdf_417_blackbox_tests.rs b/tests/pdf_417_blackbox_tests.rs index 7dd6bf32..46af1b5d 100644 --- a/tests/pdf_417_blackbox_tests.rs +++ b/tests/pdf_417_blackbox_tests.rs @@ -9,6 +9,7 @@ mod common; * * @author SITA Lab (kevin.osullivan@sita.aero) */ +#[cfg(feature = "image-formats")] #[test] fn pdf417_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -30,6 +31,7 @@ fn pdf417_black_box1_test_case() { * * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn pdf417_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -46,6 +48,7 @@ fn pdf417_black_box2_test_case() { /** * Tests {@link PDF417Reader} against more sample images. */ +#[cfg(feature = "image-formats")] #[test] fn pdf417_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/qr_code_blackbox_tests.rs b/tests/qr_code_blackbox_tests.rs index 93b9a693..fde89a2c 100644 --- a/tests/qr_code_blackbox_tests.rs +++ b/tests/qr_code_blackbox_tests.rs @@ -23,6 +23,7 @@ mod common; * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -43,6 +44,7 @@ fn qrcode_black_box1_test_case() { * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -63,6 +65,7 @@ fn qrcode_black_box2_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -84,6 +87,7 @@ fn qrcode_black_box3_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -108,6 +112,7 @@ fn qrcode_black_box4_test_case() { * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box5_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -128,6 +133,7 @@ fn qrcode_black_box5_test_case() { * rotation, which was a weak spot. */ +#[cfg(feature = "image-formats")] #[test] fn qrcode_black_box6_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/rss_14_blackbox.rs b/tests/rss_14_blackbox.rs index 3372fe08..faeb9a3b 100644 --- a/tests/rss_14_blackbox.rs +++ b/tests/rss_14_blackbox.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn rss14_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -40,6 +41,7 @@ fn rss14_black_box1_test_case() { /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn rss14_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/rss_expanded_blackbox.rs b/tests/rss_expanded_blackbox.rs index 2f0f3923..e41f1034 100644 --- a/tests/rss_expanded_blackbox.rs +++ b/tests/rss_expanded_blackbox.rs @@ -32,6 +32,7 @@ mod common; /** * A test of {@link RSSExpandedReader} against a fixed test set of images. */ +#[cfg(feature = "image-formats")] #[test] fn rssexpanded_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -50,6 +51,7 @@ fn rssexpanded_black_box1_test_case() { /** * A test of {@link RSSExpandedReader} against a fixed test set of images. */ +#[cfg(feature = "image-formats")] #[test] fn rssexpanded_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -68,6 +70,7 @@ fn rssexpanded_black_box2_test_case() { /** * A test of {@link RSSExpandedReader} against a fixed test set of images. */ +#[cfg(feature = "image-formats")] #[test] fn rssexpanded_black_box3_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -87,6 +90,7 @@ fn rssexpanded_black_box3_test_case() { * A test of {@link RSSExpandedReader} against a fixed test set of images including * stacked RSS barcodes. */ +#[cfg(feature = "image-formats")] #[test] fn rssexpanded_stacked_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -106,6 +110,7 @@ fn rssexpanded_stacked_black_box1_test_case() { * A test of {@link RSSExpandedReader} against a fixed test set of images including * stacked RSS barcodes. */ +#[cfg(feature = "image-formats")] #[test] fn rssexpanded_stacked_black_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/telepen_blackbox_1_test_case.rs b/tests/telepen_blackbox_1_test_case.rs index 97a8bc1f..fd11ca68 100644 --- a/tests/telepen_blackbox_1_test_case.rs +++ b/tests/telepen_blackbox_1_test_case.rs @@ -22,6 +22,7 @@ mod common; /** * @author Chris Wood */ +#[cfg(feature = "image-formats")] #[test] fn telepen_alpha_test_case() { const NUMTESTS: u32 = 5; @@ -38,6 +39,7 @@ fn telepen_alpha_test_case() { tester.test_black_box(); } +#[cfg(feature = "image-formats")] #[test] fn telepen_numeric_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/upc_a_blackbox_tests.rs b/tests/upc_a_blackbox_tests.rs index 6e250369..6c8a4b5d 100644 --- a/tests/upc_a_blackbox_tests.rs +++ b/tests/upc_a_blackbox_tests.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn upcablack_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -41,6 +42,7 @@ fn upcablack_box1_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upcablack_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -60,6 +62,7 @@ fn upcablack_box2_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upcablack_box3_reflective_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -79,6 +82,7 @@ fn upcablack_box3_reflective_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upcablack_box4_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -98,6 +102,7 @@ fn upcablack_box4_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upcablack_box5_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -118,6 +123,7 @@ fn upcablack_box5_test_case() { * A set of blurry images taken with a fixed-focus device. * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upcablack_box6_blurry_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/upc_e_blackbox_tests.rs b/tests/upc_e_blackbox_tests.rs index ce52b184..6d5a6826 100644 --- a/tests/upc_e_blackbox_tests.rs +++ b/tests/upc_e_blackbox_tests.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn upceblack_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -41,6 +42,7 @@ fn upceblack_box1_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upceblack_box2_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( @@ -60,6 +62,7 @@ fn upceblack_box2_test_case() { /** * @author dswitkin@google.com (Daniel Switkin) */ +#[cfg(feature = "image-formats")] #[test] fn upceblack_box3_reflective_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( diff --git a/tests/upc_ean_extension_black_box_1_testcase.rs b/tests/upc_ean_extension_black_box_1_testcase.rs index f9e6beee..6bd3d908 100644 --- a/tests/upc_ean_extension_black_box_1_testcase.rs +++ b/tests/upc_ean_extension_black_box_1_testcase.rs @@ -22,6 +22,7 @@ mod common; /** * @author Sean Owen */ +#[cfg(feature = "image-formats")] #[test] fn upceanextension_black_box1_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new( From 4ed49e10ff5eef773964bfe347f3ec3a8da8b1a3 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 12:53:04 -0600 Subject: [PATCH 04/34] 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 2946991d..70fd9622 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 398a54b2..30919e24 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,18 @@ 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, + formatInfoBits2, + Self::MirrorBits(formatInfoBits1), + mirroredFormatInfoBits2, + ], + ); + + let mut fi_model1 = Self::FindBestFormatInfo( + &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], FORMAT_INFO_DECODE_LOOKUP, &[ formatInfoBits1, @@ -60,6 +73,11 @@ impl FormatInformation { ], ); + 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 892352f0..d11cc560 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 bd0967cc..3504747e 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 2d3e99ea..9583d6cb 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 58a92bc1..15c3d7b4 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 12aab658..0e0fceb4 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 68c19b17..e777dc5d 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 67f326a6..66b9c5f0 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 fa356a0c..03a3bae9 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 6decf1bc..c02f3482 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 492b4453..8e7281ce 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 aeb58c75..e580a501 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 00000000..7f363478 --- /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 05/34] port: migrate to new cpp Version / FormatInformation --- .../base_extentions/qr_formatinformation.rs | 170 ++++++++++-------- .../base_extentions/qrcode_version.rs | 111 ++++++------ 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, 281 insertions(+), 271 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 30919e24..143db832 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_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_MASK_MICRO: u32 = 0x4445; + +// 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,20 +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, - &[ - formatInfoBits1, - formatInfoBits2, - Self::MirrorBits(formatInfoBits1), - mirroredFormatInfoBits2, - ], - ); - - let mut fi_model1 = Self::FindBestFormatInfo( - &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], - FORMAT_INFO_DECODE_LOOKUP, + &[FORMAT_INFO_MASK_QR, 0, FORMAT_INFO_MASK_QR_MODEL1], &[ formatInfoBits1, formatInfoBits2, @@ -73,15 +67,10 @@ impl FormatInformation { ], ); - 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 d11cc560..ca63868c 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); + pub fn Model2(version_number: u32) -> Result { + if version_number < 1 || version_number > 40 { + Err(Exceptions::ILLEGAL_ARGUMENT) + } else { + Ok(&VERSIONS[version_number as usize - 1]) } + } - Ok(if is_micro { - &MICRO_VERSIONS[versionNumber as usize - 1] - } else if is_model1 { - &MODEL1_VERSIONS[versionNumber as usize - 1] + pub fn Micro(version_number: u32) -> Result { + if version_number < 1 || version_number > 4 { + Err(Exceptions::ILLEGAL_ARGUMENT) } else { - &VERSIONS[versionNumber as usize - 1] - }) + 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 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 const fn isQRCodeModel1(&self) -> bool { - self.is_model1 + 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 15c3d7b4..957f35c0 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); + + match qr_type { + Type::Model1 => return Version::Model1(number), - let mut version = Version::FromDimension(dimension, false)?; + 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 0e0fceb4..8b60af52 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 f18f0382..8b9931e7 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 00000000..610d7415 --- /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 f6610e3b..1c04e9aa 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 e777dc5d..2b5bc742 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 66b9c5f0..352329c1 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 03a3bae9..324f06a7 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 c02f3482..d5d0b1e9 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 8e7281ce..4591cc92 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 858f4baa..c6cb3ad4 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 e580a501..59e22af6 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 06/34] 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 143db832..a2123293 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 ca63868c..ab779bd3 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 8b60af52..f9b3c847 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 07/34] 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 957f35c0..393f021b 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 => {} - } - 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); - } + Type::Model1 => Version::Model1(number), + Type::Micro => Version::Micro(number), + Type::Model2 => Version::Model2(number), } - - 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 08/34] 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 01b63c41..9a30749a 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 a2123293..533a6c37 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 ab779bd3..2ba70598 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 657906a0..031bcff4 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 a0a05b5b..03695fe8 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 393f021b..a0c9f315 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 f9b3c847..a4549027 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 61f7ea13..a43fb52f 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 6d711a25..d1bab452 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 610d7415..7988e16c 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 352329c1..3177acc7 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 324f06a7..52ccb7c8 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 d5d0b1e9..b2214d3d 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 00000000..13e12ab7 --- /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 4dd3017b..4df88c0c 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 4591cc92..879edd03 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 835abd76..2e3285a4 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 c6cb3ad4..7c6f032e 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 59e22af6..1b0a8ff5 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 00000000..bbc65987 --- /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 00000000..ea78e300 --- /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 00000000..f29c8893 --- /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 00000000..8b22b22d --- /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 00000000..da82284f --- /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 09/34] 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 2ba70598..039fbf33 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 10/34] 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 533a6c37..bee5c630 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 039fbf33..0bdb1a20 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 a0c9f315..9417f4b0 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 a43fb52f..91c4f26f 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 1b0a8ff5..f5d00f1b 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 11/34] 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 | 123 +++++++++----- 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, 167 insertions(+), 177 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index bee5c630..2dd548db 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 0bdb1a20..b6b7084f 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; - pub fn HasRMQRSize(bitMatrix: &BitMatrix) -> bool { - Self::getVersionRMQR(bitMatrix) != -1 - } + let square = |s: i32| point(s, s); + let valid = |v: i32, max: i32| v >= 1 && v <= max; - pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool { - let size = bitMatrix.height(); - if bitMatrix.width() != size { - Self::HasRMQRSize(bitMatrix) - } else { - Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1) + 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 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 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 DimensionOfVersionRMQR(version_number: u32) -> PointI { - if version_number < 1 || version_number as usize > dimsVersionRMQR.len() { - point(0, 0) + 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 { - dimsVersionRMQR[version_number as usize - 1] + return 0; } } - 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 + pub fn Number(bitMatrix: &BitMatrix) -> u32 { + 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 9417f4b0..b625a026 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 91c4f26f..e330e520 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 205dfa13..c0d44500 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 12/34] 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 b6b7084f..0216f144 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 e330e520..93d7ed7e 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 13/34] 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 2dd548db..1e2ca2dd 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 0216f144..4700b1e4 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 93d7ed7e..dfd6118a 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 14/34] 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 dfd6118a..56b2bde2 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 26cbb083..8bd8a316 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 6ea0892c..2be9cafe 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 15/34] 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 56b2bde2..ee7615a1 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 8bd8a316..951592a1 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 2be9cafe..7a811fd9 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 16/34] 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 ee7615a1..a7af60d6 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 17/34] 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 a7af60d6..5efa7868 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 18/34] 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 5efa7868..0bcc48f6 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 Date: Thu, 11 Jan 2024 12:53:04 -0600 Subject: [PATCH 19/34] 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 2946991d..70fd9622 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 398a54b2..30919e24 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,18 @@ 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, + formatInfoBits2, + Self::MirrorBits(formatInfoBits1), + mirroredFormatInfoBits2, + ], + ); + + let mut fi_model1 = Self::FindBestFormatInfo( + &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], FORMAT_INFO_DECODE_LOOKUP, &[ formatInfoBits1, @@ -60,6 +73,11 @@ impl FormatInformation { ], ); + 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 892352f0..d11cc560 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 bd0967cc..3504747e 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 2d3e99ea..9583d6cb 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 58a92bc1..15c3d7b4 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 12aab658..0e0fceb4 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 68c19b17..e777dc5d 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 67f326a6..66b9c5f0 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 fa356a0c..03a3bae9 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 6decf1bc..c02f3482 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 492b4453..8e7281ce 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 aeb58c75..e580a501 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 00000000..7f363478 --- /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 20/34] port: migrate to new cpp Version / FormatInformation --- .../base_extentions/qr_formatinformation.rs | 170 ++++++++++-------- .../base_extentions/qrcode_version.rs | 111 ++++++------ 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, 281 insertions(+), 271 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 30919e24..143db832 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_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_MASK_MICRO: u32 = 0x4445; + +// 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,20 +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, - &[ - formatInfoBits1, - formatInfoBits2, - Self::MirrorBits(formatInfoBits1), - mirroredFormatInfoBits2, - ], - ); - - let mut fi_model1 = Self::FindBestFormatInfo( - &[FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1], - FORMAT_INFO_DECODE_LOOKUP, + &[FORMAT_INFO_MASK_QR, 0, FORMAT_INFO_MASK_QR_MODEL1], &[ formatInfoBits1, formatInfoBits2, @@ -73,15 +67,10 @@ impl FormatInformation { ], ); - 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 d11cc560..ca63868c 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); + pub fn Model2(version_number: u32) -> Result { + if version_number < 1 || version_number > 40 { + Err(Exceptions::ILLEGAL_ARGUMENT) + } else { + Ok(&VERSIONS[version_number as usize - 1]) } + } - Ok(if is_micro { - &MICRO_VERSIONS[versionNumber as usize - 1] - } else if is_model1 { - &MODEL1_VERSIONS[versionNumber as usize - 1] + pub fn Micro(version_number: u32) -> Result { + if version_number < 1 || version_number > 4 { + Err(Exceptions::ILLEGAL_ARGUMENT) } else { - &VERSIONS[versionNumber as usize - 1] - }) + 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 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 const fn isQRCodeModel1(&self) -> bool { - self.is_model1 + 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 15c3d7b4..957f35c0 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); + + match qr_type { + Type::Model1 => return Version::Model1(number), - let mut version = Version::FromDimension(dimension, false)?; + 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 0e0fceb4..8b60af52 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 f18f0382..8b9931e7 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 00000000..610d7415 --- /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 f6610e3b..1c04e9aa 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 e777dc5d..2b5bc742 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 66b9c5f0..352329c1 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 03a3bae9..324f06a7 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 c02f3482..d5d0b1e9 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 8e7281ce..4591cc92 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 858f4baa..c6cb3ad4 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 e580a501..59e22af6 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 37bde9b2591079e8dd6103467481c9fe2813a517 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 17:35:23 -0600 Subject: [PATCH 21/34] 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 143db832..a2123293 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 ca63868c..ab779bd3 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 8b60af52..f9b3c847 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 cf47d8a8263673a7c143aa435d9d3d3b38f28c82 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Thu, 11 Jan 2024 17:39:27 -0600 Subject: [PATCH 22/34] 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 957f35c0..393f021b 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 => {} - } - 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); - } + Type::Model1 => Version::Model1(number), + Type::Micro => Version::Micro(number), + Type::Model2 => Version::Model2(number), } - - Err(Exceptions::FORMAT) } pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result { From d31da2f858443c866e0b9167489c1bc710e307ab Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 13:48:13 -0600 Subject: [PATCH 23/34] 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 01b63c41..9a30749a 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 a2123293..533a6c37 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 ab779bd3..2ba70598 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 657906a0..031bcff4 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 a0a05b5b..03695fe8 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 393f021b..a0c9f315 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 f9b3c847..a4549027 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 61f7ea13..a43fb52f 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 6d711a25..d1bab452 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 610d7415..7988e16c 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 352329c1..3177acc7 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 324f06a7..52ccb7c8 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 d5d0b1e9..b2214d3d 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 00000000..13e12ab7 --- /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 4dd3017b..4df88c0c 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 4591cc92..879edd03 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 835abd76..2e3285a4 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 c6cb3ad4..7c6f032e 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 59e22af6..1b0a8ff5 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 00000000..bbc65987 --- /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 00000000..ea78e300 --- /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 00000000..f29c8893 --- /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 00000000..8b22b22d --- /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 00000000..da82284f --- /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 24/34] 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 2ba70598..039fbf33 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 f4d9766275b7285748c1924f1603106a652206a7 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 14:02:02 -0600 Subject: [PATCH 25/34] 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 533a6c37..bee5c630 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 039fbf33..0bdb1a20 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 a0c9f315..9417f4b0 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 a43fb52f..91c4f26f 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 1b0a8ff5..f5d00f1b 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 42e2910f6241eef5c97661d8224d0368233774d0 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 15:20:15 -0600 Subject: [PATCH 26/34] 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 | 123 +++++++++----- 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, 167 insertions(+), 177 deletions(-) diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index bee5c630..2dd548db 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 0bdb1a20..b6b7084f 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; - pub fn HasRMQRSize(bitMatrix: &BitMatrix) -> bool { - Self::getVersionRMQR(bitMatrix) != -1 - } + let square = |s: i32| point(s, s); + let valid = |v: i32, max: i32| v >= 1 && v <= max; - pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool { - let size = bitMatrix.height(); - if bitMatrix.width() != size { - Self::HasRMQRSize(bitMatrix) - } else { - Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1) + 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 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 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 DimensionOfVersionRMQR(version_number: u32) -> PointI { - if version_number < 1 || version_number as usize > dimsVersionRMQR.len() { - point(0, 0) + 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 { - dimsVersionRMQR[version_number as usize - 1] + return 0; } } - 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 + pub fn Number(bitMatrix: &BitMatrix) -> u32 { + 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 9417f4b0..b625a026 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 91c4f26f..e330e520 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 205dfa13..c0d44500 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 c9e10ea9cacd654f8858419d32a04c6215e58771 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 18:11:55 -0600 Subject: [PATCH 27/34] 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 b6b7084f..0216f144 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 e330e520..93d7ed7e 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 6faf506398ac2d7b3a241286716b6520583cd366 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 13 Jan 2024 18:12:23 -0600 Subject: [PATCH 28/34] 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 2dd548db..1e2ca2dd 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 0216f144..4700b1e4 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 93d7ed7e..dfd6118a 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 29/34] 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 dfd6118a..56b2bde2 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 26cbb083..8bd8a316 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 e0f92c85..e2bc7616 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -333,10 +333,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 7f02bd07254e2ff6a7b2371a7dd4fb10554eae42 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sun, 14 Jan 2024 17:14:55 -0600 Subject: [PATCH 30/34] 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 56b2bde2..ee7615a1 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 8bd8a316..951592a1 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 e2bc7616..ac90a811 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -15,7 +15,9 @@ */ #![cfg(feature = "image")] -use rxing::{qrcode::cpp_port::QrReader, BarcodeFormat, MultiUseMultiFormatReader}; +use rxing::{ + qrcode::cpp_port::QrReader, BarcodeFormat, MultiFormatReader, MultiUseMultiFormatReader, +}; mod common; @@ -330,13 +332,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 4139d83bfb0ff5ea197613ed12dc7c38a9393f2f Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sun, 14 Jan 2024 17:16:10 -0600 Subject: [PATCH 31/34] 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 ee7615a1..a7af60d6 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 32/34] 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 a7af60d6..5efa7868 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 33/34] 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 5efa7868..0bcc48f6 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 Date: Tue, 16 Jan 2024 12:46:18 -0600 Subject: [PATCH 34/34] fix: include cfg directive for new test case --- Cargo.toml | 2 +- tests/cpp_qr_code_blackbox_tests.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7b5b2f36..a696b38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rxing" -version = "0.5.3" +version = "0.5.4" description="A rust port of the zxing barcode library." license="Apache-2.0" repository="https://github.com/rxing-core/rxing" diff --git a/tests/cpp_qr_code_blackbox_tests.rs b/tests/cpp_qr_code_blackbox_tests.rs index ac90a811..78833735 100644 --- a/tests/cpp_qr_code_blackbox_tests.rs +++ b/tests/cpp_qr_code_blackbox_tests.rs @@ -328,6 +328,7 @@ fn cpp_qrcode_black_box7_test_case() { tester.test_black_box(); } +#[cfg(feature = "image-formats")] #[test] fn cpp_rmqr_blackbox_test_case() { let mut tester = common::AbstractBlackBoxTestCase::new(