mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 12:22:34 +00:00
feat: ported qr-model1 support from zxing-cpp
This commit is contained in:
@@ -33,7 +33,7 @@ pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool {
|
||||
pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result<VersionRef> {
|
||||
let dimension = bitMatrix.height();
|
||||
|
||||
let mut version = Version::FromDimension(dimension)?;
|
||||
let mut version = Version::FromDimension(dimension, false)?;
|
||||
|
||||
if version.getVersionNumber() < 7 {
|
||||
return Ok(version);
|
||||
@@ -235,6 +235,111 @@ pub fn ReadMQRCodewords(
|
||||
Ok(result.iter().copied().map(|x| x as u8).collect())
|
||||
}
|
||||
|
||||
pub fn ReadQRCodewordsModel1(
|
||||
bitMatrix: &BitMatrix,
|
||||
version: VersionRef,
|
||||
formatInfo: &FormatInformation,
|
||||
) -> Result<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 ReadCodewords(
|
||||
bitMatrix: &BitMatrix,
|
||||
version: VersionRef,
|
||||
@@ -246,6 +351,8 @@ pub fn ReadCodewords(
|
||||
|
||||
if version.isMicroQRCode() {
|
||||
ReadMQRCodewords(bitMatrix, version, formatInfo)
|
||||
} else if formatInfo.isModel1 {
|
||||
ReadQRCodewordsModel1(bitMatrix, version, formatInfo)
|
||||
} else {
|
||||
ReadQRCodewords(bitMatrix, version, formatInfo)
|
||||
}
|
||||
|
||||
@@ -299,6 +299,10 @@ pub fn DecodeBitStream(
|
||||
let mut structuredAppend = StructuredAppendInfo::default();
|
||||
let modeBitLength = Mode::get_codec_mode_bits_length(version);
|
||||
|
||||
if version.isQRCodeModel1() {
|
||||
bits.readBits(4)?; /* Model 1 is leading with 4 0-bits -> drop them */
|
||||
}
|
||||
|
||||
let res = (|| {
|
||||
while !IsEndOfStream(&mut bits, version)? {
|
||||
let mode: Mode = if modeBitLength == 0 {
|
||||
@@ -387,11 +391,21 @@ pub fn DecodeBitStream(
|
||||
.withError(res.err())
|
||||
.withEcLevel(ecLevel.to_string())
|
||||
.withVersionNumber(version.getVersionNumber())
|
||||
.withStructuredAppend(structuredAppend))
|
||||
.withStructuredAppend(structuredAppend)
|
||||
.withIsModel1(version.isQRCodeModel1()))
|
||||
}
|
||||
|
||||
pub fn Decode(bits: &BitMatrix) -> Result<DecoderResult<bool>> {
|
||||
let Ok(pversion) = ReadVersion(bits) else {
|
||||
let isMicroQRCode = bits.height() < 21;
|
||||
let Ok(formatInfo) = ReadFormatInformation(bits, isMicroQRCode) else {
|
||||
return Err(Exceptions::format_with("Invalid format information"));
|
||||
};
|
||||
|
||||
let Ok(pversion) = (if formatInfo.isModel1 {
|
||||
Version::FromDimension(bits.height(), true)
|
||||
} else {
|
||||
ReadVersion(bits)
|
||||
}) else {
|
||||
return Err(Exceptions::format_with("Invalid version"));
|
||||
};
|
||||
let version = pversion;
|
||||
|
||||
@@ -42,7 +42,7 @@ fn SimpleByteMode() {
|
||||
let bytes: Vec<u8> = ba.into();
|
||||
let result = DecodeBitStream(
|
||||
&bytes,
|
||||
Version::FromNumber(1, false).expect("find_version"),
|
||||
Version::FromNumber(1, false, false).expect("find_version"),
|
||||
ErrorCorrectionLevel::M,
|
||||
)
|
||||
.expect("Decode")
|
||||
@@ -64,7 +64,7 @@ fn SimpleSJIS() {
|
||||
let bytes: Vec<u8> = ba.into();
|
||||
let result = DecodeBitStream(
|
||||
&bytes,
|
||||
Version::FromNumber(1, false).unwrap(),
|
||||
Version::FromNumber(1, false, false).unwrap(),
|
||||
ErrorCorrectionLevel::M,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -86,7 +86,7 @@ fn ECI() {
|
||||
let bytes: Vec<u8> = ba.into();
|
||||
let result = DecodeBitStream(
|
||||
&bytes,
|
||||
Version::FromNumber(1, false).unwrap(),
|
||||
Version::FromNumber(1, false, false).unwrap(),
|
||||
ErrorCorrectionLevel::M,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -105,7 +105,7 @@ fn Hanzi() {
|
||||
let bytes: Vec<u8> = ba.into();
|
||||
let result = DecodeBitStream(
|
||||
&bytes,
|
||||
Version::FromNumber(1, false).unwrap(),
|
||||
Version::FromNumber(1, false, false).unwrap(),
|
||||
ErrorCorrectionLevel::M,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -127,7 +127,7 @@ fn HanziLevel1() {
|
||||
|
||||
let result = DecodeBitStream(
|
||||
&bytes,
|
||||
Version::FromNumber(1, false).unwrap(),
|
||||
Version::FromNumber(1, false, false).unwrap(),
|
||||
ErrorCorrectionLevel::M,
|
||||
)
|
||||
.unwrap()
|
||||
@@ -138,7 +138,7 @@ fn HanziLevel1() {
|
||||
|
||||
#[test]
|
||||
fn SymbologyIdentifier() {
|
||||
let version = Version::FromNumber(1, false).unwrap();
|
||||
let version = Version::FromNumber(1, false, false).unwrap();
|
||||
let ecLevel = ErrorCorrectionLevel::M;
|
||||
|
||||
// Plain "ANUM(1) A"
|
||||
|
||||
@@ -63,7 +63,7 @@ fn DecodeWithBitDifference() {
|
||||
MASKED_TEST_FORMAT_INFO2 ^ 0x07,
|
||||
),
|
||||
);
|
||||
assert!(!FormatInformation::DecodeQR(
|
||||
assert!(FormatInformation::DecodeQR(
|
||||
MASKED_TEST_FORMAT_INFO ^ 0x0F,
|
||||
MASKED_TEST_FORMAT_INFO2 ^ 0x0F
|
||||
)
|
||||
|
||||
@@ -27,27 +27,39 @@ fn CharacterCount() {
|
||||
// Spot check a few values
|
||||
assert_eq!(
|
||||
10,
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap())
|
||||
Mode::CharacterCountBits(
|
||||
&Mode::NUMERIC,
|
||||
Version::FromNumber(5, false, false).unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
12,
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap())
|
||||
Mode::CharacterCountBits(
|
||||
&Mode::NUMERIC,
|
||||
Version::FromNumber(26, false, false).unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
14,
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap())
|
||||
Mode::CharacterCountBits(
|
||||
&Mode::NUMERIC,
|
||||
Version::FromNumber(40, false, false).unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
9,
|
||||
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap())
|
||||
Mode::CharacterCountBits(
|
||||
&Mode::ALPHANUMERIC,
|
||||
Version::FromNumber(6, false, false).unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
8,
|
||||
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false, false).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
8,
|
||||
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false, false).unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,26 +122,29 @@ fn MicroCharacterCount() {
|
||||
// Spot check a few values
|
||||
assert_eq!(
|
||||
3,
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true, false).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
4,
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true, false).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
6,
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true, false).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
3,
|
||||
Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap())
|
||||
Mode::CharacterCountBits(
|
||||
&Mode::ALPHANUMERIC,
|
||||
Version::FromNumber(2, true, false).unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
4,
|
||||
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true, false).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
4,
|
||||
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap())
|
||||
Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true, false).unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ fn DoTestVersion(expectedVersion: u32, mask: i32) {
|
||||
|
||||
#[test]
|
||||
fn VersionForNumber() {
|
||||
let version = Version::FromNumber(0, false);
|
||||
let version = Version::FromNumber(0, false, false);
|
||||
assert!(version.is_err(), "There is version with number 0");
|
||||
|
||||
for i in 1..=40 {
|
||||
// for (int i = 1; i <= 40; i++) {
|
||||
CheckVersion(
|
||||
Version::FromNumber(i, false).expect("version number found"),
|
||||
Version::FromNumber(i, false, false).expect("version number found"),
|
||||
i,
|
||||
4 * i + 17,
|
||||
);
|
||||
@@ -43,7 +43,7 @@ fn VersionForNumber() {
|
||||
fn GetProvisionalVersionForDimension() {
|
||||
for i in 1..=40 {
|
||||
// for (int i = 1; i <= 40; i++) {
|
||||
let prov = Version::FromDimension(4 * i + 17)
|
||||
let prov = Version::FromDimension(4 * i + 17, false)
|
||||
.unwrap_or_else(|_| panic!("version should exist for {i}"));
|
||||
// assert_ne!(prov, nullptr);
|
||||
assert_eq!(i, prov.getVersionNumber());
|
||||
@@ -63,13 +63,14 @@ fn DecodeVersionInformation() {
|
||||
|
||||
#[test]
|
||||
fn MicroVersionForNumber() {
|
||||
let version = Version::FromNumber(0, true);
|
||||
let version = Version::FromNumber(0, true, false);
|
||||
assert!(version.is_err(), "There is version with number 0");
|
||||
|
||||
for i in 1..=4 {
|
||||
// for (int i = 1; i <= 4; i++) {
|
||||
CheckVersion(
|
||||
Version::FromNumber(i, true).unwrap_or_else(|_| panic!("version for {i} should exist")),
|
||||
Version::FromNumber(i, true, false)
|
||||
.unwrap_or_else(|_| panic!("version for {i} should exist")),
|
||||
i,
|
||||
2 * i + 9,
|
||||
);
|
||||
@@ -80,7 +81,7 @@ fn MicroVersionForNumber() {
|
||||
fn GetProvisionalMicroVersionForDimension() {
|
||||
for i in 1..=4 {
|
||||
// for (int i = 1; i <= 4; i++) {
|
||||
let prov = Version::FromDimension(2 * i + 9)
|
||||
let prov = Version::FromDimension(2 * i + 9, false)
|
||||
.unwrap_or_else(|_| panic!("version for micro {i} should exist"));
|
||||
// assert_ne!(prov, nullptr);
|
||||
assert_eq!(i, prov.getVersionNumber());
|
||||
@@ -100,7 +101,7 @@ fn FunctionPattern() {
|
||||
};
|
||||
for i in 1..=4 {
|
||||
// for (int i = 1; i <= 4; i++) {
|
||||
let version = Version::FromNumber(i, true).expect("version must be found");
|
||||
let version = Version::FromNumber(i, true, false).expect("version must be found");
|
||||
let functionPattern = version
|
||||
.buildFunctionPattern()
|
||||
.expect("function pattern must be found");
|
||||
|
||||
Reference in New Issue
Block a user