diff --git a/src/qrcode/decoder/BitMatrixParser.java b/src/qrcode/decoder/BitMatrixParser.java deleted file mode 100644 index 2298695..0000000 --- a/src/qrcode/decoder/BitMatrixParser.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.FormatException; -import com.google.zxing.common.BitMatrix; - -/** - * @author Sean Owen - */ -final class BitMatrixParser { - - private final BitMatrix bitMatrix; - private Version parsedVersion; - private FormatInformation parsedFormatInfo; - private boolean mirror; - - /** - * @param bitMatrix {@link BitMatrix} to parse - * @throws FormatException if dimension is not >= 21 and 1 mod 4 - */ - BitMatrixParser(BitMatrix bitMatrix) throws FormatException { - int dimension = bitMatrix.getHeight(); - if (dimension < 21 || (dimension & 0x03) != 1) { - throw FormatException.getFormatInstance(); - } - this.bitMatrix = bitMatrix; - } - - /** - *
Reads format information from one of its two locations within the QR Code.
- * - * @return {@link FormatInformation} encapsulating the QR Code's format info - * @throws FormatException if both format information locations cannot be parsed as - * the valid encoding of format information - */ - FormatInformation readFormatInformation() throws FormatException { - - if (parsedFormatInfo != null) { - return parsedFormatInfo; - } - - // Read top-left format info bits - int formatInfoBits1 = 0; - for (int i = 0; i < 6; i++) { - formatInfoBits1 = copyBit(i, 8, formatInfoBits1); - } - // .. and skip a bit in the timing pattern ... - formatInfoBits1 = copyBit(7, 8, formatInfoBits1); - formatInfoBits1 = copyBit(8, 8, formatInfoBits1); - formatInfoBits1 = copyBit(8, 7, formatInfoBits1); - // .. and skip a bit in the timing pattern ... - for (int j = 5; j >= 0; j--) { - formatInfoBits1 = copyBit(8, j, formatInfoBits1); - } - - // Read the top-right/bottom-left pattern too - int dimension = bitMatrix.getHeight(); - int formatInfoBits2 = 0; - int jMin = dimension - 7; - for (int j = dimension - 1; j >= jMin; j--) { - formatInfoBits2 = copyBit(8, j, formatInfoBits2); - } - for (int i = dimension - 8; i < dimension; i++) { - formatInfoBits2 = copyBit(i, 8, formatInfoBits2); - } - - parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2); - if (parsedFormatInfo != null) { - return parsedFormatInfo; - } - throw FormatException.getFormatInstance(); - } - - /** - *Reads version information from one of its two locations within the QR Code.
- * - * @return {@link Version} encapsulating the QR Code's version - * @throws FormatException if both version information locations cannot be parsed as - * the valid encoding of version information - */ - Version readVersion() throws FormatException { - - if (parsedVersion != null) { - return parsedVersion; - } - - int dimension = bitMatrix.getHeight(); - - int provisionalVersion = (dimension - 17) / 4; - if (provisionalVersion <= 6) { - return Version.getVersionForNumber(provisionalVersion); - } - - // Read top-right version info: 3 wide by 6 tall - int versionBits = 0; - int ijMin = dimension - 11; - for (int j = 5; j >= 0; j--) { - for (int i = dimension - 9; i >= ijMin; i--) { - versionBits = copyBit(i, j, versionBits); - } - } - - Version theParsedVersion = Version.decodeVersionInformation(versionBits); - if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) { - parsedVersion = theParsedVersion; - return theParsedVersion; - } - - // Hmm, failed. Try bottom left: 6 wide by 3 tall - versionBits = 0; - for (int i = 5; i >= 0; i--) { - for (int j = dimension - 9; j >= ijMin; j--) { - versionBits = copyBit(i, j, versionBits); - } - } - - theParsedVersion = Version.decodeVersionInformation(versionBits); - if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) { - parsedVersion = theParsedVersion; - return theParsedVersion; - } - throw FormatException.getFormatInstance(); - } - - private int copyBit(int i, int j, int versionBits) { - boolean bit = mirror ? bitMatrix.get(j, i) : bitMatrix.get(i, j); - return bit ? (versionBits << 1) | 0x1 : versionBits << 1; - } - - /** - *Reads the bits in the {@link BitMatrix} representing the finder pattern in the - * correct order in order to reconstruct the codewords bytes contained within the - * QR Code.
- * - * @return bytes encoded within the QR Code - * @throws FormatException if the exact number of bytes expected is not read - */ - byte[] readCodewords() throws FormatException { - - FormatInformation formatInfo = readFormatInformation(); - Version version = readVersion(); - - // Get the data mask for the format used in this QR Code. This will exclude - // some bits from reading as we wind through the bit matrix. - DataMask dataMask = DataMask.values()[formatInfo.getDataMask()]; - int dimension = bitMatrix.getHeight(); - dataMask.unmaskBitMatrix(bitMatrix, dimension); - - BitMatrix functionPattern = version.buildFunctionPattern(); - - boolean readingUp = true; - byte[] result = new byte[version.getTotalCodewords()]; - int resultOffset = 0; - int currentByte = 0; - int bitsRead = 0; - // Read columns in pairs, from right to left - for (int j = dimension - 1; j > 0; j -= 2) { - if (j == 6) { - // Skip whole column with vertical alignment pattern; - // saves time and makes the other code proceed more cleanly - j--; - } - // Read alternatingly from bottom to top then top to bottom - for (int count = 0; count < dimension; count++) { - int i = readingUp ? dimension - 1 - count : count; - for (int col = 0; col < 2; col++) { - // Ignore bits covered by the function pattern - if (!functionPattern.get(j - col, i)) { - // Read a bit - bitsRead++; - currentByte <<= 1; - if (bitMatrix.get(j - col, i)) { - currentByte |= 1; - } - // If we've made a whole byte, save it off - if (bitsRead == 8) { - result[resultOffset++] = (byte) currentByte; - bitsRead = 0; - currentByte = 0; - } - } - } - } - readingUp ^= true; // readingUp = !readingUp; // switch directions - } - if (resultOffset != version.getTotalCodewords()) { - throw FormatException.getFormatInstance(); - } - return result; - } - - /** - * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state. - */ - void remask() { - if (parsedFormatInfo == null) { - return; // We have no format information, and have no data mask - } - DataMask dataMask = DataMask.values()[parsedFormatInfo.getDataMask()]; - int dimension = bitMatrix.getHeight(); - dataMask.unmaskBitMatrix(bitMatrix, dimension); - } - - /** - * Prepare the parser for a mirrored operation. - * This flag has effect only on the {@link #readFormatInformation()} and the - * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the - * {@link #mirror()} method should be called. - * - * @param mirror Whether to read version and format information mirrored. - */ - void setMirror(boolean mirror) { - parsedVersion = null; - parsedFormatInfo = null; - this.mirror = mirror; - } - - /** Mirror the bit matrix in order to attempt a second reading. */ - void mirror() { - for (int x = 0; x < bitMatrix.getWidth(); x++) { - for (int y = x + 1; y < bitMatrix.getHeight(); y++) { - if (bitMatrix.get(x, y) != bitMatrix.get(y, x)) { - bitMatrix.flip(y, x); - bitMatrix.flip(x, y); - } - } - } - } - -} diff --git a/src/qrcode/decoder/DataMaskTestCase.rs b/src/qrcode/decoder/DataMaskTestCase.rs index b18e2df..3e5eaf1 100644 --- a/src/qrcode/decoder/DataMaskTestCase.rs +++ b/src/qrcode/decoder/DataMaskTestCase.rs @@ -27,26 +27,34 @@ type MaskCondition = fn(u32, u32) -> bool; #[test] fn testMask0() { testMaskAcrossDimensions(DataMask::DATA_MASK_000, |i, j| ((i + j) % 2 == 0)); + testMaskAcrossDimensionsU8(0, |i, j| ((i + j) % 2 == 0)); + } #[test] fn testMask1() { testMaskAcrossDimensions(DataMask::DATA_MASK_001, |i, j| i % 2 == 0); + testMaskAcrossDimensionsU8(1, |i, j| i % 2 == 0); + } #[test] fn testMask2() { testMaskAcrossDimensions(DataMask::DATA_MASK_010, |i, j| j % 3 == 0); + testMaskAcrossDimensionsU8(2, |i, j| j % 3 == 0); + } #[test] fn testMask3() { testMaskAcrossDimensions(DataMask::DATA_MASK_011, |i, j| (i + j) % 3 == 0); + testMaskAcrossDimensionsU8(3, |i, j| (i + j) % 3 == 0); } #[test] fn testMask4() { testMaskAcrossDimensions(DataMask::DATA_MASK_100, |i, j| (i / 2 + j / 3) % 2 == 0); + testMaskAcrossDimensionsU8(4, |i, j| (i / 2 + j / 3) % 2 == 0); } #[test] @@ -54,6 +62,9 @@ fn testMask5() { testMaskAcrossDimensions(DataMask::DATA_MASK_101, |i, j| { (i * j) % 2 + (i * j) % 3 == 0 }); + testMaskAcrossDimensionsU8(5, |i, j| { + (i * j) % 2 + (i * j) % 3 == 0 + }); } #[test] @@ -61,6 +72,9 @@ fn testMask6() { testMaskAcrossDimensions(DataMask::DATA_MASK_110, |i, j| { ((i * j) % 2 + (i * j) % 3) % 2 == 0 }); + testMaskAcrossDimensionsU8(6, |i, j| { + ((i * j) % 2 + (i * j) % 3) % 2 == 0 + }); } #[test] @@ -68,6 +82,13 @@ fn testMask7() { testMaskAcrossDimensions(DataMask::DATA_MASK_111, |i, j| { ((i + j) % 2 + (i * j) % 3) % 2 == 0 }); + testMaskAcrossDimensionsU8(7, |i, j| { + ((i + j) % 2 + (i * j) % 3) % 2 == 0 + }); +} + +fn testMaskAcrossDimensionsU8(mask: u8, condition: MaskCondition) { + testMaskAcrossDimensions(mask.try_into().unwrap(), condition) } fn testMaskAcrossDimensions(mask: DataMask, condition: MaskCondition) { diff --git a/src/qrcode/decoder/bit_matrix_parser.rs b/src/qrcode/decoder/bit_matrix_parser.rs new file mode 100644 index 0000000..15bdcb6 --- /dev/null +++ b/src/qrcode/decoder/bit_matrix_parser.rs @@ -0,0 +1,272 @@ +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{Exceptions, common::BitMatrix}; + +use super::{Version, FormatInformation, VersionRef, DataMask}; + + +/** + * @author Sean Owen + */ +pub struct BitMatrixParser { + + bitMatrix:BitMatrix, + parsedVersion:OptionReads format information from one of its two locations within the QR Code.
+ * + * @return {@link FormatInformation} encapsulating the QR Code's format info + * @throws FormatException if both format information locations cannot be parsed as + * the valid encoding of format information + */ + pub fn readFormatInformation(&self) -> Result<&FormatInformation,Exceptions>{ + + if let Some(pfi) = self.parsedFormatInfo { + return Ok(&pfi) + } + + // Read top-left format info bits + let formatInfoBits1 = 0; + for i in 0..6 { + // for (int i = 0; i < 6; i++) { + formatInfoBits1 = self.copyBit(i, 8, formatInfoBits1); + } + // .. and skip a bit in the timing pattern ... + formatInfoBits1 = self.copyBit(7, 8, formatInfoBits1); + formatInfoBits1 = self.copyBit(8, 8, formatInfoBits1); + formatInfoBits1 = self.copyBit(8, 7, formatInfoBits1); + // .. and skip a bit in the timing pattern ... + for j in (0..=5).rev() { + // for (int j = 5; j >= 0; j--) { + formatInfoBits1 = self.copyBit(8, j, formatInfoBits1); + } + + // Read the top-right/bottom-left pattern too + let dimension = self.bitMatrix.getHeight(); + let formatInfoBits2 = 0; + let jMin = dimension - 7; + for j in (jMin..=dimension).rev() { + // for (int j = dimension - 1; j >= jMin; j--) { + formatInfoBits2 = self.copyBit(8, j, formatInfoBits2); + } + for i in (dimension -8)..dimension { + // for (int i = dimension - 8; i < dimension; i++) { + formatInfoBits2 = self.copyBit(i, 8, formatInfoBits2); + } + + self.parsedFormatInfo = FormatInformation::decodeFormatInformation(formatInfoBits1, formatInfoBits2); + if let Some(pfi) = self.parsedFormatInfo { + return Ok(&pfi) + } + Err(Exceptions::FormatException("".to_owned())) + } + + /** + *Reads version information from one of its two locations within the QR Code.
+ * + * @return {@link Version} encapsulating the QR Code's version + * @throws FormatException if both version information locations cannot be parsed as + * the valid encoding of version information + */ + pub fn readVersion(&self) -> ResultReads the bits in the {@link BitMatrix} representing the finder pattern in the + * correct order in order to reconstruct the codewords bytes contained within the + * QR Code.
+ * + * @return bytes encoded within the QR Code + * @throws FormatException if the exact number of bytes expected is not read + */ + pub fn readCodewords(&self) -> ResultEncapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
@@ -216,3 +216,21 @@ impl DataMask {
abstract boolean isMasked(int i, int j);
*/
+
+impl TryFrom