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
16 using com.google.zxing;
\r
17 using com.google.zxing.common;
\r
19 namespace com.google.zxing.qrcode.decoder
\r
21 public sealed class BitMatrixParser
\r
23 private BitMatrix bitMatrix;
\r
24 private Version parsedVersion;
\r
25 private FormatInformation parsedFormatInfo;
\r
28 * @param bitMatrix {@link BitMatrix} to parse
\r
29 * @throws ReaderException if dimension is not >= 21 and 1 mod 4
\r
31 public BitMatrixParser(BitMatrix bitMatrix){
\r
32 int dimension = bitMatrix.getDimension();
\r
33 if (dimension < 21 || (dimension & 0x03) != 1) {
\r
34 throw new ReaderException();
\r
36 this.bitMatrix = bitMatrix;
\r
40 * <p>Reads format information from one of its two locations within the QR Code.</p>
\r
42 * @return {@link FormatInformation} encapsulating the QR Code's format info
\r
43 * @throws ReaderException if both format information locations cannot be parsed as
\r
44 * the valid encoding of format information
\r
46 public FormatInformation readFormatInformation(){
\r
48 if (parsedFormatInfo != null) {
\r
49 return parsedFormatInfo;
\r
52 // Read top-left format info bits
\r
53 int formatInfoBits = 0;
\r
54 for (int j = 0; j < 6; j++) {
\r
55 formatInfoBits = copyBit(8, j, formatInfoBits);
\r
57 // .. and skip a bit in the timing pattern ...
\r
58 formatInfoBits = copyBit(8, 7, formatInfoBits);
\r
59 formatInfoBits = copyBit(8, 8, formatInfoBits);
\r
60 formatInfoBits = copyBit(7, 8, formatInfoBits);
\r
61 // .. and skip a bit in the timing pattern ...
\r
62 for (int i = 5; i >= 0; i--) {
\r
63 formatInfoBits = copyBit(i, 8, formatInfoBits);
\r
66 parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
\r
67 if (parsedFormatInfo != null) {
\r
68 return parsedFormatInfo;
\r
71 // Hmm, failed. Try the top-right/bottom-left pattern
\r
72 int dimension = bitMatrix.getDimension();
\r
74 int iMin = dimension - 8;
\r
75 for (int i = dimension - 1; i >= iMin; i--) {
\r
76 formatInfoBits = copyBit(i, 8, formatInfoBits);
\r
78 for (int j = dimension - 7; j < dimension; j++) {
\r
79 formatInfoBits = copyBit(8, j, formatInfoBits);
\r
82 parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
\r
83 if (parsedFormatInfo != null) {
\r
84 return parsedFormatInfo;
\r
86 throw new ReaderException();
\r
90 * <p>Reads version information from one of its two locations within the QR Code.</p>
\r
92 * @return {@link Version} encapsulating the QR Code's version
\r
93 * @throws ReaderException if both version information locations cannot be parsed as
\r
94 * the valid encoding of version information
\r
96 public Version readVersion(){
\r
98 if (parsedVersion != null) {
\r
99 return parsedVersion;
\r
102 int dimension = bitMatrix.getDimension();
\r
104 int provisionalVersion = (dimension - 17) >> 2;
\r
105 if (provisionalVersion <= 6) {
\r
106 return Version.getVersionForNumber(provisionalVersion);
\r
109 // Read top-right version info: 3 wide by 6 tall
\r
110 int versionBits = 0;
\r
111 for (int i = 5; i >= 0; i--) {
\r
112 int jMin = dimension - 11;
\r
113 for (int j = dimension - 9; j >= jMin; j--) {
\r
114 versionBits = copyBit(i, j, versionBits);
\r
118 parsedVersion = Version.decodeVersionInformation(versionBits);
\r
119 if (parsedVersion != null) {
\r
120 return parsedVersion;
\r
123 // Hmm, failed. Try bottom left: 6 wide by 3 tall
\r
125 for (int j = 5; j >= 0; j--) {
\r
126 int iMin = dimension - 11;
\r
127 for (int i = dimension - 11; i >= iMin; i--) {
\r
128 versionBits = copyBit(i, j, versionBits);
\r
132 parsedVersion = Version.decodeVersionInformation(versionBits);
\r
133 if (parsedVersion != null) {
\r
134 return parsedVersion;
\r
136 throw new ReaderException();
\r
139 private int copyBit(int i, int j, int versionBits) {
\r
140 return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
\r
144 * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
\r
145 * correct order in order to reconstitute the codewords bytes contained within the
\r
148 * @return bytes encoded within the QR Code
\r
149 * @throws ReaderException if the exact number of bytes expected is not read
\r
151 public sbyte[] readCodewords(){
\r
153 FormatInformation formatInfo = readFormatInformation();
\r
154 Version version = readVersion();
\r
156 // Get the data mask for the format used in this QR Code. This will exclude
\r
157 // some bits from reading as we wind through the bit matrix.
\r
158 DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
\r
159 int dimension = bitMatrix.getDimension();
\r
160 dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);
\r
162 BitMatrix functionPattern = version.buildFunctionPattern();
\r
164 bool readingUp = true;
\r
165 sbyte[] result = new sbyte[version.getTotalCodewords()];
\r
166 int resultOffset = 0;
\r
167 int currentByte = 0;
\r
169 // Read columns in pairs, from right to left
\r
170 for (int j = dimension - 1; j > 0; j -= 2) {
\r
172 // Skip whole column with vertical alignment pattern;
\r
173 // saves time and makes the other code proceed more cleanly
\r
176 // Read alternatingly from bottom to top then top to bottom
\r
177 for (int count = 0; count < dimension; count++) {
\r
178 int i = readingUp ? dimension - 1 - count : count;
\r
179 for (int col = 0; col < 2; col++) {
\r
180 // Ignore bits covered by the function pattern
\r
181 if (!functionPattern.get(i, j - col)) {
\r
185 if (bitMatrix.get(i, j - col)) {
\r
188 // If we've made a whole byte, save it off
\r
189 if (bitsRead == 8) {
\r
190 result[resultOffset++] = (sbyte) currentByte;
\r
197 readingUp = !readingUp; // switch directions
\r
199 if (resultOffset != version.getTotalCodewords()) {
\r
200 throw new ReaderException();
\r