2 * Copyright 2007 ZXing authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.google.zxing.qrcode.decoder;
21 import com.google.zxing.ChecksumException;
22 import com.google.zxing.DecodeHintType;
23 import com.google.zxing.FormatException;
24 import com.google.zxing.common.BitMatrix;
25 import com.google.zxing.common.DecoderResult;
26 import com.google.zxing.common.reedsolomon.GenericGF;
27 import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
28 import com.google.zxing.common.reedsolomon.ReedSolomonException;
31 * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
32 * the QR Code from an image.</p>
36 public final class Decoder {
38 private final ReedSolomonDecoder rsDecoder;
41 rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
44 public DecoderResult decode(boolean[][] image) throws ChecksumException, FormatException {
45 return decode(image, null);
49 * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
50 * "true" is taken to mean a black module.</p>
52 * @param image booleans representing white/black QR Code modules
53 * @return text and bytes encoded within the QR Code
54 * @throws FormatException if the QR Code cannot be decoded
55 * @throws ChecksumException if error correction fails
57 public DecoderResult decode(boolean[][] image, Map<DecodeHintType,?> hints)
58 throws ChecksumException, FormatException {
59 int dimension = image.length;
60 BitMatrix bits = new BitMatrix(dimension);
61 for (int i = 0; i < dimension; i++) {
62 for (int j = 0; j < dimension; j++) {
68 return decode(bits, hints);
71 public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {
72 return decode(bits, null);
76 * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
78 * @param bits booleans representing white/black QR Code modules
79 * @return text and bytes encoded within the QR Code
80 * @throws FormatException if the QR Code cannot be decoded
81 * @throws ChecksumException if error correction fails
83 public DecoderResult decode(BitMatrix bits, Map<DecodeHintType,?> hints)
84 throws FormatException, ChecksumException {
86 // Construct a parser and read version, error-correction level
87 BitMatrixParser parser = new BitMatrixParser(bits);
88 FormatException fe = null;
89 ChecksumException ce = null;
91 return decode(parser, hints);
92 } catch (FormatException e) {
94 } catch (ChecksumException e) {
100 // Revert the bit matrix
103 // Will be attempting a mirrored reading of the version and format info.
104 parser.setMirror(true);
106 // Preemptively read the version.
107 parser.readVersion();
109 // Preemptively read the format information.
110 parser.readFormatInformation();
113 * Since we're here, this means we have successfully detected some kind
114 * of version and format information when mirrored. This is a good sign,
115 * that the QR code may be mirrored, and we should try once more with a
118 // Prepare for a mirrored reading.
121 DecoderResult result = decode(parser, hints);
123 // Success! Notify the caller that the code was mirrored.
124 result.setOther(new QRCodeDecoderMetaData(true));
128 } catch (FormatException e) {
129 // Throw the exception from the original reading
137 } catch (ChecksumException e) {
138 // Throw the exception from the original reading
149 private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints)
150 throws FormatException, ChecksumException {
151 Version version = parser.readVersion();
152 ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
155 byte[] codewords = parser.readCodewords();
156 // Separate into data blocks
157 DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
159 // Count total number of data bytes
161 for (DataBlock dataBlock : dataBlocks) {
162 totalBytes += dataBlock.getNumDataCodewords();
164 byte[] resultBytes = new byte[totalBytes];
165 int resultOffset = 0;
167 // Error-correct and copy data blocks together into a stream of bytes
168 for (DataBlock dataBlock : dataBlocks) {
169 byte[] codewordBytes = dataBlock.getCodewords();
170 int numDataCodewords = dataBlock.getNumDataCodewords();
171 correctErrors(codewordBytes, numDataCodewords);
172 for (int i = 0; i < numDataCodewords; i++) {
173 resultBytes[resultOffset++] = codewordBytes[i];
177 // Decode the contents of that stream of bytes
178 return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);
182 * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
183 * correct the errors in-place using Reed-Solomon error correction.</p>
185 * @param codewordBytes data and error correction codewords
186 * @param numDataCodewords number of codewords that are data bytes
187 * @throws ChecksumException if error correction fails
189 private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {
190 int numCodewords = codewordBytes.length;
191 // First read into an array of ints
192 int[] codewordsInts = new int[numCodewords];
193 for (int i = 0; i < numCodewords; i++) {
194 codewordsInts[i] = codewordBytes[i] & 0xFF;
196 int numECCodewords = codewordBytes.length - numDataCodewords;
198 rsDecoder.decode(codewordsInts, numECCodewords);
199 } catch (ReedSolomonException ignored) {
200 throw ChecksumException.getChecksumInstance();
202 // Copy back into array of bytes -- only need to worry about the bytes that were data
203 // We don't care about errors in the error-correction codewords
204 for (int i = 0; i < numDataCodewords; i++) {
205 codewordBytes[i] = (byte) codewordsInts[i];