New C# port from Suraj Supekar
[zxing.git] / csharp / qrcode / encoder / MatrixUtil.cs
1 /*\r
2 * Copyright 2008 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 System;\r
17 using WriterException = com.google.zxing.WriterException;\r
18 using ByteMatrix = com.google.zxing.common.ByteMatrix;\r
19 using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;\r
20 namespace com.google.zxing.qrcode.encoder\r
21 {\r
22         \r
23         /// <author>  satorux@google.com (Satoru Takabayashi) - creator\r
24         /// </author>\r
25         /// <author>  dswitkin@google.com (Daniel Switkin) - ported from C++\r
26         /// </author>\r
27         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
28         /// </author>\r
29         public sealed class MatrixUtil\r
30         {\r
31                 \r
32                 private MatrixUtil()\r
33                 {\r
34                         // do nothing\r
35                 }\r
36                 \r
37                 //UPGRADE_NOTE: Final was removed from the declaration of 'POSITION_DETECTION_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
38                 private static readonly int[][] POSITION_DETECTION_PATTERN = new int[][]{new int[]{1, 1, 1, 1, 1, 1, 1}, new int[]{1, 0, 0, 0, 0, 0, 1}, new int[]{1, 0, 1, 1, 1, 0, 1}, new int[]{1, 0, 1, 1, 1, 0, 1}, new int[]{1, 0, 1, 1, 1, 0, 1}, new int[]{1, 0, 0, 0, 0, 0, 1}, new int[]{1, 1, 1, 1, 1, 1, 1}};\r
39                 \r
40                 //UPGRADE_NOTE: Final was removed from the declaration of 'HORIZONTAL_SEPARATION_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
41                 private static readonly int[][] HORIZONTAL_SEPARATION_PATTERN = new int[][]{new int[]{0, 0, 0, 0, 0, 0, 0, 0}};\r
42                 \r
43                 //UPGRADE_NOTE: Final was removed from the declaration of 'VERTICAL_SEPARATION_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
44                 private static readonly int[][] VERTICAL_SEPARATION_PATTERN = new int[][]{new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}};\r
45                 \r
46                 //UPGRADE_NOTE: Final was removed from the declaration of 'POSITION_ADJUSTMENT_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
47                 private static readonly int[][] POSITION_ADJUSTMENT_PATTERN = new int[][]{new int[]{1, 1, 1, 1, 1}, new int[]{1, 0, 0, 0, 1}, new int[]{1, 0, 1, 0, 1}, new int[]{1, 0, 0, 0, 1}, new int[]{1, 1, 1, 1, 1}};\r
48                 \r
49                 // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.\r
50                 //UPGRADE_NOTE: Final was removed from the declaration of 'POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
51                 private static readonly int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = new int[][]{new int[]{- 1, - 1, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 18, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 22, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 26, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 30, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 34, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 22, 38, - 1, - 1, - 1, - 1}, new int[]{6, 24, 42, - 1, - 1, - 1, - 1}, new int[]{6, 26, 46, - 1, - 1, - 1, - 1}, new int[]{6, 28, 50, - 1, - 1, - 1, - 1}, new int[]{6, 30, 54, - 1, - 1, - 1, - 1}, new int[]{6, 32, 58, - 1, - 1, - 1, - 1}, new int[]{6, 34, 62, - 1, - 1, - 1, - 1}, new int[]{6, 26, 46, 66, - 1, - 1, - 1}, new int[]{6, 26, 48, 70, - 1, - 1, - 1}, new int[]{6, 26, 50, 74, - 1, - 1, - 1}, new int[]{6, 30, 54, 78, - 1, - 1, - 1}, new int[]{6, 30, 56, 82, - 1, - 1, - 1}, new int[]{6, 30, 58, 86, - 1, - 1, - 1}, new int[]{6, 34, 62, 90, - 1, - 1, - 1}, new int[]{6, 28, 50, 72, 94, - 1, - 1}, new int[]{6, 26, 50, 74, 98, - 1, - 1}, new int[]{6, 30, 54, 78, 102, - 1, - 1}, new int[]{6, 28, 54, 80, 106, - 1, - 1}, new int[]{6, 32, 58, 84, 110, - 1, - 1}, new int[]{6, 30, 58, 86, 114, - 1, - 1}, new int[]{6, 34, 62, 90, 118, - 1, - 1}, new int[]{6, 26, 50, 74, 98, 122, - 1}, new int[]{6, 30, 54, 78, 102, 126, - 1}, new int[]{6, 26, 52, 78, 104, 130, - 1}, new int[]{6, 30, 56, 82, 108, 134, - 1}, new int[]{6, 34, 60, 86, 112, 138, - 1}, new int[]{6, 30, 58, 86, 114, 142, - 1}, new int[]{6, 34, 62, 90, 118, 146, - 1}, new int[]{6, 30, 54, 78, 102, 126, 150}, new int[]{6, 24, 50, 76, 102, 128, 154}, new int[]{6, 28, 54, 80, 106, 132, 158}, new int[]{6, 32, 58, 84, 110, 136, 162}, new int[]{6, 26, 54, 82, 110, 138, 166}, new int[]{6, 30, 58, 86, 114, 142, 170}};\r
52                 \r
53                 // Type info cells at the left top corner.\r
54                 //UPGRADE_NOTE: Final was removed from the declaration of 'TYPE_INFO_COORDINATES'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
55                 private static readonly int[][] TYPE_INFO_COORDINATES = new int[][]{new int[]{8, 0}, new int[]{8, 1}, new int[]{8, 2}, new int[]{8, 3}, new int[]{8, 4}, new int[]{8, 5}, new int[]{8, 7}, new int[]{8, 8}, new int[]{7, 8}, new int[]{5, 8}, new int[]{4, 8}, new int[]{3, 8}, new int[]{2, 8}, new int[]{1, 8}, new int[]{0, 8}};\r
56                 \r
57                 // From Appendix D in JISX0510:2004 (p. 67)\r
58                 private const int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101\r
59                 \r
60                 // From Appendix C in JISX0510:2004 (p.65).\r
61                 private const int TYPE_INFO_POLY = 0x537;\r
62                 private const int TYPE_INFO_MASK_PATTERN = 0x5412;\r
63                 \r
64                 // Set all cells to -1.  -1 means that the cell is empty (not set yet).\r
65                 //\r
66                 // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding\r
67                 // with the ByteMatrix initialized all to zero.\r
68                 public static void  clearMatrix(ByteMatrix matrix)\r
69                 {\r
70                         matrix.clear((sbyte) (- 1));\r
71                 }\r
72                 \r
73                 // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On\r
74                 // success, store the result in "matrix" and return true.\r
75                 public static void  buildMatrix(BitVector dataBits, ErrorCorrectionLevel ecLevel, int version, int maskPattern, ByteMatrix matrix)\r
76                 {\r
77                         clearMatrix(matrix);\r
78                         embedBasicPatterns(version, matrix);\r
79                         // Type information appear with any version.\r
80                         embedTypeInfo(ecLevel, maskPattern, matrix);\r
81                         // Version info appear if version >= 7.\r
82                         maybeEmbedVersionInfo(version, matrix);\r
83                         // Data should be embedded at end.\r
84                         embedDataBits(dataBits, maskPattern, matrix);\r
85                 }\r
86                 \r
87                 // Embed basic patterns. On success, modify the matrix and return true.\r
88                 // The basic patterns are:\r
89                 // - Position detection patterns\r
90                 // - Timing patterns\r
91                 // - Dark dot at the left bottom corner\r
92                 // - Position adjustment patterns, if need be\r
93                 public static void  embedBasicPatterns(int version, ByteMatrix matrix)\r
94                 {\r
95                         // Let's get started with embedding big squares at corners.\r
96                         embedPositionDetectionPatternsAndSeparators(matrix);\r
97                         // Then, embed the dark dot at the left bottom corner.\r
98                         embedDarkDotAtLeftBottomCorner(matrix);\r
99                         \r
100                         // Position adjustment patterns appear if version >= 2.\r
101                         maybeEmbedPositionAdjustmentPatterns(version, matrix);\r
102                         // Timing patterns should be embedded after position adj. patterns.\r
103                         embedTimingPatterns(matrix);\r
104                 }\r
105                 \r
106                 // Embed type information. On success, modify the matrix.\r
107                 public static void  embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)\r
108                 {\r
109                         BitVector typeInfoBits = new BitVector();\r
110                         makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);\r
111                         \r
112                         for (int i = 0; i < typeInfoBits.size(); ++i)\r
113                         {\r
114                                 // Place bits in LSB to MSB order.  LSB (least significant bit) is the last value in\r
115                                 // "typeInfoBits".\r
116                                 int bit = typeInfoBits.at(typeInfoBits.size() - 1 - i);\r
117                                 \r
118                                 // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).\r
119                                 int x1 = TYPE_INFO_COORDINATES[i][0];\r
120                                 int y1 = TYPE_INFO_COORDINATES[i][1];\r
121                                 matrix.set_Renamed(x1, y1, bit);\r
122                                 \r
123                                 if (i < 8)\r
124                                 {\r
125                                         // Right top corner.\r
126                                         int x2 = matrix.Width - i - 1;\r
127                                         int y2 = 8;\r
128                                         matrix.set_Renamed(x2, y2, bit);\r
129                                 }\r
130                                 else\r
131                                 {\r
132                                         // Left bottom corner.\r
133                                         int x2 = 8;\r
134                                         int y2 = matrix.Height - 7 + (i - 8);\r
135                                         matrix.set_Renamed(x2, y2, bit);\r
136                                 }\r
137                         }\r
138                 }\r
139                 \r
140                 // Embed version information if need be. On success, modify the matrix and return true.\r
141                 // See 8.10 of JISX0510:2004 (p.47) for how to embed version information.\r
142                 public static void  maybeEmbedVersionInfo(int version, ByteMatrix matrix)\r
143                 {\r
144                         if (version < 7)\r
145                         {\r
146                                 // Version info is necessary if version >= 7.\r
147                                 return ; // Don't need version info.\r
148                         }\r
149                         BitVector versionInfoBits = new BitVector();\r
150                         makeVersionInfoBits(version, versionInfoBits);\r
151                         \r
152                         int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.\r
153                         for (int i = 0; i < 6; ++i)\r
154                         {\r
155                                 for (int j = 0; j < 3; ++j)\r
156                                 {\r
157                                         // Place bits in LSB (least significant bit) to MSB order.\r
158                                         int bit = versionInfoBits.at(bitIndex);\r
159                                         bitIndex--;\r
160                                         // Left bottom corner.\r
161                                         matrix.set_Renamed(i, matrix.Height - 11 + j, bit);\r
162                                         // Right bottom corner.\r
163                                         matrix.set_Renamed(matrix.Height - 11 + j, i, bit);\r
164                                 }\r
165                         }\r
166                 }\r
167                 \r
168                 // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.\r
169                 // For debugging purposes, it skips masking process if "getMaskPattern" is -1.\r
170                 // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.\r
171                 public static void  embedDataBits(BitVector dataBits, int maskPattern, ByteMatrix matrix)\r
172                 {\r
173                         int bitIndex = 0;\r
174                         int direction = - 1;\r
175                         // Start from the right bottom cell.\r
176                         int x = matrix.Width - 1;\r
177                         int y = matrix.Height - 1;\r
178                         while (x > 0)\r
179                         {\r
180                                 // Skip the vertical timing pattern.\r
181                                 if (x == 6)\r
182                                 {\r
183                                         x -= 1;\r
184                                 }\r
185                                 while (y >= 0 && y < matrix.Height)\r
186                                 {\r
187                                         for (int i = 0; i < 2; ++i)\r
188                                         {\r
189                                                 int xx = x - i;\r
190                                                 // Skip the cell if it's not empty.\r
191                                                 if (!isEmpty(matrix.get_Renamed(xx, y)))\r
192                                                 {\r
193                                                         continue;\r
194                                                 }\r
195                                                 int bit;\r
196                                                 if (bitIndex < dataBits.size())\r
197                                                 {\r
198                                                         bit = dataBits.at(bitIndex);\r
199                                                         ++bitIndex;\r
200                                                 }\r
201                                                 else\r
202                                                 {\r
203                                                         // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described\r
204                                                         // in 8.4.9 of JISX0510:2004 (p. 24).\r
205                                                         bit = 0;\r
206                                                 }\r
207                                                 \r
208                                                 // Skip masking if mask_pattern is -1.\r
209                                                 if (maskPattern != - 1)\r
210                                                 {\r
211                                                         if (MaskUtil.getDataMaskBit(maskPattern, xx, y))\r
212                                                         {\r
213                                                                 bit ^= 0x1;\r
214                                                         }\r
215                                                 }\r
216                                                 matrix.set_Renamed(xx, y, bit);\r
217                                         }\r
218                                         y += direction;\r
219                                 }\r
220                                 direction = - direction; // Reverse the direction.\r
221                                 y += direction;\r
222                                 x -= 2; // Move to the left.\r
223                         }\r
224                         // All bits should be consumed.\r
225                         if (bitIndex != dataBits.size())\r
226                         {\r
227                                 throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.size());\r
228                         }\r
229                 }\r
230                 \r
231                 // Return the position of the most significant bit set (to one) in the "value". The most\r
232                 // significant bit is position 32. If there is no bit set, return 0. Examples:\r
233                 // - findMSBSet(0) => 0\r
234                 // - findMSBSet(1) => 1\r
235                 // - findMSBSet(255) => 8\r
236                 public static int findMSBSet(int value_Renamed)\r
237                 {\r
238                         int numDigits = 0;\r
239                         while (value_Renamed != 0)\r
240                         {\r
241                                 value_Renamed = SupportClass.URShift(value_Renamed, 1);\r
242                                 ++numDigits;\r
243                         }\r
244                         return numDigits;\r
245                 }\r
246                 \r
247                 // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH\r
248                 // code is used for encoding type information and version information.\r
249                 // Example: Calculation of version information of 7.\r
250                 // f(x) is created from 7.\r
251                 //   - 7 = 000111 in 6 bits\r
252                 //   - f(x) = x^2 + x^2 + x^1\r
253                 // g(x) is given by the standard (p. 67)\r
254                 //   - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1\r
255                 // Multiply f(x) by x^(18 - 6)\r
256                 //   - f'(x) = f(x) * x^(18 - 6)\r
257                 //   - f'(x) = x^14 + x^13 + x^12\r
258                 // Calculate the remainder of f'(x) / g(x)\r
259                 //         x^2\r
260                 //         __________________________________________________\r
261                 //   g(x) )x^14 + x^13 + x^12\r
262                 //         x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2\r
263                 //         --------------------------------------------------\r
264                 //                              x^11 + x^10 + x^7 + x^4 + x^2\r
265                 //\r
266                 // The remainder is x^11 + x^10 + x^7 + x^4 + x^2\r
267                 // Encode it in binary: 110010010100\r
268                 // The return value is 0xc94 (1100 1001 0100)\r
269                 //\r
270                 // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit\r
271                 // operations. We don't care if cofficients are positive or negative.\r
272                 public static int calculateBCHCode(int value_Renamed, int poly)\r
273                 {\r
274                         // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1\r
275                         // from 13 to make it 12.\r
276                         int msbSetInPoly = findMSBSet(poly);\r
277                         value_Renamed <<= msbSetInPoly - 1;\r
278                         // Do the division business using exclusive-or operations.\r
279                         while (findMSBSet(value_Renamed) >= msbSetInPoly)\r
280                         {\r
281                                 value_Renamed ^= poly << (findMSBSet(value_Renamed) - msbSetInPoly);\r
282                         }\r
283                         // Now the "value" is the remainder (i.e. the BCH code)\r
284                         return value_Renamed;\r
285                 }\r
286                 \r
287                 // Make bit vector of type information. On success, store the result in "bits" and return true.\r
288                 // Encode error correction level and mask pattern. See 8.9 of\r
289                 // JISX0510:2004 (p.45) for details.\r
290                 public static void  makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitVector bits)\r
291                 {\r
292                         if (!QRCode.isValidMaskPattern(maskPattern))\r
293                         {\r
294                                 throw new WriterException("Invalid mask pattern");\r
295                         }\r
296                         int typeInfo = (ecLevel.Bits << 3) | maskPattern;\r
297                         bits.appendBits(typeInfo, 5);\r
298                         \r
299                         int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);\r
300                         bits.appendBits(bchCode, 10);\r
301                         \r
302                         BitVector maskBits = new BitVector();\r
303                         maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);\r
304                         bits.xor(maskBits);\r
305                         \r
306                         if (bits.size() != 15)\r
307                         {\r
308                                 // Just in case.\r
309                                 throw new WriterException("should not happen but we got: " + bits.size());\r
310                         }\r
311                 }\r
312                 \r
313                 // Make bit vector of version information. On success, store the result in "bits" and return true.\r
314                 // See 8.10 of JISX0510:2004 (p.45) for details.\r
315                 public static void  makeVersionInfoBits(int version, BitVector bits)\r
316                 {\r
317                         bits.appendBits(version, 6);\r
318                         int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);\r
319                         bits.appendBits(bchCode, 12);\r
320                         \r
321                         if (bits.size() != 18)\r
322                         {\r
323                                 // Just in case.\r
324                                 throw new WriterException("should not happen but we got: " + bits.size());\r
325                         }\r
326                 }\r
327                 \r
328                 // Check if "value" is empty.\r
329                 private static bool isEmpty(int value_Renamed)\r
330                 {\r
331                         return value_Renamed == - 1;\r
332                 }\r
333                 \r
334                 // Check if "value" is valid.\r
335                 private static bool isValidValue(int value_Renamed)\r
336                 {\r
337                         return (value_Renamed == - 1 || value_Renamed == 0 || value_Renamed == 1); // Dark (black).\r
338                 }\r
339                 \r
340                 private static void  embedTimingPatterns(ByteMatrix matrix)\r
341                 {\r
342                         // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical\r
343                         // separation patterns (size 1). Thus, 8 = 7 + 1.\r
344                         for (int i = 8; i < matrix.Width - 8; ++i)\r
345                         {\r
346                                 int bit = (i + 1) % 2;\r
347                                 // Horizontal line.\r
348                                 if (!isValidValue(matrix.get_Renamed(i, 6)))\r
349                                 {\r
350                                         throw new WriterException();\r
351                                 }\r
352                                 if (isEmpty(matrix.get_Renamed(i, 6)))\r
353                                 {\r
354                                         matrix.set_Renamed(i, 6, bit);\r
355                                 }\r
356                                 // Vertical line.\r
357                                 if (!isValidValue(matrix.get_Renamed(6, i)))\r
358                                 {\r
359                                         throw new WriterException();\r
360                                 }\r
361                                 if (isEmpty(matrix.get_Renamed(6, i)))\r
362                                 {\r
363                                         matrix.set_Renamed(6, i, bit);\r
364                                 }\r
365                         }\r
366                 }\r
367                 \r
368                 // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)\r
369                 private static void  embedDarkDotAtLeftBottomCorner(ByteMatrix matrix)\r
370                 {\r
371                         if (matrix.get_Renamed(8, matrix.Height - 8) == 0)\r
372                         {\r
373                                 throw new WriterException();\r
374                         }\r
375                         matrix.set_Renamed(8, matrix.Height - 8, 1);\r
376                 }\r
377                 \r
378                 private static void  embedHorizontalSeparationPattern(int xStart, int yStart, ByteMatrix matrix)\r
379                 {\r
380                         // We know the width and height.\r
381                         if (HORIZONTAL_SEPARATION_PATTERN[0].Length != 8 || HORIZONTAL_SEPARATION_PATTERN.Length != 1)\r
382                         {\r
383                                 throw new WriterException("Bad horizontal separation pattern");\r
384                         }\r
385                         for (int x = 0; x < 8; ++x)\r
386                         {\r
387                                 if (!isEmpty(matrix.get_Renamed(xStart + x, yStart)))\r
388                                 {\r
389                                         throw new WriterException();\r
390                                 }\r
391                                 matrix.set_Renamed(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);\r
392                         }\r
393                 }\r
394                 \r
395                 private static void  embedVerticalSeparationPattern(int xStart, int yStart, ByteMatrix matrix)\r
396                 {\r
397                         // We know the width and height.\r
398                         if (VERTICAL_SEPARATION_PATTERN[0].Length != 1 || VERTICAL_SEPARATION_PATTERN.Length != 7)\r
399                         {\r
400                                 throw new WriterException("Bad vertical separation pattern");\r
401                         }\r
402                         for (int y = 0; y < 7; ++y)\r
403                         {\r
404                                 if (!isEmpty(matrix.get_Renamed(xStart, yStart + y)))\r
405                                 {\r
406                                         throw new WriterException();\r
407                                 }\r
408                                 matrix.set_Renamed(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);\r
409                         }\r
410                 }\r
411                 \r
412                 // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are\r
413                 // almost identical, since we cannot write a function that takes 2D arrays in different sizes in\r
414                 // C/C++. We should live with the fact.\r
415                 private static void  embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix)\r
416                 {\r
417                         // We know the width and height.\r
418                         if (POSITION_ADJUSTMENT_PATTERN[0].Length != 5 || POSITION_ADJUSTMENT_PATTERN.Length != 5)\r
419                         {\r
420                                 throw new WriterException("Bad position adjustment");\r
421                         }\r
422                         for (int y = 0; y < 5; ++y)\r
423                         {\r
424                                 for (int x = 0; x < 5; ++x)\r
425                                 {\r
426                                         if (!isEmpty(matrix.get_Renamed(xStart + x, yStart + y)))\r
427                                         {\r
428                                                 throw new WriterException();\r
429                                         }\r
430                                         matrix.set_Renamed(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);\r
431                                 }\r
432                         }\r
433                 }\r
434                 \r
435                 private static void  embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix)\r
436                 {\r
437                         // We know the width and height.\r
438                         if (POSITION_DETECTION_PATTERN[0].Length != 7 || POSITION_DETECTION_PATTERN.Length != 7)\r
439                         {\r
440                                 throw new WriterException("Bad position detection pattern");\r
441                         }\r
442                         for (int y = 0; y < 7; ++y)\r
443                         {\r
444                                 for (int x = 0; x < 7; ++x)\r
445                                 {\r
446                                         if (!isEmpty(matrix.get_Renamed(xStart + x, yStart + y)))\r
447                                         {\r
448                                                 throw new WriterException();\r
449                                         }\r
450                                         matrix.set_Renamed(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);\r
451                                 }\r
452                         }\r
453                 }\r
454                 \r
455                 // Embed position detection patterns and surrounding vertical/horizontal separators.\r
456                 private static void  embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix)\r
457                 {\r
458                         // Embed three big squares at corners.\r
459                         int pdpWidth = POSITION_DETECTION_PATTERN[0].Length;\r
460                         // Left top corner.\r
461                         embedPositionDetectionPattern(0, 0, matrix);\r
462                         // Right top corner.\r
463                         embedPositionDetectionPattern(matrix.Width - pdpWidth, 0, matrix);\r
464                         // Left bottom corner.\r
465                         embedPositionDetectionPattern(0, matrix.Width - pdpWidth, matrix);\r
466                         \r
467                         // Embed horizontal separation patterns around the squares.\r
468                         int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].Length;\r
469                         // Left top corner.\r
470                         embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);\r
471                         // Right top corner.\r
472                         embedHorizontalSeparationPattern(matrix.Width - hspWidth, hspWidth - 1, matrix);\r
473                         // Left bottom corner.\r
474                         embedHorizontalSeparationPattern(0, matrix.Width - hspWidth, matrix);\r
475                         \r
476                         // Embed vertical separation patterns around the squares.\r
477                         int vspSize = VERTICAL_SEPARATION_PATTERN.Length;\r
478                         // Left top corner.\r
479                         embedVerticalSeparationPattern(vspSize, 0, matrix);\r
480                         // Right top corner.\r
481                         embedVerticalSeparationPattern(matrix.Height - vspSize - 1, 0, matrix);\r
482                         // Left bottom corner.\r
483                         embedVerticalSeparationPattern(vspSize, matrix.Height - vspSize, matrix);\r
484                 }\r
485                 \r
486                 // Embed position adjustment patterns if need be.\r
487                 private static void  maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix)\r
488                 {\r
489                         if (version < 2)\r
490                         {\r
491                                 // The patterns appear if version >= 2\r
492                                 return ;\r
493                         }\r
494                         int index = version - 1;\r
495                         int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];\r
496                         int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].Length;\r
497                         for (int i = 0; i < numCoordinates; ++i)\r
498                         {\r
499                                 for (int j = 0; j < numCoordinates; ++j)\r
500                                 {\r
501                                         int y = coordinates[i];\r
502                                         int x = coordinates[j];\r
503                                         if (x == - 1 || y == - 1)\r
504                                         {\r
505                                                 continue;\r
506                                         }\r
507                                         // If the cell is unset, we embed the position adjustment pattern here.\r
508                                         if (isEmpty(matrix.get_Renamed(x, y)))\r
509                                         {\r
510                                                 // -2 is necessary since the x/y coordinates point to the center of the pattern, not the\r
511                                                 // left top corner.\r
512                                                 embedPositionAdjustmentPattern(x - 2, y - 2, matrix);\r
513                                         }\r
514                                 }\r
515                         }\r
516                 }\r
517         }\r
518 }