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.

MaskUtil.cs 9.3 kB

10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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 System;
  17. namespace ZXing.QrCode.Internal
  18. {
  19. /// <summary>
  20. ///
  21. /// </summary>
  22. /// <author>Satoru Takabayashi</author>
  23. /// <author>Daniel Switkin</author>
  24. /// <author>Sean Owen</author>
  25. public static class MaskUtil
  26. {
  27. // Penalty weights from section 6.8.2.1
  28. private const int N1 = 3;
  29. private const int N2 = 3;
  30. private const int N3 = 40;
  31. private const int N4 = 10;
  32. /// <summary>
  33. /// Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
  34. /// give penalty to them. Example: 00000 or 11111.
  35. /// </summary>
  36. /// <param name="matrix">The matrix.</param>
  37. /// <returns></returns>
  38. public static int applyMaskPenaltyRule1(ByteMatrix matrix)
  39. {
  40. return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
  41. }
  42. /// <summary>
  43. /// Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
  44. /// penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a
  45. /// penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
  46. /// </summary>
  47. /// <param name="matrix">The matrix.</param>
  48. /// <returns></returns>
  49. public static int applyMaskPenaltyRule2(ByteMatrix matrix)
  50. {
  51. int penalty = 0;
  52. var array = matrix.Array;
  53. int width = matrix.Width;
  54. int height = matrix.Height;
  55. for (int y = 0; y < height - 1; y++)
  56. {
  57. for (int x = 0; x < width - 1; x++)
  58. {
  59. int value = array[y][x];
  60. if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1])
  61. {
  62. penalty++;
  63. }
  64. }
  65. }
  66. return N2 * penalty;
  67. }
  68. /// <summary>
  69. /// Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
  70. /// 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give
  71. /// penalties twice (i.e. 40 * 2).
  72. /// </summary>
  73. /// <param name="matrix">The matrix.</param>
  74. /// <returns></returns>
  75. public static int applyMaskPenaltyRule3(ByteMatrix matrix)
  76. {
  77. int numPenalties = 0;
  78. byte[][] array = matrix.Array;
  79. int width = matrix.Width;
  80. int height = matrix.Height;
  81. for (int y = 0; y < height; y++)
  82. {
  83. for (int x = 0; x < width; x++)
  84. {
  85. byte[] arrayY = array[y]; // We can at least optimize this access
  86. if (x + 6 < width &&
  87. arrayY[x] == 1 &&
  88. arrayY[x + 1] == 0 &&
  89. arrayY[x + 2] == 1 &&
  90. arrayY[x + 3] == 1 &&
  91. arrayY[x + 4] == 1 &&
  92. arrayY[x + 5] == 0 &&
  93. arrayY[x + 6] == 1 &&
  94. (isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11)))
  95. {
  96. numPenalties++;
  97. }
  98. if (y + 6 < height &&
  99. array[y][x] == 1 &&
  100. array[y + 1][x] == 0 &&
  101. array[y + 2][x] == 1 &&
  102. array[y + 3][x] == 1 &&
  103. array[y + 4][x] == 1 &&
  104. array[y + 5][x] == 0 &&
  105. array[y + 6][x] == 1 &&
  106. (isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11)))
  107. {
  108. numPenalties++;
  109. }
  110. }
  111. }
  112. return numPenalties * N3;
  113. }
  114. private static bool isWhiteHorizontal(byte[] rowArray, int from, int to)
  115. {
  116. for (int i = from; i < to; i++)
  117. {
  118. if (i >= 0 && i < rowArray.Length && rowArray[i] == 1)
  119. {
  120. return false;
  121. }
  122. }
  123. return true;
  124. }
  125. private static bool isWhiteVertical(byte[][] array, int col, int from, int to)
  126. {
  127. for (int i = from; i < to; i++)
  128. {
  129. if (i >= 0 && i < array.Length && array[i][col] == 1)
  130. {
  131. return false;
  132. }
  133. }
  134. return true;
  135. }
  136. /// <summary>
  137. /// Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
  138. /// penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.
  139. /// </summary>
  140. /// <param name="matrix">The matrix.</param>
  141. /// <returns></returns>
  142. public static int applyMaskPenaltyRule4(ByteMatrix matrix)
  143. {
  144. int numDarkCells = 0;
  145. var array = matrix.Array;
  146. int width = matrix.Width;
  147. int height = matrix.Height;
  148. for (int y = 0; y < height; y++)
  149. {
  150. var arrayY = array[y];
  151. for (int x = 0; x < width; x++)
  152. {
  153. if (arrayY[x] == 1)
  154. {
  155. numDarkCells++;
  156. }
  157. }
  158. }
  159. var numTotalCells = matrix.Height * matrix.Width;
  160. var darkRatio = (double)numDarkCells / numTotalCells;
  161. var fivePercentVariances = (int)(Math.Abs(darkRatio - 0.5) * 20.0); // * 100.0 / 5.0
  162. return fivePercentVariances * N4;
  163. }
  164. /// <summary>
  165. /// Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
  166. /// pattern conditions.
  167. /// </summary>
  168. /// <param name="maskPattern">The mask pattern.</param>
  169. /// <param name="x">The x.</param>
  170. /// <param name="y">The y.</param>
  171. /// <returns></returns>
  172. public static bool getDataMaskBit(int maskPattern, int x, int y)
  173. {
  174. int intermediate, temp;
  175. switch (maskPattern)
  176. {
  177. case 0:
  178. intermediate = (y + x) & 0x1;
  179. break;
  180. case 1:
  181. intermediate = y & 0x1;
  182. break;
  183. case 2:
  184. intermediate = x % 3;
  185. break;
  186. case 3:
  187. intermediate = (y + x) % 3;
  188. break;
  189. case 4:
  190. intermediate = (((int)((uint)y >> 1)) + (x / 3)) & 0x1;
  191. break;
  192. case 5:
  193. temp = y * x;
  194. intermediate = (temp & 0x1) + (temp % 3);
  195. break;
  196. case 6:
  197. temp = y * x;
  198. intermediate = (((temp & 0x1) + (temp % 3)) & 0x1);
  199. break;
  200. case 7:
  201. temp = y * x;
  202. intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
  203. break;
  204. default:
  205. throw new ArgumentException("Invalid mask pattern: " + maskPattern);
  206. }
  207. return intermediate == 0;
  208. }
  209. /// <summary>
  210. /// Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
  211. /// vertical and horizontal orders respectively.
  212. /// </summary>
  213. /// <param name="matrix">The matrix.</param>
  214. /// <param name="isHorizontal">if set to <c>true</c> [is horizontal].</param>
  215. /// <returns></returns>
  216. private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, bool isHorizontal)
  217. {
  218. int penalty = 0;
  219. int iLimit = isHorizontal ? matrix.Height : matrix.Width;
  220. int jLimit = isHorizontal ? matrix.Width : matrix.Height;
  221. var array = matrix.Array;
  222. for (int i = 0; i < iLimit; i++)
  223. {
  224. int numSameBitCells = 0;
  225. int prevBit = -1;
  226. for (int j = 0; j < jLimit; j++)
  227. {
  228. int bit = isHorizontal ? array[i][j] : array[j][i];
  229. if (bit == prevBit)
  230. {
  231. numSameBitCells++;
  232. }
  233. else
  234. {
  235. if (numSameBitCells >= 5)
  236. {
  237. penalty += N1 + (numSameBitCells - 5);
  238. }
  239. numSameBitCells = 1; // Include the cell itself.
  240. prevBit = bit;
  241. }
  242. }
  243. if (numSameBitCells >= 5)
  244. {
  245. penalty += N1 + (numSameBitCells - 5);
  246. }
  247. }
  248. return penalty;
  249. }
  250. }
  251. }