Committed C# port from Mohamad
[zxing.git] / csharp / qrcode / decoder / BitMatrixParser.cs
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 using com.google.zxing;\r
17 using com.google.zxing.common;\r
18   \r
19 namespace com.google.zxing.qrcode.decoder\r
20 {\r
21     public sealed class BitMatrixParser \r
22     { \r
23           private BitMatrix bitMatrix;\r
24           private Version parsedVersion;\r
25           private FormatInformation parsedFormatInfo;\r
26 \r
27           /**\r
28            * @param bitMatrix {@link BitMatrix} to parse\r
29            * @throws ReaderException if dimension is not >= 21 and 1 mod 4\r
30            */\r
31           public BitMatrixParser(BitMatrix bitMatrix){\r
32             int dimension = bitMatrix.getDimension();\r
33             if (dimension < 21 || (dimension & 0x03) != 1) {\r
34               throw new ReaderException();\r
35             }\r
36             this.bitMatrix = bitMatrix;\r
37           }\r
38 \r
39           /**\r
40            * <p>Reads format information from one of its two locations within the QR Code.</p>\r
41            *\r
42            * @return {@link FormatInformation} encapsulating the QR Code's format info\r
43            * @throws ReaderException if both format information locations cannot be parsed as\r
44            * the valid encoding of format information\r
45            */\r
46           public FormatInformation readFormatInformation(){\r
47 \r
48             if (parsedFormatInfo != null) {\r
49               return parsedFormatInfo;\r
50             }\r
51 \r
52             // Read top-left format info bits\r
53             int formatInfoBits = 0;\r
54             for (int j = 0; j < 6; j++) {\r
55               formatInfoBits = copyBit(8, j, formatInfoBits);\r
56             }\r
57             // .. and skip a bit in the timing pattern ...\r
58             formatInfoBits = copyBit(8, 7, formatInfoBits);\r
59             formatInfoBits = copyBit(8, 8, formatInfoBits);\r
60             formatInfoBits = copyBit(7, 8, formatInfoBits);\r
61             // .. and skip a bit in the timing pattern ...\r
62             for (int i = 5; i >= 0; i--) {\r
63               formatInfoBits = copyBit(i, 8, formatInfoBits);\r
64             }\r
65 \r
66             parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);\r
67             if (parsedFormatInfo != null) {\r
68               return parsedFormatInfo;\r
69             }\r
70 \r
71             // Hmm, failed. Try the top-right/bottom-left pattern\r
72             int dimension = bitMatrix.getDimension();\r
73             formatInfoBits = 0;\r
74             int iMin = dimension - 8;\r
75             for (int i = dimension - 1; i >= iMin; i--) {\r
76               formatInfoBits = copyBit(i, 8, formatInfoBits);\r
77             }\r
78             for (int j = dimension - 7; j < dimension; j++) {\r
79               formatInfoBits = copyBit(8, j, formatInfoBits);\r
80             }\r
81 \r
82             parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);\r
83             if (parsedFormatInfo != null) {\r
84               return parsedFormatInfo;\r
85             }\r
86             throw new ReaderException();\r
87           }\r
88 \r
89           /**\r
90            * <p>Reads version information from one of its two locations within the QR Code.</p>\r
91            *\r
92            * @return {@link Version} encapsulating the QR Code's version\r
93            * @throws ReaderException if both version information locations cannot be parsed as\r
94            * the valid encoding of version information\r
95            */\r
96           public Version readVersion(){\r
97 \r
98             if (parsedVersion != null) {\r
99               return parsedVersion;\r
100             }\r
101 \r
102             int dimension = bitMatrix.getDimension();\r
103 \r
104             int provisionalVersion = (dimension - 17) >> 2;\r
105             if (provisionalVersion <= 6) {\r
106               return Version.getVersionForNumber(provisionalVersion);\r
107             }\r
108 \r
109             // Read top-right version info: 3 wide by 6 tall\r
110             int versionBits = 0;\r
111             for (int i = 5; i >= 0; i--) {\r
112               int jMin = dimension - 11;\r
113               for (int j = dimension - 9; j >= jMin; j--) {\r
114                 versionBits = copyBit(i, j, versionBits);\r
115               }\r
116             }\r
117 \r
118             parsedVersion = Version.decodeVersionInformation(versionBits);\r
119             if (parsedVersion != null) {\r
120               return parsedVersion;\r
121             }\r
122 \r
123             // Hmm, failed. Try bottom left: 6 wide by 3 tall\r
124             versionBits = 0;\r
125             for (int j = 5; j >= 0; j--) {\r
126               int iMin = dimension - 11;\r
127               for (int i = dimension - 11; i >= iMin; i--) {\r
128                 versionBits = copyBit(i, j, versionBits);\r
129               }\r
130             }\r
131 \r
132             parsedVersion = Version.decodeVersionInformation(versionBits);\r
133             if (parsedVersion != null) {\r
134               return parsedVersion;\r
135             }\r
136             throw new ReaderException();\r
137           }\r
138 \r
139           private int copyBit(int i, int j, int versionBits) {\r
140             return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;\r
141           }\r
142 \r
143           /**\r
144            * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the\r
145            * correct order in order to reconstitute the codewords bytes contained within the\r
146            * QR Code.</p>\r
147            *\r
148            * @return bytes encoded within the QR Code\r
149            * @throws ReaderException if the exact number of bytes expected is not read\r
150            */\r
151           public sbyte[] readCodewords(){\r
152 \r
153             FormatInformation formatInfo = readFormatInformation();\r
154             Version version = readVersion();\r
155 \r
156             // Get the data mask for the format used in this QR Code. This will exclude\r
157             // some bits from reading as we wind through the bit matrix.\r
158             DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());\r
159             int dimension = bitMatrix.getDimension();\r
160             dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);\r
161 \r
162             BitMatrix functionPattern = version.buildFunctionPattern();\r
163 \r
164             bool readingUp = true;\r
165             sbyte[] result = new sbyte[version.getTotalCodewords()];\r
166             int resultOffset = 0;\r
167             int currentByte = 0;\r
168             int bitsRead = 0;\r
169             // Read columns in pairs, from right to left\r
170             for (int j = dimension - 1; j > 0; j -= 2) {\r
171               if (j == 6) {\r
172                 // Skip whole column with vertical alignment pattern;\r
173                 // saves time and makes the other code proceed more cleanly\r
174                 j--;\r
175               }\r
176               // Read alternatingly from bottom to top then top to bottom\r
177               for (int count = 0; count < dimension; count++) {\r
178                 int i = readingUp ? dimension - 1 - count : count;\r
179                 for (int col = 0; col < 2; col++) {\r
180                   // Ignore bits covered by the function pattern\r
181                   if (!functionPattern.get(i, j - col)) {\r
182                     // Read a bit\r
183                     bitsRead++;\r
184                     currentByte <<= 1;\r
185                     if (bitMatrix.get(i, j - col)) {\r
186                       currentByte |= 1;\r
187                     }\r
188                     // If we've made a whole byte, save it off\r
189                     if (bitsRead == 8) {\r
190                       result[resultOffset++] = (sbyte) currentByte;\r
191                       bitsRead = 0;\r
192                       currentByte = 0;\r
193                     }\r
194                   }\r
195                 }\r
196               }\r
197               readingUp = !readingUp; // switch directions\r
198             }\r
199             if (resultOffset != version.getTotalCodewords()) {\r
200               throw new ReaderException();\r
201             }\r
202             return result;\r
203           }\r
204     \r
205     \r
206     }\r
207 \r
208 }