7e20d75ecde7816c83f3ca9c3e46951b2abfb772
[zxing.git] / core / src / com / google / zxing / qrcode / decoder / BitMatrixParser.java
1 /*\r
2  * Copyright 2007 Google Inc.\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.ReaderException;\r
20 import com.google.zxing.common.BitMatrix;\r
21 \r
22 /**\r
23  * @author srowen@google.com (Sean Owen)\r
24  */\r
25 final class BitMatrixParser {\r
26 \r
27   private final BitMatrix bitMatrix;\r
28   private Version parsedVersion;\r
29   private FormatInformation parsedFormatInfo;\r
30 \r
31   /**\r
32    * @throws com.google.zxing.ReaderException\r
33    *          if dimension is not >= 21 and 1 mod 4\r
34    */\r
35   BitMatrixParser(BitMatrix bitMatrix) throws ReaderException {\r
36     int dimension = bitMatrix.getDimension();\r
37     if (dimension < 21 || (dimension & 0x03) != 1) {\r
38       throw new ReaderException("Dimension must be 1 mod 4 and >= 21");\r
39     }\r
40     this.bitMatrix = bitMatrix;\r
41   }\r
42 \r
43   FormatInformation readFormatInformation() throws ReaderException {\r
44 \r
45     if (parsedFormatInfo != null) {\r
46       return parsedFormatInfo;\r
47     }\r
48 \r
49     // Read top-left format info bits\r
50     int formatInfoBits = 0;\r
51     for (int j = 0; j < 6; j++) {\r
52       formatInfoBits = copyBit(8, j, formatInfoBits);\r
53     }\r
54     // .. and skip a bit in the timing pattern ...\r
55     formatInfoBits = copyBit(8, 7, formatInfoBits);\r
56     formatInfoBits = copyBit(8, 8, formatInfoBits);\r
57     formatInfoBits = copyBit(7, 8, formatInfoBits);\r
58     // .. and skip a bit in the timing pattern ...\r
59     for (int i = 5; i >= 0; i--) {\r
60       formatInfoBits = copyBit(i, 8, formatInfoBits);\r
61     }\r
62 \r
63     parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);\r
64     if (parsedFormatInfo != null) {\r
65       return parsedFormatInfo;\r
66     }\r
67 \r
68     // Hmm, failed. Try the top-right/bottom-left pattern\r
69     int dimension = bitMatrix.getDimension();\r
70     formatInfoBits = 0;\r
71     int iMin = dimension - 8;\r
72     for (int i = dimension - 1; i >= iMin; i--) {\r
73       formatInfoBits = copyBit(i, 8, formatInfoBits);\r
74     }\r
75     for (int j = dimension - 7; j < dimension; j++) {\r
76       formatInfoBits = copyBit(8, j, formatInfoBits);\r
77     }\r
78 \r
79     parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);\r
80     if (parsedFormatInfo != null) {\r
81       return parsedFormatInfo;\r
82     }\r
83     throw new ReaderException("Could not decode format information");\r
84   }\r
85 \r
86   Version readVersion() throws ReaderException {\r
87 \r
88     if (parsedVersion != null) {\r
89       return parsedVersion;\r
90     }\r
91 \r
92     int dimension = bitMatrix.getDimension();\r
93 \r
94     int provisionalVersion = (dimension - 17) >> 2;\r
95     if (provisionalVersion <= 6) {\r
96       return Version.getVersionForNumber(provisionalVersion);\r
97     }\r
98 \r
99     // Read top-right version info: 3 wide by 6 tall\r
100     int versionBits = 0;\r
101     for (int i = 5; i >= 0; i--) {\r
102       int jMin = dimension - 11;\r
103       for (int j = dimension - 9; j >= jMin; j--) {\r
104         versionBits = copyBit(i, j, versionBits);\r
105       }\r
106     }\r
107 \r
108     parsedVersion = Version.decodeVersionInformation(versionBits);\r
109     if (parsedVersion != null) {\r
110       return parsedVersion;\r
111     }\r
112 \r
113     // Hmm, failed. Try bottom left: 6 wide by 3 tall\r
114     versionBits = 0;\r
115     for (int j = 5; j >= 0; j--) {\r
116       int iMin = dimension - 11;\r
117       for (int i = dimension - 11; i >= iMin; i--) {\r
118         versionBits = copyBit(i, j, versionBits);\r
119       }\r
120     }\r
121 \r
122     parsedVersion = Version.decodeVersionInformation(versionBits);\r
123     if (parsedVersion != null) {\r
124       return parsedVersion;\r
125     }\r
126     throw new ReaderException("Could not decode version");\r
127   }\r
128 \r
129   private int copyBit(int i, int j, int versionBits) {\r
130     return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;\r
131   }\r
132 \r
133   byte[] readCodewords() throws ReaderException {\r
134 \r
135     FormatInformation formatInfo = readFormatInformation();\r
136     Version version = readVersion();\r
137 \r
138     DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());\r
139     int dimension = bitMatrix.getDimension();\r
140     dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);\r
141 \r
142     BitMatrix functionPattern = version.buildFunctionPattern();\r
143 \r
144     boolean readingUp = true;\r
145     byte[] result = new byte[version.getTotalCodewords()];\r
146     int resultOffset = 0;\r
147     int currentByte = 0;\r
148     int bitsRead = 0;\r
149     for (int j = dimension - 1; j > 0; j -= 2) {\r
150       if (j == 6) {\r
151         // Skip whole column with vertical alignment pattern;\r
152         // saves time and makes the other code proceed more cleanly\r
153         j--;\r
154       }\r
155       for (int count = 0; count < dimension; count++) {\r
156         int i = readingUp ? dimension - 1 - count : count;\r
157         for (int col = 0; col < 2; col++) {\r
158           if (!functionPattern.get(i, j - col)) {\r
159             bitsRead++;\r
160             currentByte <<= 1;\r
161             if (bitMatrix.get(i, j - col)) {\r
162               currentByte |= 1;\r
163             }\r
164             if (bitsRead == 8) {\r
165               result[resultOffset++] = (byte) currentByte;\r
166               bitsRead = 0;\r
167               currentByte = 0;\r
168             }\r
169           }\r
170         }\r
171       }\r
172       readingUp = !readingUp; // switch directions\r
173     }\r
174     if (resultOffset != version.getTotalCodewords()) {\r
175       throw new ReaderException("Did not read all codewords");\r
176     }\r
177     return result;\r
178   }\r
179 \r
180 }