You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

MatrixUtil.cs 29 kB

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