520e076f9e4d43c39b9dd560329864ef5ae17ec7
[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 FormatException if the QR Code cannot be decoded\r
56    * @throws ChecksumException if error correction fails\r
57    */\r
58   public DecoderResult decode(boolean[][] image, Hashtable hints) throws ChecksumException, FormatException {\r
59     int dimension = image.length;\r
60     BitMatrix bits = new BitMatrix(dimension);\r
61     for (int i = 0; i < dimension; i++) {\r
62       for (int j = 0; j < dimension; j++) {\r
63         if (image[i][j]) {\r
64           bits.set(j, i);\r
65         }\r
66       }\r
67     }\r
68     return decode(bits, hints);\r
69   }\r
70 \r
71   public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {\r
72     return decode(bits, null);\r
73   }\r
74 \r
75   /**\r
76    * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>\r
77    *\r
78    * @param bits booleans representing white/black QR Code modules\r
79    * @return text and bytes encoded within the QR Code\r
80    * @throws FormatException if the QR Code cannot be decoded\r
81    * @throws ChecksumException if error correction fails\r
82    */\r
83   public DecoderResult decode(BitMatrix bits, Hashtable hints) throws FormatException, ChecksumException {\r
84 \r
85     // Construct a parser and read version, error-correction level\r
86     BitMatrixParser parser = new BitMatrixParser(bits);\r
87     Version version = parser.readVersion();\r
88     ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();\r
89 \r
90     // Read codewords\r
91     byte[] codewords = parser.readCodewords();\r
92     // Separate into data blocks\r
93     DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);\r
94 \r
95     // Count total number of data bytes\r
96     int totalBytes = 0;\r
97     for (int i = 0; i < dataBlocks.length; i++) {\r
98       totalBytes += dataBlocks[i].getNumDataCodewords();\r
99     }\r
100     byte[] resultBytes = new byte[totalBytes];\r
101     int resultOffset = 0;\r
102 \r
103     // Error-correct and copy data blocks together into a stream of bytes\r
104     for (int j = 0; j < dataBlocks.length; j++) {\r
105       DataBlock dataBlock = dataBlocks[j];\r
106       byte[] codewordBytes = dataBlock.getCodewords();\r
107       int numDataCodewords = dataBlock.getNumDataCodewords();\r
108       correctErrors(codewordBytes, numDataCodewords);\r
109       for (int i = 0; i < numDataCodewords; i++) {\r
110         resultBytes[resultOffset++] = codewordBytes[i];\r
111       }\r
112     }\r
113 \r
114     // Decode the contents of that stream of bytes\r
115     return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);\r
116   }\r
117 \r
118   /**\r
119    * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to\r
120    * correct the errors in-place using Reed-Solomon error correction.</p>\r
121    *\r
122    * @param codewordBytes data and error correction codewords\r
123    * @param numDataCodewords number of codewords that are data bytes\r
124    * @throws ChecksumException if error correction fails\r
125    */\r
126   private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {\r
127     int numCodewords = codewordBytes.length;\r
128     // First read into an array of ints\r
129     int[] codewordsInts = new int[numCodewords];\r
130     for (int i = 0; i < numCodewords; i++) {\r
131       codewordsInts[i] = codewordBytes[i] & 0xFF;\r
132     }\r
133     int numECCodewords = codewordBytes.length - numDataCodewords;\r
134     try {\r
135       rsDecoder.decode(codewordsInts, numECCodewords);\r
136     } catch (ReedSolomonException rse) {\r
137       throw ChecksumException.getChecksumInstance();\r
138     }\r
139     // Copy back into array of bytes -- only need to worry about the bytes that were data\r
140     // We don't care about errors in the error-correction codewords\r
141     for (int i = 0; i < numDataCodewords; i++) {\r
142       codewordBytes[i] = (byte) codewordsInts[i];\r
143     }\r
144   }\r
145 \r
146 }\r