From 724a8e6c28d1d1b8e412ebef3b440005cf1efa76 Mon Sep 17 00:00:00 2001 From: Nakatsubo Seiji Date: Fri, 13 Oct 2023 13:43:47 +0900 Subject: [PATCH 1/9] SupportsQRCodeModel --- core/src/qrcode/QRBitMatrixParser.cpp | 85 ++++++++++++++++-- core/src/qrcode/QRBitMatrixParser.h | 6 +- core/src/qrcode/QRDecoder.cpp | 28 ++++-- core/src/qrcode/QRFormatInformation.cpp | 12 ++- core/src/qrcode/QRFormatInformation.h | 2 +- core/src/qrcode/QRVersion.cpp | 115 +++++++++++++++++++++++- core/src/qrcode/QRVersion.h | 5 +- 7 files changed, 228 insertions(+), 25 deletions(-) diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index b970266c30..824a94cf65 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -31,11 +31,11 @@ static bool hasValidDimension(const BitMatrix& bitMatrix, bool isMicro) return dimension >= 21 && dimension <= 177 && (dimension % 4) == 1; } -const Version* ReadVersion(const BitMatrix& bitMatrix) +const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1) { int dimension = bitMatrix.height(); - const Version* version = Version::FromDimension(dimension); + const Version* version = Version::FromDimension(dimension, isModel1); if (!version || version->versionNumber() < 7) return version; @@ -55,7 +55,7 @@ const Version* ReadVersion(const BitMatrix& bitMatrix) return nullptr; } -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro) +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro, bool& isModel1) { if (!hasValidDimension(bitMatrix, isMicro)) return {}; @@ -93,7 +93,7 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro for (int x = dimension - 8; x < dimension; x++) AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8)); - return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2); + return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2, isModel1); } static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) @@ -135,6 +135,66 @@ static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& vers return result; } +static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) +{ + + ByteArray result; + result.reserve(version.totalCodewords()); + int dimension = bitMatrix.height(); + uint8_t currentByte = 0; + int columns = dimension / 4 + 1 + 2; + for (int j = 0; j < columns; j++) { + if (j <= 1) { // vertical symbols on the right side + int rows = (dimension - 8) / 4; + for (int i = 0; i < rows; i++) { + int x = (dimension - 1) - (j * 2); + int y = (dimension - 1) - (i * 4); + for (int b = 0; b < 8; b++) { + AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 2, y - (b / 2)) + != getBit(bitMatrix, x - b % 2, y - (b / 2), formatInfo.isMirrored)); + } + if (j == 0 && i % 2 == 0 && i > 0 && i < rows - 1) { // extension + continue; + } + result.push_back(std::exchange(currentByte, 0)); + } + } else if (columns - j <= 4) { // vertical symbols on the left side + int rows = (dimension - 16) / 4; + for (int i = 0; i < rows; i++) { + int x = (columns - j - 1) * 2 + 1 + (columns - j == 4 ? 1 : 0); // timing + int y = (dimension - 1) - 8 - (i * 4); + for (int b = 0; b < 8; b++) { + AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 2, y - (b / 2)) + != getBit(bitMatrix, x - b % 2, y - (b / 2), formatInfo.isMirrored)); + } + result.push_back(std::exchange(currentByte, 0)); + } + } else { // horizontal symbols + int rows = dimension / 2; + 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; + } + int x = (dimension - 1) - (2 * 2) - (j - 2) * 4; + int y = (dimension - 1) - (i * 2) - (i >= rows - 3 ? 1 : 0); // timing + for (int b = 0; b < 8; b++) { + AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 4, y - (b / 4)) + != getBit(bitMatrix, x - b % 4, y - (b / 4), formatInfo.isMirrored)); + } + result.push_back(std::exchange(currentByte, 0)); + } + } + } + + result[0] &= 0xf; // ignore corner + if (Size(result) != version.totalCodewords()) + return {}; + return result; +} + static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Version& version, const FormatInformation& formatInfo) { BitMatrix functionPattern = version.buildFunctionPattern(); @@ -181,13 +241,22 @@ static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Vers return result; } -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo,bool isModel1) { if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) return {}; - - return version.isMicroQRCode() ? ReadMQRCodewords(bitMatrix, version, formatInfo) - : ReadQRCodewords(bitMatrix, version, formatInfo); + if (version.isMicroQRCode()) + { + return ReadMQRCodewords(bitMatrix, version, formatInfo); + } + else if (isModel1) + { + return ReadQRCodewordsModel1(bitMatrix, version, formatInfo); + } + else + { + return ReadQRCodewords(bitMatrix, version, formatInfo); + } } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index 0f070c8403..58b8b6a4f6 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -20,20 +20,20 @@ class FormatInformation; * @brief Reads version information from the QR Code. * @return {@link Version} encapsulating the QR Code's version, nullptr if neither location can be parsed */ -const Version* ReadVersion(const BitMatrix& bitMatrix); +const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1); /** * @brief Reads format information from one of its two locations within the QR Code. * @return {@link FormatInformation} encapsulating the QR Code's format info, result is invalid if both format * information locations cannot be parsed as the valid encoding of format information */ -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro); +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro, bool& isModel1); /** * @brief Reads the codewords from the BitMatrix. * @return bytes encoded within the QR Code or empty array if the exact number of bytes expected is not read */ -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo); +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo , bool isModel1); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 905f0a5c8d..c50980d2a0 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -228,7 +228,7 @@ bool IsEndOfStream(const BitSource& bits, const Version& version) *

See ISO 18004:2006, 6.4.3 - 6.4.7

*/ ZXING_EXPORT_TEST_ONLY -DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) +DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, bool isModel1) { BitSource bits(bytes); Content result; @@ -237,6 +237,11 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); + if (isModel1) + { + bits.readBits(4); // model1 + } + try { while(!IsEndOfStream(bits, version)) { @@ -316,17 +321,28 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo DecoderResult Decode(const BitMatrix& bits) { - const Version* pversion = ReadVersion(bits); + bool isModel1 = false; + + const Version* pversion = ReadVersion(bits, isModel1); if (!pversion) return FormatError("Invalid version"); - const Version& version = *pversion; + const Version& preversion = *pversion; - auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode()); + auto formatInfo = ReadFormatInformation(bits, preversion.isMicroQRCode(), isModel1); if (!formatInfo.isValid()) return FormatError("Invalid format information"); + if (isModel1) + { + pversion = nullptr; + pversion = ReadVersion(bits, isModel1); + if (!pversion) + return FormatError("Invalid version"); + } + const Version& version = *pversion; + // Read codewords - ByteArray codewords = ReadCodewords(bits, version, formatInfo); + ByteArray codewords = ReadCodewords(bits, version, formatInfo, isModel1); if (codewords.empty()) return FormatError("Failed to read codewords"); @@ -354,7 +370,7 @@ DecoderResult Decode(const BitMatrix& bits) } // Decode the contents of that stream of bytes - return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel).setIsMirrored(formatInfo.isMirrored); + return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, isModel1).setIsMirrored(formatInfo.isMirrored); } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index 9195a7fa65..326f753a0c 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -15,6 +15,8 @@ namespace ZXing::QRCode { static const int FORMAT_INFO_MASK_QR = 0x5412; +static const int FORMAT_INFO_MASK_QR_MODEL1 = 0x2825; + /** * See ISO 18004:2006, Annex C, Table C.1 */ @@ -117,13 +119,21 @@ static FormatInformation FindBestFormatInfo(int mask, const std::array> 1) & 0b111111110000000) | (formatInfoBits2 & 0b1111111)); formatInfoBits2 = ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); auto fi = FindBestFormatInfo(FORMAT_INFO_MASK_QR, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); + auto fi_model1 = FindBestFormatInfo(FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1, FORMAT_INFO_DECODE_LOOKUP, + {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); + + if (fi_model1.hammingDistance < fi.hammingDistance) + { + isModel1 = true; + fi = fi_model1; + } // Use bits 3/4 for error correction, and 0-2 for mask. fi.ecLevel = ECLevelFromBits((fi.index >> 3) & 0x03); diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index f5ebc096df..45d01b1fa1 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -25,7 +25,7 @@ class FormatInformation FormatInformation() = default; - static FormatInformation DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2); + static FormatInformation DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2, bool& isModel1); static FormatInformation DecodeMQR(uint32_t formatInfoBits); // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index 2f202b654d..3489483ec5 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -292,6 +292,101 @@ const Version* Version::AllMicroVersions() return allVersions; } +const Version* Version::AllModel1Versions() +{ + /** + * See ISO 18004:2000 M.4.2 Table M.2 + * See ISO 18004:2000 M.5 Table M.4 + */ + static const Version allVersions[] = { + {1, {}, { + 7 , 1, 19, 0, 0, + 10, 1, 16, 0, 0, + 13, 1, 13, 0, 0, + 17, 1, 9 , 0, 0 + }}, + {2, {}, { + 10, 1, 36, 0, 0, + 16, 1, 30, 0, 0, + 22, 1, 24, 0, 0, + 30, 1, 16, 0, 0, + }}, + {3, {}, { + 15, 1, 57, 0, 0, + 28, 1, 44, 0, 0, + 36, 1, 36, 0, 0, + 48, 1, 24, 0, 0, + }}, + {4, {}, { + 20, 1, 80, 0, 0, + 40, 1, 60, 0, 0, + 50, 1, 50, 0, 0, + 66, 1, 34, 0, 0, + }}, + {5, {}, { + 26, 1, 108, 0, 0, + 52, 1, 82 , 0, 0, + 66, 1, 68 , 0, 0, + 88, 2, 46 , 0, 0, + }}, + {6, {}, { + 34 , 1, 136, 0, 0, + 63 , 2, 106, 0, 0, + 84 , 2, 86 , 0, 0, + 112, 2, 58 , 0, 0, + }}, + {7, {}, { + 42 , 1, 170, 0, 0, + 80 , 2, 132, 0, 0, + 104, 2, 108, 0, 0, + 138, 3, 72 , 0, 0, + }}, + {8, {}, { + 48 , 2, 208, 0, 0, + 96 , 2, 160, 0, 0, + 128, 2, 128, 0, 0, + 168, 3, 87 , 0, 0, + }}, + {9, {}, { + 60 , 2, 246, 0, 0, + 120, 2, 186, 0, 0, + 150, 3, 156, 0, 0, + 204, 3, 102, 0, 0, + }}, + {10, {}, { + 68 , 2, 290, 0, 0, + 136, 2, 222, 0, 0, + 174, 3, 183, 0, 0, + 232, 4, 124, 0, 0, + }}, + {11, {}, { + 80 , 2, 336, 0, 0, + 160, 4, 256, 0, 0, + 208, 4, 208, 0, 0, + 270, 5, 145, 0, 0, + }}, + {12, {}, { + 92 , 2, 384, 0, 0, + 184, 4, 292, 0, 0, + 232, 4, 244, 0, 0, + 310, 5, 165, 0, 0, + }}, + {13, {}, { + 108, 3, 432, 0, 0, + 208, 4, 332, 0, 0, + 264, 4, 276, 0, 0, + 348, 6, 192, 0, 0, + }}, + {14, {}, { + 120, 3, 489, 0, 0, + 240, 4, 368, 0, 0, + 300, 5, 310, 0, 0, + 396, 6, 210, 0, 0, + }}, + }; + return allVersions; +} + Version::Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array& ecBlocks) : _versionNumber(versionNumber), _alignmentPatternCenters(alignmentPatternCenters), _ecBlocks(ecBlocks), _isMicro(false) { @@ -304,23 +399,35 @@ Version::Version(int versionNumber, const std::array& ecBlocks) _totalCodewords = ecBlocks[0].totalDataCodewords(); } -const Version* Version::FromNumber(int versionNumber, bool isMicro) +const Version* Version::FromNumber(int versionNumber, bool isMicro, bool isModel1) { if (versionNumber < 1 || versionNumber > (isMicro ? 4 : 40)) { //throw std::invalid_argument("Version should be in range [1-40]."); return nullptr; } - return &(isMicro ? AllMicroVersions() : AllVersions())[versionNumber - 1]; + + if (isMicro) + { + return &AllMicroVersions()[versionNumber - 1]; + } + else if (isModel1) + { + return &AllModel1Versions()[versionNumber - 1]; + } + else + { + return &AllVersions()[versionNumber - 1]; + } } -const Version* Version::FromDimension(int dimension) +const Version* Version::FromDimension(int dimension, bool isModel1) { bool isMicro = dimension < 21; if (dimension % DimensionStep(isMicro) != 1) { //throw std::invalid_argument("Unexpected dimension"); return nullptr; } - return FromNumber((dimension - DimensionOffset(isMicro)) / DimensionStep(isMicro), isMicro); + return FromNumber((dimension - DimensionOffset(isMicro)) / DimensionStep(isMicro), isMicro, isModel1); } const Version* Version::DecodeVersionInformation(int versionBitsA, int versionBitsB) diff --git a/core/src/qrcode/QRVersion.h b/core/src/qrcode/QRVersion.h index 0b03270011..7c2b48a993 100644 --- a/core/src/qrcode/QRVersion.h +++ b/core/src/qrcode/QRVersion.h @@ -54,9 +54,9 @@ class Version * @param dimension dimension in modules * @return Version for a QR Code of that dimension */ - static const Version* FromDimension(int dimension); + static const Version* FromDimension(int dimension ,bool isModel1); - static const Version* FromNumber(int versionNumber, bool isMicro = false); + static const Version* FromNumber(int versionNumber, bool isMicro = false, bool isModel1 = false); static const Version* DecodeVersionInformation(int versionBitsA, int versionBitsB = 0); @@ -71,6 +71,7 @@ class Version Version(int versionNumber, const std::array& ecBlocks); static const Version* AllVersions(); static const Version* AllMicroVersions(); + static const Version* AllModel1Versions(); }; } // QRCode From ec36b2ddf804cc0f2db2a5b90929037a15a9a60f Mon Sep 17 00:00:00 2001 From: Nakatsubo Seiji Date: Thu, 19 Oct 2023 10:55:24 +0900 Subject: [PATCH 2/9] ModifiedTheCode --- core/src/qrcode/QRBitMatrixParser.cpp | 33 +++++++++++------------- core/src/qrcode/QRBitMatrixParser.h | 4 +-- core/src/qrcode/QRDecoder.cpp | 14 +++++----- core/src/qrcode/QRFormatInformation.cpp | 7 +++-- core/src/qrcode/QRFormatInformation.h | 3 ++- core/src/qrcode/QRVersion.cpp | 9 +++---- test/blackbox/BlackboxTestRunner.cpp | 12 ++++----- test/samples/qrcode-2/qr-model-1.png | Bin 0 -> 3786 bytes test/samples/qrcode-2/qr-model-1.txt | 1 + 9 files changed, 39 insertions(+), 44 deletions(-) create mode 100644 test/samples/qrcode-2/qr-model-1.png create mode 100644 test/samples/qrcode-2/qr-model-1.txt diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index 824a94cf65..aa6daed002 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -55,7 +55,7 @@ const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1) return nullptr; } -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro, bool& isModel1) +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro) { if (!hasValidDimension(bitMatrix, isMicro)) return {}; @@ -93,7 +93,7 @@ FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro for (int x = dimension - 8; x < dimension; x++) AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8)); - return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2, isModel1); + return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2); } static ByteArray ReadQRCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) @@ -141,33 +141,34 @@ static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version ByteArray result; result.reserve(version.totalCodewords()); int dimension = bitMatrix.height(); - uint8_t currentByte = 0; int columns = dimension / 4 + 1 + 2; for (int j = 0; j < columns; j++) { if (j <= 1) { // vertical symbols on the right side int rows = (dimension - 8) / 4; for (int i = 0; i < rows; i++) { + if (j == 0 && i % 2 == 0 && i > 0 && i < rows - 1) { // extension + continue; + } int x = (dimension - 1) - (j * 2); int y = (dimension - 1) - (i * 4); + uint8_t currentByte = 0; for (int b = 0; b < 8; b++) { AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 2, y - (b / 2)) != getBit(bitMatrix, x - b % 2, y - (b / 2), formatInfo.isMirrored)); } - if (j == 0 && i % 2 == 0 && i > 0 && i < rows - 1) { // extension - continue; - } - result.push_back(std::exchange(currentByte, 0)); + result.push_back(currentByte); } } else if (columns - j <= 4) { // vertical symbols on the left side int rows = (dimension - 16) / 4; for (int i = 0; i < rows; i++) { int x = (columns - j - 1) * 2 + 1 + (columns - j == 4 ? 1 : 0); // timing int y = (dimension - 1) - 8 - (i * 4); + uint8_t currentByte = 0; for (int b = 0; b < 8; b++) { AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 2, y - (b / 2)) != getBit(bitMatrix, x - b % 2, y - (b / 2), formatInfo.isMirrored)); } - result.push_back(std::exchange(currentByte, 0)); + result.push_back(currentByte); } } else { // horizontal symbols int rows = dimension / 2; @@ -180,11 +181,12 @@ static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version } int x = (dimension - 1) - (2 * 2) - (j - 2) * 4; int y = (dimension - 1) - (i * 2) - (i >= rows - 3 ? 1 : 0); // timing + uint8_t currentByte = 0; for (int b = 0; b < 8; b++) { AppendBit(currentByte, GetDataMaskBit(formatInfo.dataMask, x - b % 4, y - (b / 4)) != getBit(bitMatrix, x - b % 4, y - (b / 4), formatInfo.isMirrored)); } - result.push_back(std::exchange(currentByte, 0)); + result.push_back(currentByte); } } } @@ -241,20 +243,15 @@ static ByteArray ReadMQRCodewords(const BitMatrix& bitMatrix, const QRCode::Vers return result; } -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo,bool isModel1) +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo) { if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) return {}; - if (version.isMicroQRCode()) - { + if (version.isMicroQRCode()) { return ReadMQRCodewords(bitMatrix, version, formatInfo); - } - else if (isModel1) - { + } else if (formatInfo.isModel1) { return ReadQRCodewordsModel1(bitMatrix, version, formatInfo); - } - else - { + } else { return ReadQRCodewords(bitMatrix, version, formatInfo); } } diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index 58b8b6a4f6..f2c1b23f06 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -27,13 +27,13 @@ const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1); * @return {@link FormatInformation} encapsulating the QR Code's format info, result is invalid if both format * information locations cannot be parsed as the valid encoding of format information */ -FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro, bool& isModel1); +FormatInformation ReadFormatInformation(const BitMatrix& bitMatrix, bool isMicro); /** * @brief Reads the codewords from the BitMatrix. * @return bytes encoded within the QR Code or empty array if the exact number of bytes expected is not read */ -ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo , bool isModel1); +ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, const FormatInformation& formatInfo); } // QRCode } // ZXing diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index c50980d2a0..71b97b69bc 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -321,28 +321,27 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo DecoderResult Decode(const BitMatrix& bits) { - bool isModel1 = false; - const Version* pversion = ReadVersion(bits, isModel1); + const Version* pversion = ReadVersion(bits, false); if (!pversion) return FormatError("Invalid version"); const Version& preversion = *pversion; - auto formatInfo = ReadFormatInformation(bits, preversion.isMicroQRCode(), isModel1); + auto formatInfo = ReadFormatInformation(bits, preversion.isMicroQRCode()); if (!formatInfo.isValid()) return FormatError("Invalid format information"); - if (isModel1) + if (formatInfo.isModel1) { pversion = nullptr; - pversion = ReadVersion(bits, isModel1); + pversion = ReadVersion(bits, formatInfo.isModel1); if (!pversion) return FormatError("Invalid version"); } const Version& version = *pversion; // Read codewords - ByteArray codewords = ReadCodewords(bits, version, formatInfo, isModel1); + ByteArray codewords = ReadCodewords(bits, version, formatInfo); if (codewords.empty()) return FormatError("Failed to read codewords"); @@ -370,7 +369,8 @@ DecoderResult Decode(const BitMatrix& bits) } // Decode the contents of that stream of bytes - return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, isModel1).setIsMirrored(formatInfo.isMirrored); + return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, formatInfo.isModel1) + .setIsMirrored(formatInfo.isMirrored); } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index 326f753a0c..5c819dbb0a 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -119,7 +119,7 @@ static FormatInformation FindBestFormatInfo(int mask, const std::array> 1) & 0b111111110000000) | (formatInfoBits2 & 0b1111111)); @@ -129,9 +129,8 @@ FormatInformation FormatInformation::DecodeQR(uint32_t formatInfoBits1, uint32_t auto fi_model1 = FindBestFormatInfo(FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); - if (fi_model1.hammingDistance < fi.hammingDistance) - { - isModel1 = true; + if (fi_model1.hammingDistance < fi.hammingDistance){ + fi_model1.isModel1 = true; fi = fi_model1; } diff --git a/core/src/qrcode/QRFormatInformation.h b/core/src/qrcode/QRFormatInformation.h index 45d01b1fa1..ec2d1ce322 100644 --- a/core/src/qrcode/QRFormatInformation.h +++ b/core/src/qrcode/QRFormatInformation.h @@ -18,6 +18,7 @@ class FormatInformation uint8_t index = 255; uint8_t hammingDistance = 255; bool isMirrored = false; + bool isModel1 = false; uint8_t dataMask = 0; uint8_t microVersion = 0; uint8_t bitsIndex = 255; @@ -25,7 +26,7 @@ class FormatInformation FormatInformation() = default; - static FormatInformation DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2, bool& isModel1); + static FormatInformation DecodeQR(uint32_t formatInfoBits1, uint32_t formatInfoBits2); static FormatInformation DecodeMQR(uint32_t formatInfoBits); // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index 3489483ec5..0ad569bbc4 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -406,16 +406,13 @@ const Version* Version::FromNumber(int versionNumber, bool isMicro, bool isModel return nullptr; } - if (isMicro) - { + if (isMicro){ return &AllMicroVersions()[versionNumber - 1]; } - else if (isModel1) - { + else if (isModel1){ return &AllModel1Versions()[versionNumber - 1]; } - else - { + else{ return &AllVersions()[versionNumber - 1]; } } diff --git a/test/blackbox/BlackboxTestRunner.cpp b/test/blackbox/BlackboxTestRunner.cpp index af2e247d80..e98f22a31b 100644 --- a/test/blackbox/BlackboxTestRunner.cpp +++ b/test/blackbox/BlackboxTestRunner.cpp @@ -562,12 +562,12 @@ int runBlackBoxTests(const fs::path& testPathPrefix, const std::set { 16, 16, 270 }, }); - runTests("qrcode-2", "QRCode", 49, { - { 45, 47, 0 }, - { 45, 47, 90 }, - { 45, 47, 180 }, - { 45, 47, 270 }, - { 21, 1, pure }, // the misread is the 'outer' symbol in 16.png + runTests("qrcode-2", "QRCode", 50, { + { 46, 48, 0 }, + { 46, 48, 90 }, + { 46, 48, 180 }, + { 46, 48, 270 }, + { 22, 1, pure }, // the misread is the 'outer' symbol in 16.png }); runTests("qrcode-3", "QRCode", 28, { diff --git a/test/samples/qrcode-2/qr-model-1.png b/test/samples/qrcode-2/qr-model-1.png new file mode 100644 index 0000000000000000000000000000000000000000..da58f398e2885e048a064395cf7fc9cb3a4f6153 GIT binary patch literal 3786 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 Ang9R* literal 0 HcmV?d00001 diff --git a/test/samples/qrcode-2/qr-model-1.txt b/test/samples/qrcode-2/qr-model-1.txt new file mode 100644 index 0000000000..a6bce8d223 --- /dev/null +++ b/test/samples/qrcode-2/qr-model-1.txt @@ -0,0 +1 @@ +QR Code Model 1 \ No newline at end of file From 47de5e8d9b4c15847395a9e1b8c842a50a16c57d Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 19 Oct 2023 09:09:35 +0200 Subject: [PATCH 3/9] QRCode: fix build failure of unit tests --- core/src/qrcode/QRBitMatrixParser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index f2c1b23f06..42b9984fef 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -20,7 +20,7 @@ class FormatInformation; * @brief Reads version information from the QR Code. * @return {@link Version} encapsulating the QR Code's version, nullptr if neither location can be parsed */ -const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1); +const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1 = false); /** * @brief Reads format information from one of its two locations within the QR Code. From 42e1e2a1c8cd5a1fc9b7c48df705f598b74c5fa9 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 19 Oct 2023 09:17:03 +0200 Subject: [PATCH 4/9] QRCode: another unit test build fix... --- core/src/qrcode/QRVersion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/qrcode/QRVersion.h b/core/src/qrcode/QRVersion.h index 7c2b48a993..33d886c733 100644 --- a/core/src/qrcode/QRVersion.h +++ b/core/src/qrcode/QRVersion.h @@ -54,7 +54,7 @@ class Version * @param dimension dimension in modules * @return Version for a QR Code of that dimension */ - static const Version* FromDimension(int dimension ,bool isModel1); + static const Version* FromDimension(int dimension, bool isModel1 = false); static const Version* FromNumber(int versionNumber, bool isMicro = false, bool isModel1 = false); From 944f92ff8cb7baf9542b8e67dc0a20c50ce3094f Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 19 Oct 2023 09:59:35 +0200 Subject: [PATCH 5/9] QRCode: and yet another build fix... arghh --- core/src/qrcode/QRDecoder.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 71b97b69bc..d607602e5a 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -227,7 +227,6 @@ bool IsEndOfStream(const BitSource& bits, const Version& version) * *

See ISO 18004:2006, 6.4.3 - 6.4.7

*/ -ZXING_EXPORT_TEST_ONLY DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, bool isModel1) { BitSource bits(bytes); @@ -319,6 +318,13 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo .setStructuredAppend(structuredAppend); } +ZXING_EXPORT_TEST_ONLY +DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) +{ + //TODO: cleanup + return DecodeBitStream(bytes, version, ecLevel, false); +} + DecoderResult Decode(const BitMatrix& bits) { From af1345fd27e733a10dcf96c2ed51c1d498ac54e9 Mon Sep 17 00:00:00 2001 From: axxel Date: Thu, 19 Oct 2023 10:03:38 +0200 Subject: [PATCH 6/9] QRCode: no comment :-( --- core/src/qrcode/QRDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index d607602e5a..c3c6449354 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -322,7 +322,7 @@ ZXING_EXPORT_TEST_ONLY DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) { //TODO: cleanup - return DecodeBitStream(bytes, version, ecLevel, false); + return DecodeBitStream(std::move(bytes), version, ecLevel, false); } DecoderResult Decode(const BitMatrix& bits) From cd0c34b4876f0bd41fc2f1ea3144d246f7ba368c Mon Sep 17 00:00:00 2001 From: axxel Date: Fri, 20 Oct 2023 23:54:42 +0200 Subject: [PATCH 7/9] fix failing test and replace sample image --- core/src/qrcode/QRFormatInformation.cpp | 14 +++++++------- test/samples/qrcode-2/qr-model-1.png | Bin 3786 -> 230 bytes test/unit/qrcode/QRFormatInformationTest.cpp | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index 5c819dbb0a..7ceb7809bd 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -95,13 +95,12 @@ static uint32_t MirrorBits(uint32_t bits) return BitHacks::Reverse(bits) >> 17; } -static FormatInformation FindBestFormatInfo(int mask, const std::array, 32> lookup, +static FormatInformation FindBestFormatInfo(const std::vector& masks, const std::array, 32> lookup, const std::vector& bits) { FormatInformation fi; - // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. - for (auto mask : {0, mask}) + for (auto mask : masks) for (int bitsIndex = 0; bitsIndex < Size(bits); ++bitsIndex) for (const auto& [pattern, index] : lookup) { // Find the int in lookup with fewest bits differing @@ -124,10 +123,11 @@ FormatInformation FormatInformation::DecodeQR(uint32_t formatInfoBits1, uint32_t // maks out the 'Dark Module' for mirrored and non-mirrored case (see Figure 25 in ISO/IEC 18004:2015) uint32_t mirroredFormatInfoBits2 = MirrorBits(((formatInfoBits2 >> 1) & 0b111111110000000) | (formatInfoBits2 & 0b1111111)); formatInfoBits2 = ((formatInfoBits2 >> 1) & 0b111111100000000) | (formatInfoBits2 & 0b11111111); - auto fi = FindBestFormatInfo(FORMAT_INFO_MASK_QR, FORMAT_INFO_DECODE_LOOKUP, - {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); - auto fi_model1 = FindBestFormatInfo(FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1, FORMAT_INFO_DECODE_LOOKUP, + // Some QR codes apparently do not apply the XOR mask. Try without and with additional masking. + auto fi = FindBestFormatInfo({0, FORMAT_INFO_MASK_QR}, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); + auto fi_model1 = FindBestFormatInfo({FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1}, FORMAT_INFO_DECODE_LOOKUP, + {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); if (fi_model1.hammingDistance < fi.hammingDistance){ fi_model1.isModel1 = true; @@ -148,7 +148,7 @@ FormatInformation FormatInformation::DecodeQR(uint32_t formatInfoBits1, uint32_t FormatInformation FormatInformation::DecodeMQR(uint32_t formatInfoBits) { // We don't use the additional masking (with 0x4445) to work around potentially non complying MicroQRCode encoders - auto fi = FindBestFormatInfo(0, FORMAT_INFO_DECODE_LOOKUP_MICRO, {formatInfoBits, MirrorBits(formatInfoBits)}); + auto fi = FindBestFormatInfo({0}, FORMAT_INFO_DECODE_LOOKUP_MICRO, {formatInfoBits, MirrorBits(formatInfoBits)}); constexpr uint8_t BITS_TO_VERSION[] = {1, 2, 2, 3, 3, 4, 4, 4}; diff --git a/test/samples/qrcode-2/qr-model-1.png b/test/samples/qrcode-2/qr-model-1.png index da58f398e2885e048a064395cf7fc9cb3a4f6153..4b199902d0bae9a8fd98dbe698d570069d80ac40 100644 GIT binary patch literal 230 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 Ang9R* diff --git a/test/unit/qrcode/QRFormatInformationTest.cpp b/test/unit/qrcode/QRFormatInformationTest.cpp index bbbef8a0ed..fb933e0ab5 100644 --- a/test/unit/qrcode/QRFormatInformationTest.cpp +++ b/test/unit/qrcode/QRFormatInformationTest.cpp @@ -15,7 +15,6 @@ static const int MASKED_TEST_FORMAT_INFO = 0x2BED; static const int MASKED_TEST_FORMAT_INFO2 = ((0x2BED << 1) & 0b1111111000000000) | 0b100000000 | (0x2BED & 0b11111111); // insert the 'Dark Module' static const int UNMASKED_TEST_FORMAT_INFO = MASKED_TEST_FORMAT_INFO ^ 0x5412; static const int MICRO_MASKED_TEST_FORMAT_INFO = 0x3BBA; -static const int MICRO_UNMASKED_TEST_FORMAT_INFO = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445; static void DoFormatInformationTest(const int formatInfo, const uint8_t expectedMask, const ErrorCorrectionLevel& expectedECL) { @@ -43,7 +42,9 @@ TEST(QRFormatInformationTest, DecodeWithBitDifference) EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x01, MASKED_TEST_FORMAT_INFO2 ^ 0x01)); EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO2 ^ 0x03)); EXPECT_EQ(expected, FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x07, MASKED_TEST_FORMAT_INFO2 ^ 0x07)); - EXPECT_TRUE(!FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO2 ^ 0x0F).isValid()); + auto unexpected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO2 ^ 0x0F); + EXPECT_FALSE(expected == unexpected); + EXPECT_FALSE(unexpected.isValid() && !unexpected.isModel1); } TEST(QRFormatInformationTest, DecodeWithMisread) @@ -65,6 +66,7 @@ TEST(QRFormatInformationTest, DecodeMicro) DoFormatInformationTest(MICRO_MASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality); // where the code forgot the mask! +// static const int MICRO_UNMASKED_TEST_FORMAT_INFO = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445; // DoFormatInformationTest(MICRO_UNMASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality); } From 65dca9f17308d83f174cd7b95262983538a1ce73 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 Oct 2023 00:32:00 +0200 Subject: [PATCH 8/9] code style cleanup and version parsing logic cleanup --- core/src/qrcode/QRBitMatrixParser.cpp | 17 +++++++---------- core/src/qrcode/QRDecoder.cpp | 18 +++++------------- core/src/qrcode/QRFormatInformation.cpp | 2 +- core/src/qrcode/QRVersion.cpp | 12 ++---------- 4 files changed, 15 insertions(+), 34 deletions(-) diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index aa6daed002..6c7a8ae523 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -146,9 +146,8 @@ static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version if (j <= 1) { // vertical symbols on the right side int rows = (dimension - 8) / 4; for (int i = 0; i < rows; i++) { - if (j == 0 && i % 2 == 0 && i > 0 && i < rows - 1) { // extension + if (j == 0 && i % 2 == 0 && i > 0 && i < rows - 1) // extension continue; - } int x = (dimension - 1) - (j * 2); int y = (dimension - 1) - (i * 4); uint8_t currentByte = 0; @@ -173,12 +172,10 @@ static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version } else { // horizontal symbols int rows = dimension / 2; for (int i = 0; i < rows; i++) { - if (j == 2 && i >= rows - 4) { // alignment & finder + if (j == 2 && i >= rows - 4) // alignment & finder continue; - } - if (i == 0 && j % 2 == 1 && j + 1 != columns - 4) { // extension + if (i == 0 && j % 2 == 1 && j + 1 != columns - 4) // extension continue; - } int x = (dimension - 1) - (2 * 2) - (j - 2) * 4; int y = (dimension - 1) - (i * 2) - (i >= rows - 3 ? 1 : 0); // timing uint8_t currentByte = 0; @@ -194,6 +191,7 @@ static ByteArray ReadQRCodewordsModel1(const BitMatrix& bitMatrix, const Version result[0] &= 0xf; // ignore corner if (Size(result) != version.totalCodewords()) return {}; + return result; } @@ -247,13 +245,12 @@ ByteArray ReadCodewords(const BitMatrix& bitMatrix, const Version& version, cons { if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) return {}; - if (version.isMicroQRCode()) { + if (version.isMicroQRCode()) return ReadMQRCodewords(bitMatrix, version, formatInfo); - } else if (formatInfo.isModel1) { + else if (formatInfo.isModel1) return ReadQRCodewordsModel1(bitMatrix, version, formatInfo); - } else { + else return ReadQRCodewords(bitMatrix, version, formatInfo); - } } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index c3c6449354..2479992d02 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -327,23 +327,15 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo DecoderResult Decode(const BitMatrix& bits) { + bool isMicroQRCode = bits.height() < 21; + auto formatInfo = ReadFormatInformation(bits, isMicroQRCode); + if (!formatInfo.isValid()) + return FormatError("Invalid format information"); - const Version* pversion = ReadVersion(bits, false); + const Version* pversion = formatInfo.isModel1 ? Version::FromDimension(bits.height(), true) : ReadVersion(bits, false); if (!pversion) return FormatError("Invalid version"); - const Version& preversion = *pversion; - auto formatInfo = ReadFormatInformation(bits, preversion.isMicroQRCode()); - if (!formatInfo.isValid()) - return FormatError("Invalid format information"); - - if (formatInfo.isModel1) - { - pversion = nullptr; - pversion = ReadVersion(bits, formatInfo.isModel1); - if (!pversion) - return FormatError("Invalid version"); - } const Version& version = *pversion; // Read codewords diff --git a/core/src/qrcode/QRFormatInformation.cpp b/core/src/qrcode/QRFormatInformation.cpp index 7ceb7809bd..c5502d1553 100644 --- a/core/src/qrcode/QRFormatInformation.cpp +++ b/core/src/qrcode/QRFormatInformation.cpp @@ -129,7 +129,7 @@ FormatInformation FormatInformation::DecodeQR(uint32_t formatInfoBits1, uint32_t auto fi_model1 = FindBestFormatInfo({FORMAT_INFO_MASK_QR ^ FORMAT_INFO_MASK_QR_MODEL1}, FORMAT_INFO_DECODE_LOOKUP, {formatInfoBits1, formatInfoBits2, MirrorBits(formatInfoBits1), mirroredFormatInfoBits2}); - if (fi_model1.hammingDistance < fi.hammingDistance){ + if (fi_model1.hammingDistance < fi.hammingDistance) { fi_model1.isModel1 = true; fi = fi_model1; } diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index 0ad569bbc4..bc86b2e185 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -401,20 +401,12 @@ Version::Version(int versionNumber, const std::array& ecBlocks) const Version* Version::FromNumber(int versionNumber, bool isMicro, bool isModel1) { - if (versionNumber < 1 || versionNumber > (isMicro ? 4 : 40)) { + if (versionNumber < 1 || versionNumber > (isMicro ? 4 : (isModel1 ? 14 : 40))) { //throw std::invalid_argument("Version should be in range [1-40]."); return nullptr; } - if (isMicro){ - return &AllMicroVersions()[versionNumber - 1]; - } - else if (isModel1){ - return &AllModel1Versions()[versionNumber - 1]; - } - else{ - return &AllVersions()[versionNumber - 1]; - } + return &(isMicro ? AllMicroVersions() : (isModel1 ? AllModel1Versions() : AllVersions()))[versionNumber - 1]; } const Version* Version::FromDimension(int dimension, bool isModel1) From 6f32629d868c4e9dcd9ce8c4368ebc7b7135b447 Mon Sep 17 00:00:00 2001 From: axxel Date: Sat, 21 Oct 2023 01:00:49 +0200 Subject: [PATCH 9/9] further minimize patch size + code cosmetic --- core/src/qrcode/QRBitMatrixParser.cpp | 4 +- core/src/qrcode/QRBitMatrixParser.h | 2 +- core/src/qrcode/QRDecoder.cpp | 23 +++------- core/src/qrcode/QRVersion.cpp | 61 +++++++++++++++------------ core/src/qrcode/QRVersion.h | 2 + 5 files changed, 45 insertions(+), 47 deletions(-) diff --git a/core/src/qrcode/QRBitMatrixParser.cpp b/core/src/qrcode/QRBitMatrixParser.cpp index 6c7a8ae523..c554242226 100644 --- a/core/src/qrcode/QRBitMatrixParser.cpp +++ b/core/src/qrcode/QRBitMatrixParser.cpp @@ -31,11 +31,11 @@ static bool hasValidDimension(const BitMatrix& bitMatrix, bool isMicro) return dimension >= 21 && dimension <= 177 && (dimension % 4) == 1; } -const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1) +const Version* ReadVersion(const BitMatrix& bitMatrix) { int dimension = bitMatrix.height(); - const Version* version = Version::FromDimension(dimension, isModel1); + const Version* version = Version::FromDimension(dimension); if (!version || version->versionNumber() < 7) return version; diff --git a/core/src/qrcode/QRBitMatrixParser.h b/core/src/qrcode/QRBitMatrixParser.h index 42b9984fef..0f070c8403 100644 --- a/core/src/qrcode/QRBitMatrixParser.h +++ b/core/src/qrcode/QRBitMatrixParser.h @@ -20,7 +20,7 @@ class FormatInformation; * @brief Reads version information from the QR Code. * @return {@link Version} encapsulating the QR Code's version, nullptr if neither location can be parsed */ -const Version* ReadVersion(const BitMatrix& bitMatrix, bool isModel1 = false); +const Version* ReadVersion(const BitMatrix& bitMatrix); /** * @brief Reads format information from one of its two locations within the QR Code. diff --git a/core/src/qrcode/QRDecoder.cpp b/core/src/qrcode/QRDecoder.cpp index 2479992d02..2190618ca5 100644 --- a/core/src/qrcode/QRDecoder.cpp +++ b/core/src/qrcode/QRDecoder.cpp @@ -227,7 +227,8 @@ bool IsEndOfStream(const BitSource& bits, const Version& version) * *

See ISO 18004:2006, 6.4.3 - 6.4.7

*/ -DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel, bool isModel1) +ZXING_EXPORT_TEST_ONLY +DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) { BitSource bits(bytes); Content result; @@ -236,11 +237,9 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo StructuredAppendInfo structuredAppend; const int modeBitLength = CodecModeBitsLength(version); - if (isModel1) - { - bits.readBits(4); // model1 - } - + if (version.isQRCodeModel1()) + bits.readBits(4); // Model 1 is leading with 4 0-bits -> drop them + try { while(!IsEndOfStream(bits, version)) { @@ -318,13 +317,6 @@ DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCo .setStructuredAppend(structuredAppend); } -ZXING_EXPORT_TEST_ONLY -DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) -{ - //TODO: cleanup - return DecodeBitStream(std::move(bytes), version, ecLevel, false); -} - DecoderResult Decode(const BitMatrix& bits) { bool isMicroQRCode = bits.height() < 21; @@ -332,7 +324,7 @@ DecoderResult Decode(const BitMatrix& bits) if (!formatInfo.isValid()) return FormatError("Invalid format information"); - const Version* pversion = formatInfo.isModel1 ? Version::FromDimension(bits.height(), true) : ReadVersion(bits, false); + const Version* pversion = formatInfo.isModel1 ? Version::FromDimension(bits.height(), true) : ReadVersion(bits); if (!pversion) return FormatError("Invalid version"); @@ -367,8 +359,7 @@ DecoderResult Decode(const BitMatrix& bits) } // Decode the contents of that stream of bytes - return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel, formatInfo.isModel1) - .setIsMirrored(formatInfo.isMirrored); + return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel).setIsMirrored(formatInfo.isMirrored); } } // namespace ZXing::QRCode diff --git a/core/src/qrcode/QRVersion.cpp b/core/src/qrcode/QRVersion.cpp index bc86b2e185..83971c85ac 100644 --- a/core/src/qrcode/QRVersion.cpp +++ b/core/src/qrcode/QRVersion.cpp @@ -299,85 +299,85 @@ const Version* Version::AllModel1Versions() * See ISO 18004:2000 M.5 Table M.4 */ static const Version allVersions[] = { - {1, {}, { + {1, { 7 , 1, 19, 0, 0, 10, 1, 16, 0, 0, 13, 1, 13, 0, 0, 17, 1, 9 , 0, 0 }}, - {2, {}, { - 10, 1, 36, 0, 0, - 16, 1, 30, 0, 0, - 22, 1, 24, 0, 0, - 30, 1, 16, 0, 0, + {2, { + 10, 1, 36, 0, 0, + 16, 1, 30, 0, 0, + 22, 1, 24, 0, 0, + 30, 1, 16, 0, 0, }}, - {3, {}, { - 15, 1, 57, 0, 0, - 28, 1, 44, 0, 0, - 36, 1, 36, 0, 0, - 48, 1, 24, 0, 0, + {3, { + 15, 1, 57, 0, 0, + 28, 1, 44, 0, 0, + 36, 1, 36, 0, 0, + 48, 1, 24, 0, 0, }}, - {4, {}, { - 20, 1, 80, 0, 0, - 40, 1, 60, 0, 0, - 50, 1, 50, 0, 0, - 66, 1, 34, 0, 0, + {4, { + 20, 1, 80, 0, 0, + 40, 1, 60, 0, 0, + 50, 1, 50, 0, 0, + 66, 1, 34, 0, 0, }}, - {5, {}, { + {5, { 26, 1, 108, 0, 0, 52, 1, 82 , 0, 0, 66, 1, 68 , 0, 0, 88, 2, 46 , 0, 0, }}, - {6, {}, { + {6, { 34 , 1, 136, 0, 0, 63 , 2, 106, 0, 0, 84 , 2, 86 , 0, 0, 112, 2, 58 , 0, 0, }}, - {7, {}, { + {7, { 42 , 1, 170, 0, 0, 80 , 2, 132, 0, 0, 104, 2, 108, 0, 0, 138, 3, 72 , 0, 0, }}, - {8, {}, { + {8, { 48 , 2, 208, 0, 0, 96 , 2, 160, 0, 0, 128, 2, 128, 0, 0, 168, 3, 87 , 0, 0, }}, - {9, {}, { + {9, { 60 , 2, 246, 0, 0, 120, 2, 186, 0, 0, 150, 3, 156, 0, 0, 204, 3, 102, 0, 0, }}, - {10, {}, { + {10, { 68 , 2, 290, 0, 0, 136, 2, 222, 0, 0, 174, 3, 183, 0, 0, 232, 4, 124, 0, 0, }}, - {11, {}, { + {11, { 80 , 2, 336, 0, 0, 160, 4, 256, 0, 0, 208, 4, 208, 0, 0, 270, 5, 145, 0, 0, }}, - {12, {}, { + {12, { 92 , 2, 384, 0, 0, 184, 4, 292, 0, 0, 232, 4, 244, 0, 0, 310, 5, 165, 0, 0, }}, - {13, {}, { + {13, { 108, 3, 432, 0, 0, 208, 4, 332, 0, 0, 264, 4, 276, 0, 0, 348, 6, 192, 0, 0, }}, - {14, {}, { + {14, { 120, 3, 489, 0, 0, 240, 4, 368, 0, 0, 300, 5, 310, 0, 0, @@ -387,14 +387,19 @@ const Version* Version::AllModel1Versions() return allVersions; } +static inline bool isMicro(const std::array& ecBlocks) +{ + return ecBlocks[0].codewordsPerBlock < 7 || ecBlocks[0].codewordsPerBlock == 8; +} + Version::Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array& ecBlocks) - : _versionNumber(versionNumber), _alignmentPatternCenters(alignmentPatternCenters), _ecBlocks(ecBlocks), _isMicro(false) + : _versionNumber(versionNumber), _alignmentPatternCenters(alignmentPatternCenters), _ecBlocks(ecBlocks), _isMicro(false), _isModel1(false) { _totalCodewords = ecBlocks[0].totalDataCodewords(); } Version::Version(int versionNumber, const std::array& ecBlocks) - : _versionNumber(versionNumber), _ecBlocks(ecBlocks), _isMicro(true) + : _versionNumber(versionNumber), _ecBlocks(ecBlocks), _isMicro(isMicro(ecBlocks)), _isModel1(!isMicro(ecBlocks)) { _totalCodewords = ecBlocks[0].totalDataCodewords(); } diff --git a/core/src/qrcode/QRVersion.h b/core/src/qrcode/QRVersion.h index 33d886c733..eca41d87e3 100644 --- a/core/src/qrcode/QRVersion.h +++ b/core/src/qrcode/QRVersion.h @@ -40,6 +40,7 @@ class Version BitMatrix buildFunctionPattern() const; bool isMicroQRCode() const { return _isMicro; } + bool isQRCodeModel1() const { return _isModel1; } static constexpr int DimensionStep(bool isMicro) { return std::array{4, 2}[isMicro]; } static constexpr int DimensionOffset(bool isMicro) { return std::array{17, 9}[isMicro]; } @@ -66,6 +67,7 @@ class Version std::array _ecBlocks; int _totalCodewords; bool _isMicro; + bool _isModel1; Version(int versionNumber, std::initializer_list alignmentPatternCenters, const std::array &ecBlocks); Version(int versionNumber, const std::array& ecBlocks);