2 * Copyright 2007 Google Inc.
\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.ReaderException;
\r
20 import com.google.zxing.common.BitMatrix;
\r
23 * @author srowen@google.com (Sean Owen)
\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 ReaderException if dimension is not >= 21 and 1 mod 4
\r
35 BitMatrixParser(BitMatrix bitMatrix) throws ReaderException {
\r
36 int dimension = bitMatrix.getDimension();
\r
37 if (dimension < 21 || (dimension & 0x03) != 1) {
\r
38 throw new ReaderException("Dimension must be 1 mod 4 and >= 21");
\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 ReaderException if both format information locations cannot be parsed as
\r
48 * the valid encoding of format information
\r
50 FormatInformation readFormatInformation() throws ReaderException {
\r
52 if (parsedFormatInfo != null) {
\r
53 return parsedFormatInfo;
\r
56 // Read top-left format info bits
\r
57 int formatInfoBits = 0;
\r
58 for (int j = 0; j < 6; j++) {
\r
59 formatInfoBits = copyBit(8, j, formatInfoBits);
\r
61 // .. and skip a bit in the timing pattern ...
\r
62 formatInfoBits = copyBit(8, 7, formatInfoBits);
\r
63 formatInfoBits = copyBit(8, 8, formatInfoBits);
\r
64 formatInfoBits = copyBit(7, 8, formatInfoBits);
\r
65 // .. and skip a bit in the timing pattern ...
\r
66 for (int i = 5; i >= 0; i--) {
\r
67 formatInfoBits = copyBit(i, 8, formatInfoBits);
\r
70 parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
\r
71 if (parsedFormatInfo != null) {
\r
72 return parsedFormatInfo;
\r
75 // Hmm, failed. Try the top-right/bottom-left pattern
\r
76 int dimension = bitMatrix.getDimension();
\r
78 int iMin = dimension - 8;
\r
79 for (int i = dimension - 1; i >= iMin; i--) {
\r
80 formatInfoBits = copyBit(i, 8, formatInfoBits);
\r
82 for (int j = dimension - 7; j < dimension; j++) {
\r
83 formatInfoBits = copyBit(8, j, formatInfoBits);
\r
86 parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
\r
87 if (parsedFormatInfo != null) {
\r
88 return parsedFormatInfo;
\r
90 throw new ReaderException("Could not decode format information");
\r
94 * <p>Reads version information from one of its two locations within the QR Code.</p>
\r
96 * @return {@link Version} encapsulating the QR Code's version
\r
97 * @throws ReaderException if both version information locations cannot be parsed as
\r
98 * the valid encoding of version information
\r
100 Version readVersion() throws ReaderException {
\r
102 if (parsedVersion != null) {
\r
103 return parsedVersion;
\r
106 int dimension = bitMatrix.getDimension();
\r
108 int provisionalVersion = (dimension - 17) >> 2;
\r
109 if (provisionalVersion <= 6) {
\r
110 return Version.getVersionForNumber(provisionalVersion);
\r
113 // Read top-right version info: 3 wide by 6 tall
\r
114 int versionBits = 0;
\r
115 for (int i = 5; i >= 0; i--) {
\r
116 int jMin = dimension - 11;
\r
117 for (int j = dimension - 9; j >= jMin; j--) {
\r
118 versionBits = copyBit(i, j, versionBits);
\r
122 parsedVersion = Version.decodeVersionInformation(versionBits);
\r
123 if (parsedVersion != null) {
\r
124 return parsedVersion;
\r
127 // Hmm, failed. Try bottom left: 6 wide by 3 tall
\r
129 for (int j = 5; j >= 0; j--) {
\r
130 int iMin = dimension - 11;
\r
131 for (int i = dimension - 11; i >= iMin; i--) {
\r
132 versionBits = copyBit(i, j, versionBits);
\r
136 parsedVersion = Version.decodeVersionInformation(versionBits);
\r
137 if (parsedVersion != null) {
\r
138 return parsedVersion;
\r
140 throw new ReaderException("Could not decode version");
\r
143 private int copyBit(int i, int j, int versionBits) {
\r
144 return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
\r
148 * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
\r
149 * correct order in order to reconstitute the codewords bytes contained within the
\r
152 * @return bytes encoded within the QR Code
\r
153 * @throws ReaderException if the exact number of bytes expected is not read
\r
155 byte[] readCodewords() throws ReaderException {
\r
157 FormatInformation formatInfo = readFormatInformation();
\r
158 Version version = readVersion();
\r
160 // Get the data mask for the format used in this QR Code. This will exclude
\r
161 // some bits from reading as we wind through the bit matrix.
\r
162 DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
\r
163 int dimension = bitMatrix.getDimension();
\r
164 dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);
\r
166 BitMatrix functionPattern = version.buildFunctionPattern();
\r
168 boolean readingUp = true;
\r
169 byte[] result = new byte[version.getTotalCodewords()];
\r
170 int resultOffset = 0;
\r
171 int currentByte = 0;
\r
173 // Read columns in pairs, from right to left
\r
174 for (int j = dimension - 1; j > 0; j -= 2) {
\r
176 // Skip whole column with vertical alignment pattern;
\r
177 // saves time and makes the other code proceed more cleanly
\r
180 // Read alternatingly from bottom to top then top to bottom
\r
181 for (int count = 0; count < dimension; count++) {
\r
182 int i = readingUp ? dimension - 1 - count : count;
\r
183 for (int col = 0; col < 2; col++) {
\r
184 // Ignore bits covered by the function pattern
\r
185 if (!functionPattern.get(i, j - col)) {
\r
189 if (bitMatrix.get(i, j - col)) {
\r
192 // If we've made a whole byte, save it off
\r
193 if (bitsRead == 8) {
\r
194 result[resultOffset++] = (byte) currentByte;
\r
201 readingUp = !readingUp; // switch directions
\r
203 if (resultOffset != version.getTotalCodewords()) {
\r
204 throw new ReaderException("Did not read all codewords");
\r