Merge pull request #43 from rxing-core/rmqr_port

rMQR port from zxing-cpp
This commit is contained in:
Henry A Schimke
2024-01-16 13:04:43 -06:00
committed by GitHub
41 changed files with 3458 additions and 1123 deletions

View File

@@ -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"

View File

@@ -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,

View File

@@ -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
}

View File

@@ -1,41 +1,49 @@
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_MASK_MODEL2, FORMAT_INFO_MASK_QR,
},
};
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_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,
// 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 {
/**
@@ -49,9 +57,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(
FORMAT_INFO_MASK_QR,
FORMAT_INFO_DECODE_LOOKUP,
&[FORMAT_INFO_MASK_QR, 0, FORMAT_INFO_MASK_QR_MODEL1],
&[
formatInfoBits1,
formatInfoBits2,
@@ -62,8 +70,8 @@ impl FormatInformation {
// 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
@@ -72,8 +80,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)],
);
@@ -81,34 +88,67 @@ 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
}
/**
* @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 mut fi = if formatInfoBits2 != 0 {
Self::FindBestFormatInfoRMQR(&[formatInfoBits1], &[formatInfoBits2])
} else {
// TODO probably remove if `sampleRMQR()` done properly
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.microVersion = (fi.data & 0x1F) + 1;
fi.isMirrored = false; // TODO: implement mirrored format bit reading
fi
}
#[inline(always)]
pub fn MirrorBits(bits: u32) -> u32 {
(bits.reverse_bits()) >> 17
}
pub fn FindBestFormatInfo(mask: 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.
for mask in [0, mask] {
// for (auto mask : {0, mask})
for (bitsIndex, bit_set) in bits.iter().enumerate() {
// 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 : 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;
}
@@ -116,16 +156,102 @@ 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
}
// 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 {
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 (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
}
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()
}
}

View File

@@ -1,51 +1,94 @@
use crate::common::Result;
use crate::qrcode::decoder::{Version, VersionRef, MICRO_VERSIONS, VERSIONS, VERSION_DECODE_INFO};
use crate::Exceptions;
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2023 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
// 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;
// }
use num::Integer;
use crate::common::{BitMatrix, Result};
use crate::qrcode::cpp_port::Type;
use crate::qrcode::decoder::{
Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, RMQR_VERSIONS, VERSIONS,
VERSION_DECODE_INFO,
};
use crate::{point, Exceptions, PointI};
const RMQR_SIZES: [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 FromDimension(dimension: u32) -> Result<VersionRef> {
let isMicro = dimension < 21;
if dimension % Self::DimensionStep(isMicro) != 1 {
//throw std::invalid_argument("Unexpected dimension");
return Err(Exceptions::ILLEGAL_ARGUMENT);
}
Self::FromNumber(
(dimension - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro),
isMicro,
)
}
pub fn FromNumber(versionNumber: u32, is_micro: bool) -> Result<VersionRef> {
if versionNumber < 1 || versionNumber > (if is_micro { 4 } 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]
pub fn Model1(version_number: u32) -> Result<VersionRef> {
if !(1..=14).contains(&version_number) {
Err(Exceptions::ILLEGAL_ARGUMENT)
} else {
&VERSIONS[versionNumber as usize - 1]
})
Ok(&MODEL1_VERSIONS[version_number as usize - 1])
}
}
pub fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 {
pub fn Model2(version_number: u32) -> Result<VersionRef> {
if !(1..=40).contains(&version_number) {
Err(Exceptions::ILLEGAL_ARGUMENT)
} else {
Ok(&VERSIONS[version_number as usize - 1])
}
}
pub fn Micro(version_number: u32) -> Result<VersionRef> {
if !(1..=4).contains(&version_number) {
Err(Exceptions::ILLEGAL_ARGUMENT)
} else {
Ok(&MICRO_VERSIONS[version_number as usize - 1])
}
}
pub fn rMQR(version_number: u32) -> Result<VersionRef> {
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 - 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,
@@ -53,7 +96,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,
@@ -64,13 +107,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);
@@ -79,6 +115,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.
@@ -89,7 +128,112 @@ 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 const fn isRMQR(&self) -> bool {
Type::const_eq(self.qr_type, Type::RectMicro)
}
pub fn SymbolSize(version: u32, qr_type: Type) -> PointI {
let version = version as i32;
let square = |s: i32| point(s, s);
let valid = |v: i32, max: i32| v >= 1 && v <= max;
match qr_type {
Type::Model1 => {
if valid(version, 32) {
square(17 + 4 * version)
} else {
PointI::default()
}
}
Type::Model2 => {
if valid(version, 40) {
square(17 + 4 * version)
} else {
PointI::default()
}
}
Type::Micro => {
if valid(version, 4) {
square(9 + 2 * version)
} else {
PointI::default()
}
}
Type::RectMicro => {
if valid(version, 32) {
RMQR_SIZES[(version - 1) as usize]
} else {
PointI::default()
}
}
}
}
pub fn 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 {
Self::IsValidSize(
point(bitMatrix.width() as i32, bitMatrix.height() as i32),
qr_type,
)
}
pub fn HasValidSize(matrix: &BitMatrix) -> bool {
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)
.map(|x| x as i32)
.unwrap_or(-1)
}
pub fn NumberPoint(size: PointI) -> 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 {
0
}
}
pub fn Number(bitMatrix: &BitMatrix) -> u32 {
Self::NumberPoint(point(bitMatrix.width() as i32, bitMatrix.height() as i32))
}
}

View File

@@ -157,6 +157,13 @@ where
self
}
pub fn withIsModel1(mut self, is_model_1: bool) -> DecoderResult<T> {
if is_model_1 {
self.content.symbology.modifier = 48
}
self
}
// pub fn build(self) -> DecoderResult<T> {
// }

View File

@@ -38,7 +38,7 @@ pub struct ECIStringBuilder {
pub has_eci: bool,
eci_result: Option<String>,
bytes: Vec<u8>,
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<Eci>,
}

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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>) -> bool {
let mirrored = mirrored.unwrap_or(false);
@@ -21,51 +21,23 @@ pub fn getBit(bitMatrix: &BitMatrix, x: u32, y: u32, mirrored: Option<bool>) ->
}
}
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) -> Result<VersionRef> {
let dimension = bitMatrix.height();
let mut version = Version::FromDimension(dimension)?;
if version.getVersionNumber() < 7 {
return Ok(version);
}
for mirror in [false, true] {
// for (bool mirror : {false, true}) {
// Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored)
let mut versionBits = 0;
for y in (0..=5).rev() {
// for (int y = 5; y >= 0; --y) {
for x in ((dimension - 11)..=(dimension - 9)).rev() {
// for (int x = dimension - 9; x >= dimension - 11; --x) {
AppendBit(&mut versionBits, getBit(bitMatrix, x, y, Some(mirror)));
}
}
version = Version::DecodeVersionInformation(versionBits, 0)?; // THIS MIGHT BE WRONG todo!()
if version.getDimensionForVersion() == dimension {
return Ok(version);
}
}
Err(Exceptions::FORMAT)
}
pub fn ReadFormatInformation(bitMatrix: &BitMatrix, isMicro: bool) -> Result<FormatInformation> {
if !hasValidDimension(bitMatrix, isMicro) {
pub fn ReadVersion(bitMatrix: &BitMatrix, qr_type: Type) -> Result<VersionRef> {
if !Version::HasValidSize(bitMatrix) {
return Err(Exceptions::FORMAT);
}
if isMicro {
let number = Version::Number(bitMatrix);
match qr_type {
Type::Model1 => Version::Model1(number),
Type::Micro => Version::Micro(number),
Type::Model2 => Version::Model2(number),
Type::RectMicro => Version::rMQR(number),
}
}
pub fn ReadFormatInformation(bitMatrix: &BitMatrix) -> Result<FormatInformation> {
if Version::HasValidSizeType(bitMatrix, Type::Micro) {
// Read top-left format info bits
let mut formatInfoBits = 0;
for x in 1..9 {
@@ -80,6 +52,47 @@ pub fn ReadFormatInformation(bitMatrix: &BitMatrix, isMicro: bool) -> Result<For
return Ok(FormatInformation::DecodeMQR(formatInfoBits as u32));
}
if Version::HasValidSizeType(bitMatrix, Type::RectMicro) {
// 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 {
@@ -235,18 +248,173 @@ pub fn ReadMQRCodewords(
Ok(result.iter().copied().map(|x| x as u8).collect())
}
pub fn ReadQRCodewordsModel1(
bitMatrix: &BitMatrix,
version: VersionRef,
formatInfo: &FormatInformation,
) -> Result<Vec<u8>> {
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 ReadRMQRCodewords(
bitMatrix: &BitMatrix,
version: VersionRef,
formatInfo: &FormatInformation,
) -> Result<Vec<u8>> {
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,
formatInfo: &FormatInformation,
) -> Result<Vec<u8>> {
if !hasValidDimension(bitMatrix, version.isMicroQRCode()) {
return Err(Exceptions::FORMAT);
}
if version.isMicroQRCode() {
ReadMQRCodewords(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),
Type::RectMicro => ReadRMQRCodewords(bitMatrix, version, formatInfo),
}
}

View File

@@ -299,6 +299,10 @@ pub fn DecodeBitStream(
let mut structuredAppend = StructuredAppendInfo::default();
let modeBitLength = Mode::get_codec_mode_bits_length(version);
if version.isModel1() {
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 {
@@ -306,7 +310,7 @@ pub fn DecodeBitStream(
} else {
Mode::CodecModeForBits(
bits.readBits(modeBitLength as usize)?,
Some(version.isMicroQRCode()),
Some(version.qr_type),
)?
};
@@ -387,16 +391,24 @@ pub fn DecodeBitStream(
.withError(res.err())
.withEcLevel(ecLevel.to_string())
.withVersionNumber(version.getVersionNumber())
.withStructuredAppend(structuredAppend))
.withStructuredAppend(structuredAppend)
.withIsModel1(version.isModel1()))
}
pub fn Decode(bits: &BitMatrix) -> Result<DecoderResult<bool>> {
let Ok(pversion) = ReadVersion(bits) 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) = 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"));
};

View File

@@ -2,10 +2,11 @@ use crate::{
common::{
cpp_essentials::{
CenterOfRing, DMRegressionLine, FindConcentricPatternCorners, FindLeftGuardBy, Matrix,
Value,
},
DefaultGridSampler, GridSampler, Result, SamplerControl,
},
point_i,
point, point_i,
qrcode::{
decoder::{FormatInformation, Version, VersionRef},
detector::QRCodeDetectorResult,
@@ -26,6 +27,8 @@ use crate::{
point_f, Point,
};
use super::Type;
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct FinderPatternSet {
pub bl: ConcentricPattern,
@@ -567,7 +570,7 @@ pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result<QRCodeDetect
Quadrilateral::from([fp.tl.p, fp.tr.p, br.p, fp.bl.p]),
)?;
if dimension >= Version::DimensionOfVersion(7, false) 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
@@ -796,10 +799,9 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
// 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);
@@ -843,8 +845,7 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
.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,
@@ -885,10 +886,9 @@ pub fn DetectPureQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
pub fn DetectPureMQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
type Pattern = [PatternType; 5];
let MIN_MODULES = Version::DimensionOfVersion(1, true);
let MAX_MODULES = 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 {
@@ -911,8 +911,7 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
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 !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,
@@ -949,6 +948,109 @@ pub fn DetectPureMQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
// {{left, top}, {right, top}, {right, bottom}, {left, bottom}}};
}
pub fn DetectPureRMQR(image: &BitMatrix) -> Result<QRCodeDetectorResult> {
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<PatternView::value_type, PATTERN.size()>;
// type SubPattern = [PatternType; 5]; //std::array<PatternView::value_type, SUBPATTERN_RMQR.size()>;
// type CornerEdgePattern = [PatternType; 2]; //std::array<PatternView::value_type, CORNER_EDGE_RMQR.size()>;
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 (found, left, top, width, height) = image.findBoundingBox(0, 0, 0, 0, MIN_MODULES as u32);
if !found || height >= width {
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::<E2E, 5, 7, false>(&view, &PATTERN, None, 0.0, 0.0) == 0.0 {
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 = subdiagonal.to_vec().into();
let view = PatternView::new(&subdiagonal_hld);
if IsPattern::<false, 4, 4, false>(&view, &SUBPATTERN, None, 0.0, 0.0) == 0.0 {
return Err(Exceptions::NOT_FOUND);
}
let mut moduleSize: f32 =
(diagonal.iter().sum::<u16>() + subdiagonal.iter().sum::<u16>()) as f32;
// 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)?;
let timing_hld = timing.to_vec().into();
let view = PatternView::new(&timing_hld);
if IsPattern::<E2E, 10, 10, false>(&view, &TIMINGPATTERN, None, 0.0, 0.0) == 0.0 {
return Err(Exceptions::NOT_FOUND);
}
moduleSize += timing.iter().sum::<u16>() 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
// 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 as u32,
dimH as u32,
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<QRCodeDetectorResult> {
let Some(fpQuad) = FindConcentricPatternCorners(image, fp.p, fp.size, 2) else {
return Err(Exceptions::NOT_FOUND);
@@ -986,6 +1088,7 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result<QRCodeDetec
srcQuad,
fpQuad.rotated_corners(Some(0), None),
)?;
let cur = EdgeTracer::new(image, Point::default(), Point::default());
for i in 0..4 {
// for (int i = 0; i < 4; ++i) {
@@ -1011,7 +1114,7 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result<QRCodeDetec
{
AppendBit(
&mut formatInfoBits,
image.get_point(mod2Pix.transform_point(Point::centered(*info_coord))),
cur.blackAt(mod2Pix.transform_point(Point::centered(*info_coord))),
);
}
@@ -1026,7 +1129,7 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result<QRCodeDetec
return Err(Exceptions::NOT_FOUND);
}
let dim: u32 = Version::DimensionOfVersion(bestFI.microVersion, true);
let dim: u32 = Version::SymbolSize(bestFI.microVersion, Type::Micro).x as u32;
// check that we are in fact not looking at a corner of a non-micro QRCode symbol
// we accept at most 1/3rd black pixels in the quite zone (in a QRCode symbol we expect about 1/2).
@@ -1035,7 +1138,7 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result<QRCodeDetec
// for (int i = 0; i < dim; ++i) {
let px = bestPT.transform_point(Point::centered(point_i(i, dim)));
let py = bestPT.transform_point(Point::centered(point_i(dim, i)));
blackPixels += u32::from(image.is_in(px) && image.get_point(px))
blackPixels += u32::from(cur.blackAt(px) && cur.blackAt(px))
+ u32::from(image.is_in(py) && image.get_point(py));
}
if blackPixels > 2 * dim / 3 {
@@ -1057,3 +1160,214 @@ pub fn SampleMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result<QRCodeDetec
// SampleGrid(image, dim, dim, bestPT)
}
pub fn SampleRMQR(image: &BitMatrix, fp: ConcentricPattern) -> Result<QRCodeDetectorResult> {
// 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(11, 3),
point_i(11, 2),
point_i(11, 1),
point_i(10, 5),
point_i(10, 4),
point_i(10, 3),
point_i(10, 2),
point_i(10, 1),
point_i(9, 5),
point_i(9, 4),
point_i(9, 3),
point_i(9, 2),
point_i(9, 1),
point_i(8, 5),
point_i(8, 4),
point_i(8, 3),
point_i(8, 2),
point_i(8, 1),
];
let mut bestFI: FormatInformation = FormatInformation::default();
let mut bestPT: PerspectiveTransform = PerspectiveTransform::default();
let cur = EdgeTracer::new(image, Point::default(), Point::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
cur.testAt(mod2Pix.transform_point(Point::centered(FORMAT_INFO_EDGE_COORDS[i])))
== Value::from(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 coord in FORMAT_INFO_COORDS {
// for i in 0..FORMAT_INFO_COORDS.len() {
// for (int i = 0; i < Size(FORMAT_INFO_COORDS); ++i)
AppendBit(
&mut formatInfoBits,
cur.blackAt(mod2Pix.transform_point(Point::centered(coord))),
);
}
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::SymbolSize(bestFI.microVersion, Type::RectMicro);
// TODO: this is a WIP
let intersectQuads = |a: &Quadrilateral, b: &Quadrilateral| -> Result<Quadrilateral> {
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::<Point>::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,
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)
}

View File

@@ -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;

View File

@@ -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,14 @@ impl Reader for QrReader {
Ok(RXingResult::with_decoder_result(
decoderResult,
position,
if detectorResult.getBits().width() < 21 {
if detectorResult.getBits().width() != detectorResult.getBits().height() {
BarcodeFormat::RECTANGULAR_MICRO_QR_CODE
} else 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 +270,18 @@ impl QrReader {
let mut usedFPs: Vec<ConcentricPattern> = Vec::new();
let mut results: Vec<RXingResult> = 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 +310,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 +326,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 +344,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;

View File

@@ -0,0 +1,15 @@
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum Type {
Model1,
Model2,
Micro,
RectMicro,
}
impl Type {
pub const fn const_eq(a: Type, b: Type) -> bool {
let (a, b) = (a as u8, b as u8);
a == b
}
}

View File

@@ -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]);

View File

@@ -42,7 +42,7 @@ fn SimpleByteMode() {
let bytes: Vec<u8> = ba.into();
let result = DecodeBitStream(
&bytes,
Version::FromNumber(1, 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<u8> = ba.into();
let result = DecodeBitStream(
&bytes,
Version::FromNumber(1, 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<u8> = ba.into();
let result = DecodeBitStream(
&bytes,
Version::FromNumber(1, 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<u8> = ba.into();
let result = DecodeBitStream(
&bytes,
Version::FromNumber(1, 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<u8> = ba.into();
let result = DecodeBitStream(
&bytes,
Version::FromNumber(1, 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).unwrap();
let version = Version::Model2(1).unwrap();
let ecLevel = ErrorCorrectionLevel::M;
// Plain "ANUM(1) A"

View File

@@ -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 =
@@ -12,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);
@@ -20,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
@@ -63,11 +94,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]
@@ -127,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);
}
}

View File

@@ -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]
@@ -27,27 +39,27 @@ fn CharacterCount() {
// Spot check a few values
assert_eq!(
10,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap())
Mode::CharacterCountBits(&Mode::NUMERIC, Version::Model2(5).unwrap())
);
assert_eq!(
12,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap())
Mode::CharacterCountBits(&Mode::NUMERIC, Version::Model2(26).unwrap())
);
assert_eq!(
14,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap())
Mode::CharacterCountBits(&Mode::NUMERIC, Version::Model2(40).unwrap())
);
assert_eq!(
9,
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap())
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::Model2(6).unwrap())
);
assert_eq!(
8,
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap())
Mode::CharacterCountBits(&Mode::BYTE, Version::Model2(7).unwrap())
);
assert_eq!(
8,
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false).unwrap())
Mode::CharacterCountBits(&Mode::KANJI, Version::Model2(8).unwrap())
);
}
@@ -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]
@@ -110,26 +122,110 @@ fn MicroCharacterCount() {
// Spot check a few values
assert_eq!(
3,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap())
Mode::CharacterCountBits(&Mode::NUMERIC, Version::Micro(1).unwrap())
);
assert_eq!(
4,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap())
Mode::CharacterCountBits(&Mode::NUMERIC, Version::Micro(2).unwrap())
);
assert_eq!(
6,
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap())
Mode::CharacterCountBits(&Mode::NUMERIC, Version::Micro(4).unwrap())
);
assert_eq!(
3,
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap())
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::Micro(2).unwrap())
);
assert_eq!(
4,
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap())
Mode::CharacterCountBits(&Mode::BYTE, Version::Micro(3).unwrap())
);
assert_eq!(
4,
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap())
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")
)
);
}

View File

@@ -6,13 +6,16 @@
use crate::{
common::BitMatrix,
qrcode::decoder::{Version, VersionRef},
qrcode::{
cpp_port::Type,
decoder::{Version, VersionRef},
},
};
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 +29,13 @@ fn DoTestVersion(expectedVersion: u32, mask: i32) {
#[test]
fn VersionForNumber() {
let version = Version::FromNumber(0, 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).expect("version number found"),
Version::Model2(i).expect("version number found"),
i,
4 * i + 17,
);
@@ -43,10 +46,13 @@ fn VersionForNumber() {
fn GetProvisionalVersionForDimension() {
for i in 1..=40 {
// for (int i = 1; i <= 40; i++) {
let prov = Version::FromDimension(4 * i + 17)
.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,13 +69,13 @@ fn DecodeVersionInformation() {
#[test]
fn MicroVersionForNumber() {
let version = Version::FromNumber(0, true);
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).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,
);
@@ -80,10 +86,12 @@ fn MicroVersionForNumber() {
fn GetProvisionalMicroVersionForDimension() {
for i in 1..=4 {
// for (int i = 1; i <= 4; i++) {
let prov = Version::FromDimension(2 * i + 9)
.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")
)
);
}
}
@@ -100,7 +108,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::Micro(i).expect("version must be found");
let functionPattern = version
.buildFunctionPattern()
.expect("function pattern must be found");
@@ -120,3 +128,130 @@ fn FunctionPattern() {
}
}
}
fn CheckRMQRVersion(version: VersionRef, number: u32) {
assert_eq!(number, version.getVersionNumber());
assert_eq!(
Version::SymbolSize(number, Type::RectMicro).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);
}
}

View File

@@ -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________________________"
);
}

View File

@@ -12,3 +12,5 @@ mod QRDataMaskTest;
mod QRBitMatrixParserTest;
mod MQRDecoderTest;
mod RMQRDecoderTest;

View File

@@ -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
@@ -74,7 +75,8 @@ pub struct FormatInformation {
pub microVersion: u32,
pub isMirrored: bool,
pub index: u8, // = 255;
pub mask: u32, // = 0
pub data: u32, // = 255
pub bitsIndex: u8, // = 255;
}
@@ -86,7 +88,8 @@ impl Default for FormatInformation {
data_mask: Default::default(),
microVersion: 0,
isMirrored: false,
index: 255,
mask: 0,
data: 255,
bitsIndex: 255,
}
}
@@ -104,8 +107,9 @@ impl FormatInformation {
error_correction_level: errorCorrectionLevel,
data_mask: dataMask,
isMirrored: false,
index: 255,
mask: 0,
bitsIndex: 255,
data: 255,
})
}

View File

@@ -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;

View File

@@ -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 {
(if version.isMicroQRCode() {
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 {
(if version.isMicroQRCode() {
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,20 +145,32 @@ 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<bool>) -> Result<Self> {
let isMicro = isMicro.unwrap_or(false);
const BITS_2_MODE_LEN: usize = 4;
pub fn CodecModeForBits(bits: u32, qr_type: Option<Type>) -> Result<Self> {
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);
}
} else {
const BITS_2_MODE: [Mode; BITS_2_MODE_LEN] =
if qr_type == Type::Micro {
const Bits2Mode: [Mode; 4] =
[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 < (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 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"))
@@ -168,7 +183,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],
@@ -179,6 +194,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 {

View File

@@ -18,6 +18,7 @@ use std::fmt;
use crate::{
common::{BitMatrix, Result},
qrcode::cpp_port::Type,
Exceptions,
};
@@ -29,6 +30,8 @@ pub type VersionRef = &'static Version;
pub static VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::buildVersions);
pub static MICRO_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_micro_versions);
pub static MODEL1_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_model1_versions);
pub static RMQR_VERSIONS: Lazy<Vec<Version>> = Lazy::new(Version::build_rmqr_versions);
/**
* See ISO 18004:2006 Annex D.
@@ -54,17 +57,22 @@ pub struct Version {
alignmentPatternCenters: Vec<u32>,
ecBlocks: Vec<ECBlocks>,
totalCodewords: u32,
pub(crate) is_micro: bool,
pub(crate) qr_type: Type,
}
impl Version {
fn new(versionNumber: u32, alignmentPatternCenters: Vec<u32>, ecBlocks: [ECBlocks; 4]) -> Self {
pub(super) fn new(
versionNumber: u32,
alignmentPatternCenters: Vec<u32>,
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 {
@@ -72,11 +80,15 @@ impl Version {
alignmentPatternCenters,
ecBlocks: ecBlocks.to_vec(),
totalCodewords: total,
is_micro: false,
qr_type: if ecBlocks[0].getECCodewordsPerBlock() != 0 {
Type::Model2
} else {
Type::RectMicro
},
}
}
fn new_micro(versionNumber: u32, ecBlocks: Vec<ECBlocks>) -> Self {
pub(super) fn new_micro(versionNumber: u32, ecBlocks: Vec<ECBlocks>) -> Self {
let mut total = 0;
let ecCodewords = ecBlocks[0].getECCodewordsPerBlock();
let ecbArray = ecBlocks[0].getECBlocks();
@@ -91,7 +103,26 @@ impl Version {
alignmentPatternCenters: Vec::default(),
ecBlocks,
totalCodewords: total,
is_micro: true,
qr_type: Type::Micro,
}
}
pub(super) fn new_model1(versionNumber: u32, ecBlocks: Vec<ECBlocks>) -> 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,
qr_type: Type::Model1,
}
}
@@ -108,7 +139,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
}
@@ -170,13 +201,55 @@ impl Version {
* See ISO 18004:2006 Annex E
*/
pub fn buildFunctionPattern(&self) -> Result<BitMatrix> {
if self.isRMQR() {
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, 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
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, (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(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((size.x - 5) as u32, (size.y - 5) as u32, 5 - 1, 5 - 1)?;
// Bottom right format
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((size.x - 2) as u32, 1);
if size.y > 9 {
// Bottom left corner finder
bitMatrix.set(1, (size.y - 2) as u32);
}
return Ok(bitMatrix);
}
let dimension = self.getDimensionForVersion();
let mut bitMatrix = BitMatrix::with_single_dimension(dimension)?;
// 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
@@ -215,761 +288,6 @@ impl Version {
Ok(bitMatrix)
}
pub fn build_micro_versions() -> Vec<Version> {
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<Version> {
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)))
]*/
}
}
impl fmt::Display for Version {

File diff suppressed because it is too large Load Diff

View File

@@ -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<T> PartialEq for PointT<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
impl Eq for Point {}
impl<T> Eq for PointT<T> where T: PartialEq {}
impl<T> PointT<T>
where

View File

@@ -0,0 +1,2 @@
symbologyIdentifier=]Q0
ecLevel=M

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@@ -0,0 +1 @@
QR Code Model 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081

View File

@@ -0,0 +1 @@
symbologyIdentifier=]Q1

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

View File

@@ -0,0 +1 @@
,,

View File

@@ -0,0 +1 @@
isInverted=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

View File

@@ -0,0 +1 @@
,,

View File

@@ -309,15 +309,6 @@ impl<T: Reader> AbstractBlackBoxTestCase<T> {
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));
// }
}
}
@@ -464,8 +455,10 @@ impl<T: Reader> AbstractBlackBoxTestCase<T> {
);
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
};
}

View File

@@ -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;
@@ -207,10 +209,10 @@ fn cpp_qrcode_black_box2_test_case() {
BarcodeFormat::QR_CODE,
);
tester.add_test(45, 47, 0.0);
tester.add_test(45, 47, 90.0);
tester.add_test(45, 47, 180.0);
tester.add_test(45, 46, 270.0);
tester.add_test(46, 48, 0.0);
tester.add_test(46, 48, 90.0);
tester.add_test(46, 48, 180.0);
tester.add_test(46, 48, 270.0);
tester.ignore_pure = true;
@@ -325,3 +327,27 @@ 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(
"test_resources/blackbox/cpp/rmqrcode-1",
MultiFormatReader::default(),
BarcodeFormat::RECTANGULAR_MICRO_QR_CODE,
);
// 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();
}
//