22cdeb028ae0b989ca11aa2001e19c028c6ee2cd
[zxing.git] / core / src / com / google / zxing / qrcode / decoder / Decoder.java
1 /*\r
2  * Copyright 2007 ZXing authors\r
3  *\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
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\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
15  */\r
16 \r
17 package com.google.zxing.qrcode.decoder;\r
18 \r
19 import com.google.zxing.ChecksumException;\r
20 import com.google.zxing.FormatException;\r
21 import com.google.zxing.NotFoundException;\r
22 import com.google.zxing.common.BitMatrix;\r
23 import com.google.zxing.common.DecoderResult;\r
24 import com.google.zxing.common.reedsolomon.GF256;\r
25 import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;\r
26 import com.google.zxing.common.reedsolomon.ReedSolomonException;\r
27 \r
28 import java.util.Hashtable;\r
29 \r
30 /**\r
31  * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting\r
32  * the QR Code from an image.</p>\r
33  *\r
34  * @author Sean Owen\r
35  */\r
36 public final class Decoder {\r
37 \r
38   private final ReedSolomonDecoder rsDecoder;\r
39 \r
40   public Decoder() {\r
41     rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD);\r
42   }\r
43 \r
44   public DecoderResult decode(boolean[][] image)\r
45       throws ChecksumException, FormatException, NotFoundException {\r
46     return decode(image, null);\r
47   }\r
48 \r
49   /**\r
50    * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.\r
51    * "true" is taken to mean a black module.</p>\r
52    *\r
53    * @param image booleans representing white/black QR Code modules\r
54    * @return text and bytes encoded within the QR Code\r
55    * @throws NotFoundException if the QR Code cannot be found\r
56    * @throws FormatException if the QR Code cannot be decoded\r
57    * @throws ChecksumException if error correction fails\r
58    */\r
59   public DecoderResult decode(boolean[][] image, Hashtable hints)\r
60       throws ChecksumException, FormatException, NotFoundException {\r
61     int dimension = image.length;\r
62     BitMatrix bits = new BitMatrix(dimension);\r
63     for (int i = 0; i < dimension; i++) {\r
64       for (int j = 0; j < dimension; j++) {\r
65         if (image[i][j]) {\r
66           bits.set(j, i);\r
67         }\r
68       }\r
69     }\r
70     return decode(bits, hints);\r
71   }\r
72 \r
73   public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException, NotFoundException {\r
74     return decode(bits, null);\r
75   }\r
76 \r
77   /**\r
78    * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>\r
79    *\r
80    * @param bits booleans representing white/black QR Code modules\r
81    * @return text and bytes encoded within the QR Code\r
82    * @throws FormatException if the QR Code cannot be decoded\r
83    * @throws ChecksumException if error correction fails\r
84    */\r
85   public DecoderResult decode(BitMatrix bits, Hashtable hints) throws FormatException, ChecksumException {\r
86 \r
87     // Construct a parser and read version, error-correction level\r
88     BitMatrixParser parser = new BitMatrixParser(bits);\r
89     Version version = parser.readVersion();\r
90     ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();\r
91 \r
92     // Read codewords\r
93     byte[] codewords = parser.readCodewords();\r
94     // Separate into data blocks\r
95     DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);\r
96 \r
97     // Count total number of data bytes\r
98     int totalBytes = 0;\r
99     for (int i = 0; i < dataBlocks.length; i++) {\r
100       totalBytes += dataBlocks[i].getNumDataCodewords();\r
101     }\r
102     byte[] resultBytes = new byte[totalBytes];\r
103     int resultOffset = 0;\r
104 \r
105     // Error-correct and copy data blocks together into a stream of bytes\r
106     for (int j = 0; j < dataBlocks.length; j++) {\r
107       DataBlock dataBlock = dataBlocks[j];\r
108       byte[] codewordBytes = dataBlock.getCodewords();\r
109       int numDataCodewords = dataBlock.getNumDataCodewords();\r
110       correctErrors(codewordBytes, numDataCodewords);\r
111       for (int i = 0; i < numDataCodewords; i++) {\r
112         resultBytes[resultOffset++] = codewordBytes[i];\r
113       }\r
114     }\r
115 \r
116     // Decode the contents of that stream of bytes\r
117     return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);\r
118   }\r
119 \r
120   /**\r
121    * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to\r
122    * correct the errors in-place using Reed-Solomon error correction.</p>\r
123    *\r
124    * @param codewordBytes data and error correction codewords\r
125    * @param numDataCodewords number of codewords that are data bytes\r
126    * @throws ChecksumException if error correction fails\r
127    */\r
128   private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {\r
129     int numCodewords = codewordBytes.length;\r
130     // First read into an array of ints\r
131     int[] codewordsInts = new int[numCodewords];\r
132     for (int i = 0; i < numCodewords; i++) {\r
133       codewordsInts[i] = codewordBytes[i] & 0xFF;\r
134     }\r
135     int numECCodewords = codewordBytes.length - numDataCodewords;\r
136     try {\r
137       rsDecoder.decode(codewordsInts, numECCodewords);\r
138     } catch (ReedSolomonException rse) {\r
139       throw ChecksumException.getChecksumInstance();\r
140     }\r
141     // Copy back into array of bytes -- only need to worry about the bytes that were data\r
142     // We don't care about errors in the error-correction codewords\r
143     for (int i = 0; i < numDataCodewords; i++) {\r
144       codewordBytes[i] = (byte) codewordsInts[i];\r
145     }\r
146   }\r
147 \r
148 }\r