mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
feat: initial port for Rectangular Micro QR
This is the intial port for rMQR support. Directly ported from 7a294f2f3c (diff-5d6a0ddd024f3102876492f502a41becde594de81f46d942b87b64cbfdc1985b)
subsequent improvements will be incorporated in future patches.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ use crate::qrcode::{
|
||||
|
||||
pub const FORMAT_INFO_MASK_QR_MODEL1: u32 = 0x2825;
|
||||
pub const FORMAT_INFO_MASK_MICRO: u32 = 0x4445;
|
||||
pub const FORMAT_INFO_MASK_RMQR: u32 = 0x1FAB2; // Finder pattern side
|
||||
pub const FORMAT_INFO_MASK_RMQR_SUB: u32 = 0x20A7B; // Finder sub pattern side
|
||||
|
||||
// pub const FORMAT_INFO_DECODE_LOOKUP_MICRO: [u32 ;32] = [
|
||||
// 0x4445,
|
||||
@@ -94,6 +96,33 @@ impl FormatInformation {
|
||||
fi
|
||||
}
|
||||
|
||||
/**
|
||||
* @param formatInfoBits1 format info indicator, with mask still applied
|
||||
* @param formatInfoBits2 second copy of same info; both are checked at the same time to establish best match
|
||||
*/
|
||||
pub fn DecodeRMQR(formatInfoBits1: u32, formatInfoBits2: u32) -> Self {
|
||||
//FormatInformation fi;
|
||||
let mirror18Bits = |bits: u32| bits.reverse_bits() >> 14;
|
||||
let mut fi = if (formatInfoBits2 != 0) {
|
||||
Self::FindBestFormatInfoRMQR(
|
||||
&[formatInfoBits1, mirror18Bits(formatInfoBits1)],
|
||||
&[formatInfoBits2, mirror18Bits(formatInfoBits2)],
|
||||
)
|
||||
} else {
|
||||
// TODO probably remove if `sampleRMQR()` done properly
|
||||
Self::FindBestFormatInfoRMQR(&[formatInfoBits1, mirror18Bits(formatInfoBits1)], &[])
|
||||
};
|
||||
|
||||
// Bit 6 is error correction (M/H), and bits 0-5 version.
|
||||
fi.error_correction_level =
|
||||
ErrorCorrectionLevel::ECLevelFromBits(((fi.data >> 5) as u8 & 1) << 1, false); // Shift to match QRCode M/H
|
||||
fi.data_mask = 4; // ((y / 2) + (x / 3)) % 2 == 0
|
||||
fi.rMQRVersion = fi.data as u8 & 0x1F;
|
||||
fi.isMirrored = fi.bitsIndex > 1;
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn MirrorBits(bits: u32) -> u32 {
|
||||
(bits.reverse_bits()) >> 17
|
||||
@@ -153,16 +182,73 @@ impl FormatInformation {
|
||||
fi
|
||||
}
|
||||
|
||||
pub fn qr_type(&self) -> Type {
|
||||
pub fn FindBestFormatInfoRMQR(bits: &[u32], subbits: &[u32]) -> Self {
|
||||
// See ISO/IEC 23941:2022, Annex C, Table C.1 - Valid format information sequences
|
||||
const MASKED_PATTERNS: [u32; 64] = [
|
||||
// Finder pattern side
|
||||
0x1FAB2, 0x1E597, 0x1DBDD, 0x1C4F8, 0x1B86C, 0x1A749, 0x19903, 0x18626, 0x17F0E,
|
||||
0x1602B, 0x15E61, 0x14144, 0x13DD0, 0x122F5, 0x11CBF, 0x1039A, 0x0F1CA, 0x0EEEF,
|
||||
0x0D0A5, 0x0CF80, 0x0B314, 0x0AC31, 0x0927B, 0x08D5E, 0x07476, 0x06B53, 0x05519,
|
||||
0x04A3C, 0x036A8, 0x0298D, 0x017C7, 0x008E2, 0x3F367, 0x3EC42, 0x3D208, 0x3CD2D,
|
||||
0x3B1B9, 0x3AE9C, 0x390D6, 0x38FF3, 0x376DB, 0x369FE, 0x357B4, 0x34891, 0x33405,
|
||||
0x32B20, 0x3156A, 0x30A4F, 0x2F81F, 0x2E73A, 0x2D970, 0x2C655, 0x2BAC1, 0x2A5E4,
|
||||
0x29BAE, 0x2848B, 0x27DA3, 0x26286, 0x25CCC, 0x243E9, 0x23F7D, 0x22058, 0x21E12,
|
||||
0x20137,
|
||||
];
|
||||
const MASKED_PATTERNS_SUB: [u32; 64] = [
|
||||
// Finder sub pattern side
|
||||
0x20A7B, 0x2155E, 0x22B14, 0x23431, 0x248A5, 0x25780, 0x269CA, 0x276EF, 0x28FC7,
|
||||
0x290E2, 0x2AEA8, 0x2B18D, 0x2CD19, 0x2D23C, 0x2EC76, 0x2F353, 0x30103, 0x31E26,
|
||||
0x3206C, 0x33F49, 0x343DD, 0x35CF8, 0x362B2, 0x37D97, 0x384BF, 0x39B9A, 0x3A5D0,
|
||||
0x3BAF5, 0x3C661, 0x3D944, 0x3E70E, 0x3F82B, 0x003AE, 0x01C8B, 0x022C1, 0x03DE4,
|
||||
0x04170, 0x05E55, 0x0601F, 0x07F3A, 0x08612, 0x09937, 0x0A77D, 0x0B858, 0x0C4CC,
|
||||
0x0DBE9, 0x0E5A3, 0x0FA86, 0x108D6, 0x117F3, 0x129B9, 0x1369C, 0x14A08, 0x1552D,
|
||||
0x16B67, 0x17442, 0x18D6A, 0x1924F, 0x1AC05, 0x1B320, 0x1CFB4, 0x1D091, 0x1EEDB,
|
||||
0x1F1FE,
|
||||
];
|
||||
|
||||
let mut fi = FormatInformation::default();
|
||||
|
||||
let mut best = |bits: &[u32], &patterns: &[u32; 64], mask: u32| {
|
||||
for bitsIndex in 0..bits.len() {
|
||||
// for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) {
|
||||
for l_pattern in patterns {
|
||||
// for (uint32_t pattern : patterns) {
|
||||
// 'unmask' the pattern first to get the original 6-data bits + 12-ec bits back
|
||||
let pattern = l_pattern ^ mask;
|
||||
// Find the pattern with fewest bits differing
|
||||
let hammingDist = ((bits[bitsIndex] ^ mask) ^ pattern).count_ones();
|
||||
if (hammingDist < fi.hammingDistance) {
|
||||
fi.mask = mask; // store the used mask to discriminate between types/models
|
||||
fi.data = pattern >> 12; // drop the 12 BCH error correction bits
|
||||
fi.hammingDistance = hammingDist;
|
||||
fi.bitsIndex = bitsIndex as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
best(bits, &MASKED_PATTERNS, FORMAT_INFO_MASK_RMQR);
|
||||
if (!subbits.is_empty())
|
||||
// TODO probably remove if `sampleRMQR()` done properly
|
||||
{
|
||||
best(subbits, &MASKED_PATTERNS_SUB, FORMAT_INFO_MASK_RMQR_SUB);
|
||||
}
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
pub const fn qr_type(&self) -> Type {
|
||||
match self.mask {
|
||||
FORMAT_INFO_MASK_QR_MODEL1 => Type::Model1,
|
||||
FORMAT_INFO_MASK_MICRO => Type::Micro,
|
||||
FORMAT_INFO_MASK_RMQR | FORMAT_INFO_MASK_RMQR_SUB => Type::RectMicro,
|
||||
_ => Type::Model2,
|
||||
}
|
||||
}
|
||||
|
||||
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match
|
||||
pub fn isValid(&self) -> bool {
|
||||
// Hamming distance of the 32 masked codes is 7 (64 and 8 for rMQR), by construction, so <= 3 bits differing means we found a match
|
||||
pub const fn isValid(&self) -> bool {
|
||||
self.hammingDistance <= 3
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,45 @@
|
||||
use crate::common::{BitMatrix, Result};
|
||||
use crate::qrcode::cpp_port::Type;
|
||||
use crate::qrcode::decoder::{
|
||||
Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, VERSIONS, VERSION_DECODE_INFO,
|
||||
Version, VersionRef, MICRO_VERSIONS, MODEL1_VERSIONS, RMQR_VERSIONS, VERSIONS,
|
||||
VERSION_DECODE_INFO,
|
||||
};
|
||||
use crate::Exceptions;
|
||||
use crate::{point, Exceptions, PointI};
|
||||
|
||||
const dimsVersionRMQR: [PointI; 32] = [
|
||||
point(43, 7),
|
||||
point(59, 7),
|
||||
point(77, 7),
|
||||
point(99, 7),
|
||||
point(139, 7),
|
||||
point(43, 9),
|
||||
point(59, 9),
|
||||
point(77, 9),
|
||||
point(99, 9),
|
||||
point(139, 9),
|
||||
point(27, 11),
|
||||
point(43, 11),
|
||||
point(59, 11),
|
||||
point(77, 11),
|
||||
point(99, 11),
|
||||
point(139, 11),
|
||||
point(27, 13),
|
||||
point(43, 13),
|
||||
point(59, 13),
|
||||
point(77, 13),
|
||||
point(99, 13),
|
||||
point(139, 13),
|
||||
point(43, 15),
|
||||
point(59, 15),
|
||||
point(77, 15),
|
||||
point(99, 15),
|
||||
point(139, 15),
|
||||
point(43, 17),
|
||||
point(59, 17),
|
||||
point(77, 17),
|
||||
point(99, 17),
|
||||
point(139, 17),
|
||||
];
|
||||
|
||||
impl Version {
|
||||
pub fn Model1(version_number: u32) -> Result<VersionRef> {
|
||||
@@ -37,11 +73,20 @@ impl Version {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 {
|
||||
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 as usize - 1])
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn DimensionOfVersion(version: u32, is_micro: bool) -> u32 {
|
||||
Self::DimensionOffset(is_micro) + Self::DimensionStep(is_micro) * version
|
||||
}
|
||||
|
||||
pub fn DimensionOffset(is_micro: bool) -> u32 {
|
||||
pub const fn DimensionOffset(is_micro: bool) -> u32 {
|
||||
match is_micro {
|
||||
true => 9,
|
||||
false => 17,
|
||||
@@ -49,7 +94,7 @@ impl Version {
|
||||
// return std::array{17, 9}[isMicro];
|
||||
}
|
||||
|
||||
pub fn DimensionStep(is_micro: bool) -> u32 {
|
||||
pub const fn DimensionStep(is_micro: bool) -> u32 {
|
||||
match is_micro {
|
||||
true => 2,
|
||||
false => 4,
|
||||
@@ -93,22 +138,65 @@ impl Version {
|
||||
Type::const_eq(self.qr_type, Type::Model2)
|
||||
}
|
||||
|
||||
pub const fn isRMQR(&self) -> bool {
|
||||
Type::const_eq(self.qr_type, Type::RectMicro)
|
||||
}
|
||||
|
||||
pub fn HasMicroSize(bitMatrix: &BitMatrix) -> bool {
|
||||
let size = bitMatrix.height();
|
||||
(11..=17).contains(&size) && (size % 2) == 1
|
||||
size == bitMatrix.width() && size >= 11 && size <= 17 && (size % 2) == 1
|
||||
}
|
||||
|
||||
pub fn HasRMQRSize(bitMatrix: &BitMatrix) -> bool {
|
||||
Self::getVersionRMQR(bitMatrix) != -1
|
||||
}
|
||||
|
||||
pub fn HasValidSize(bitMatrix: &BitMatrix) -> bool {
|
||||
let size = bitMatrix.height();
|
||||
Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1)
|
||||
if bitMatrix.width() != size {
|
||||
Self::HasRMQRSize(bitMatrix)
|
||||
} else {
|
||||
Self::HasMicroSize(bitMatrix) || ((21..=177).contains(&size) && (size % 4) == 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn Number(bitMatrix: &BitMatrix) -> u32 {
|
||||
if !Self::HasValidSize(bitMatrix) {
|
||||
if bitMatrix.width() != bitMatrix.height() {
|
||||
Self::getVersionRMQR(bitMatrix) as u32 + 1
|
||||
} else if !Self::HasValidSize(bitMatrix) {
|
||||
0
|
||||
} else {
|
||||
let isMicro = Self::HasMicroSize(bitMatrix);
|
||||
(bitMatrix.height() - Self::DimensionOffset(isMicro)) / Self::DimensionStep(isMicro)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn DimensionOfVersionRMQR(version_number: u32) -> PointI {
|
||||
if version_number < 1 || version_number as usize > dimsVersionRMQR.len() {
|
||||
point(0, 0)
|
||||
} else {
|
||||
dimsVersionRMQR[version_number as usize - 1]
|
||||
}
|
||||
}
|
||||
|
||||
fn getVersionRMQR(bitMatrix: &BitMatrix) -> i32 {
|
||||
let width = bitMatrix.width() as i32;
|
||||
let height = bitMatrix.height() as i32;
|
||||
if width != height
|
||||
&& (width & 1 != 0)
|
||||
&& (height & 1 != 0)
|
||||
&& width >= 27
|
||||
&& width <= 139
|
||||
&& height >= 7
|
||||
&& height <= 17
|
||||
{
|
||||
for i in 0..dimsVersionRMQR.len() {
|
||||
// for (int i = 0; i < Size(dimsVersionRMQR); i++){
|
||||
if width == dimsVersionRMQR[i].x && height == dimsVersionRMQR[i].y {
|
||||
return i as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user