2 * Copyright 2007 ZXing authors
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
17 package com.google.zxing.qrcode.decoder;
\r
19 import com.google.zxing.FormatException;
\r
20 import com.google.zxing.common.BitMatrix;
\r
25 final class BitMatrixParser {
\r
27 private final BitMatrix bitMatrix;
\r
28 private Version parsedVersion;
\r
29 private FormatInformation parsedFormatInfo;
\r
32 * @param bitMatrix {@link BitMatrix} to parse
\r
33 * @throws FormatException if dimension is not >= 21 and 1 mod 4
\r
35 BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
\r
36 int dimension = bitMatrix.getDimension();
\r
37 if (dimension < 21 || (dimension & 0x03) != 1) {
\r
38 throw FormatException.getFormatInstance();
\r
40 this.bitMatrix = bitMatrix;
\r
44 * <p>Reads format information from one of its two locations within the QR Code.</p>
\r
46 * @return {@link FormatInformation} encapsulating the QR Code's format info
\r
47 * @throws FormatException if both format information locations cannot be parsed as
\r
48 * the valid encoding of format information
\r
50 FormatInformation readFormatInformation() throws FormatException {
\r
52 if (parsedFormatInfo != null) {
\r
53 return parsedFormatInfo;
\r
56 // Read top-left format info bits
\r
57 int formatInfoBits1 = 0;
\r
58 for (int i = 0; i < 6; i++) {
\r
59 formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
\r
61 // .. and skip a bit in the timing pattern ...
\r
62 formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
\r
63 formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
\r
64 formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
\r
65 // .. and skip a bit in the timing pattern ...
\r
66 for (int j = 5; j >= 0; j--) {
\r
67 formatInfoBits1 = copyBit(8, j, formatInfoBits1);
\r
70 // Read the top-right/bottom-left pattern too
\r
71 int dimension = bitMatrix.getDimension();
\r
72 int formatInfoBits2 = 0;
\r
73 int iMin = dimension - 8;
\r
74 for (int i = dimension - 1; i >= iMin; i--) {
\r
75 formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
\r
77 for (int j = dimension - 7; j < dimension; j++) {
\r
78 formatInfoBits2 = copyBit(8, j, formatInfoBits2);
\r
81 parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
\r
82 if (parsedFormatInfo != null) {
\r
83 return parsedFormatInfo;
\r
85 throw FormatException.getFormatInstance();
\r
89 * <p>Reads version information from one of its two locations within the QR Code.</p>
\r
91 * @return {@link Version} encapsulating the QR Code's version
\r
92 * @throws FormatException if both version information locations cannot be parsed as
\r
93 * the valid encoding of version information
\r
95 Version readVersion() throws FormatException {
\r
97 if (parsedVersion != null) {
\r
98 return parsedVersion;
\r
101 int dimension = bitMatrix.getDimension();
\r
103 int provisionalVersion = (dimension - 17) >> 2;
\r
104 if (provisionalVersion <= 6) {
\r
105 return Version.getVersionForNumber(provisionalVersion);
\r
108 // Read top-right version info: 3 wide by 6 tall
\r
109 int versionBits = 0;
\r
110 int ijMin = dimension - 11;
\r
111 for (int j = 5; j >= 0; j--) {
\r
112 for (int i = dimension - 9; i >= ijMin; i--) {
\r
113 versionBits = copyBit(i, j, versionBits);
\r
117 parsedVersion = Version.decodeVersionInformation(versionBits);
\r
118 if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
\r
119 return parsedVersion;
\r
122 // Hmm, failed. Try bottom left: 6 wide by 3 tall
\r
124 for (int i = 5; i >= 0; i--) {
\r
125 for (int j = dimension - 9; j >= ijMin; j--) {
\r
126 versionBits = copyBit(i, j, versionBits);
\r
130 parsedVersion = Version.decodeVersionInformation(versionBits);
\r
131 if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
\r
132 return parsedVersion;
\r
134 throw FormatException.getFormatInstance();
\r
137 private int copyBit(int i, int j, int versionBits) {
\r
138 return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
\r
142 * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
\r
143 * correct order in order to reconstitute the codewords bytes contained within the
\r
146 * @return bytes encoded within the QR Code
\r
147 * @throws FormatException if the exact number of bytes expected is not read
\r
149 byte[] readCodewords() throws FormatException {
\r
151 FormatInformation formatInfo = readFormatInformation();
\r
152 Version version = readVersion();
\r
154 // Get the data mask for the format used in this QR Code. This will exclude
\r
155 // some bits from reading as we wind through the bit matrix.
\r
156 DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
\r
157 int dimension = bitMatrix.getDimension();
\r
158 dataMask.unmaskBitMatrix(bitMatrix, dimension);
\r
160 BitMatrix functionPattern = version.buildFunctionPattern();
\r
162 boolean readingUp = true;
\r
163 byte[] result = new byte[version.getTotalCodewords()];
\r
164 int resultOffset = 0;
\r
165 int currentByte = 0;
\r
167 // Read columns in pairs, from right to left
\r
168 for (int j = dimension - 1; j > 0; j -= 2) {
\r
170 // Skip whole column with vertical alignment pattern;
\r
171 // saves time and makes the other code proceed more cleanly
\r
174 // Read alternatingly from bottom to top then top to bottom
\r
175 for (int count = 0; count < dimension; count++) {
\r
176 int i = readingUp ? dimension - 1 - count : count;
\r
177 for (int col = 0; col < 2; col++) {
\r
178 // Ignore bits covered by the function pattern
\r
179 if (!functionPattern.get(j - col, i)) {
\r
183 if (bitMatrix.get(j - col, i)) {
\r
186 // If we've made a whole byte, save it off
\r
187 if (bitsRead == 8) {
\r
188 result[resultOffset++] = (byte) currentByte;
\r
195 readingUp ^= true; // readingUp = !readingUp; // switch directions
\r
197 if (resultOffset != version.getTotalCodewords()) {
\r
198 throw FormatException.getFormatInstance();
\r