]> Pileus Git - ~andy/freeotp/blob - src/com/google/zxing/qrcode/decoder/Decoder.java
Add native camera support
[~andy/freeotp] / src / com / google / zxing / qrcode / decoder / Decoder.java
1 /*
2  * Copyright 2007 ZXing authors
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.google.zxing.qrcode.decoder;
18
19 import java.util.Map;
20
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;
29
30 /**
31  * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
32  * the QR Code from an image.</p>
33  *
34  * @author Sean Owen
35  */
36 public final class Decoder {
37
38   private final ReedSolomonDecoder rsDecoder;
39
40   public Decoder() {
41     rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
42   }
43
44   public DecoderResult decode(boolean[][] image) throws ChecksumException, FormatException {
45     return decode(image, null);
46   }
47
48   /**
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>
51    *
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
56    */
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++) {
63         if (image[i][j]) {
64           bits.set(j, i);
65         }
66       }
67     }
68     return decode(bits, hints);
69   }
70
71   public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {
72     return decode(bits, null);
73   }
74
75   /**
76    * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
77    *
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
82    */
83   public DecoderResult decode(BitMatrix bits, Map<DecodeHintType,?> hints)
84       throws FormatException, ChecksumException {
85
86     // Construct a parser and read version, error-correction level
87     BitMatrixParser parser = new BitMatrixParser(bits);
88     FormatException fe = null;
89     ChecksumException ce = null;
90     try {
91       return decode(parser, hints);
92     } catch (FormatException e) {
93       fe = e;
94     } catch (ChecksumException e) {
95       ce = e;
96     }
97
98     try {
99
100       // Revert the bit matrix
101       parser.remask();
102
103       // Will be attempting a mirrored reading of the version and format info.
104       parser.setMirror(true);
105
106       // Preemptively read the version.
107       parser.readVersion();
108
109       // Preemptively read the format information.
110       parser.readFormatInformation();
111
112       /*
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
116        * mirrored content.
117        */
118       // Prepare for a mirrored reading.
119       parser.mirror();
120
121       DecoderResult result = decode(parser, hints);
122
123       // Success! Notify the caller that the code was mirrored.
124       result.setOther(new QRCodeDecoderMetaData(true));
125
126       return result;
127
128     } catch (FormatException e) {
129       // Throw the exception from the original reading
130       if (fe != null) {
131         throw fe;
132       }
133       if (ce != null) {
134         throw ce;
135       }
136       throw e;
137     } catch (ChecksumException e) {
138       // Throw the exception from the original reading
139       if (fe != null) {
140         throw fe;
141       }
142       if (ce != null) {
143         throw ce;
144       }
145       throw e;
146     }
147   }
148
149   private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints)
150       throws FormatException, ChecksumException {
151     Version version = parser.readVersion();
152     ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
153
154     // Read codewords
155     byte[] codewords = parser.readCodewords();
156     // Separate into data blocks
157     DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
158
159     // Count total number of data bytes
160     int totalBytes = 0;
161     for (DataBlock dataBlock : dataBlocks) {
162       totalBytes += dataBlock.getNumDataCodewords();
163     }
164     byte[] resultBytes = new byte[totalBytes];
165     int resultOffset = 0;
166
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];
174       }
175     }
176
177     // Decode the contents of that stream of bytes
178     return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);
179   }
180
181   /**
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>
184    *
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
188    */
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;
195     }
196     int numECCodewords = codewordBytes.length - numDataCodewords;
197     try {
198       rsDecoder.decode(codewordsInts, numECCodewords);
199     } catch (ReedSolomonException ignored) {
200       throw ChecksumException.getChecksumInstance();
201     }
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];
206     }
207   }
208
209 }