BitMatrix.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. * Copyright 2007 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 FastReport.Barcode.Aztec
  18. {
  19. /// <summary>
  20. /// <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
  21. /// module, x is the column position, and y is the row position. The ordering is always x, y.
  22. /// The origin is at the top-left.</p>
  23. /// <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
  24. /// with a new int. This is done intentionally so that we can copy out a row into a BitArray very
  25. /// efficiently.</p>
  26. /// <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
  27. /// meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
  28. /// </summary>
  29. /// <author>Sean Owen</author>
  30. /// <author>dswitkin@google.com (Daniel Switkin)</author>
  31. internal sealed partial class BitMatrix
  32. {
  33. private readonly int width;
  34. private readonly int height;
  35. private readonly int rowSize;
  36. private readonly int[] bits;
  37. /// <returns> The width of the matrix
  38. /// </returns>
  39. public int Width
  40. {
  41. get
  42. {
  43. return width;
  44. }
  45. }
  46. /// <returns> The height of the matrix
  47. /// </returns>
  48. public int Height
  49. {
  50. get
  51. {
  52. return height;
  53. }
  54. }
  55. /// <summary> This method is for compatibility with older code. It's only logical to call if the matrix
  56. /// is square, so I'm throwing if that's not the case.
  57. ///
  58. /// </summary>
  59. /// <returns> row/column dimension of this matrix
  60. /// </returns>
  61. public int Dimension
  62. {
  63. get
  64. {
  65. if (width != height)
  66. {
  67. throw new System.ArgumentException("Can't call getDimension() on a non-square matrix");
  68. }
  69. return width;
  70. }
  71. }
  72. // A helper to construct a square matrix.
  73. public BitMatrix(int dimension)
  74. : this(dimension, dimension)
  75. {
  76. }
  77. public BitMatrix(int width, int height)
  78. {
  79. if (width < 1 || height < 1)
  80. {
  81. throw new System.ArgumentException("Both dimensions must be greater than 0");
  82. }
  83. this.width = width;
  84. this.height = height;
  85. this.rowSize = (width + 31) >> 5;
  86. bits = new int[rowSize * height];
  87. }
  88. internal BitMatrix(int width, int height, int rowSize, int[] bits)
  89. {
  90. this.width = width;
  91. this.height = height;
  92. this.rowSize = rowSize;
  93. this.bits = bits;
  94. }
  95. internal BitMatrix(int width, int height, int[] bits)
  96. {
  97. this.width = width;
  98. this.height = height;
  99. this.rowSize = (width + 31) >> 5;
  100. this.bits = bits;
  101. }
  102. /// <summary> <p>Gets the requested bit, where true means black.</p>
  103. ///
  104. /// </summary>
  105. /// <param name="x">The horizontal component (i.e. which column)
  106. /// </param>
  107. /// <param name="y">The vertical component (i.e. which row)
  108. /// </param>
  109. /// <returns> value of given bit in matrix
  110. /// </returns>
  111. public bool this[int x, int y]
  112. {
  113. get
  114. {
  115. int offset = y * rowSize + (x >> 5);
  116. return (((int)((uint)(bits[offset]) >> (x & 0x1f))) & 1) != 0;
  117. }
  118. set
  119. {
  120. if (value)
  121. {
  122. int offset = y * rowSize + (x >> 5);
  123. bits[offset] |= 1 << (x & 0x1f);
  124. }
  125. }
  126. }
  127. /// <summary> <p>Flips the given bit.</p>
  128. ///
  129. /// </summary>
  130. /// <param name="x">The horizontal component (i.e. which column)
  131. /// </param>
  132. /// <param name="y">The vertical component (i.e. which row)
  133. /// </param>
  134. public void flip(int x, int y)
  135. {
  136. int offset = y * rowSize + (x >> 5);
  137. bits[offset] ^= 1 << (x & 0x1f);
  138. }
  139. /// <summary> Clears all bits (sets to false).</summary>
  140. public void clear()
  141. {
  142. int max = bits.Length;
  143. for (int i = 0; i < max; i++)
  144. {
  145. bits[i] = 0;
  146. }
  147. }
  148. /// <summary> <p>Sets a square region of the bit matrix to true.</p>
  149. ///
  150. /// </summary>
  151. /// <param name="left">The horizontal position to begin at (inclusive)
  152. /// </param>
  153. /// <param name="top">The vertical position to begin at (inclusive)
  154. /// </param>
  155. /// <param name="width">The width of the region
  156. /// </param>
  157. /// <param name="height">The height of the region
  158. /// </param>
  159. public void setRegion(int left, int top, int width, int height)
  160. {
  161. if (top < 0 || left < 0)
  162. {
  163. throw new System.ArgumentException("Left and top must be nonnegative");
  164. }
  165. if (height < 1 || width < 1)
  166. {
  167. throw new System.ArgumentException("Height and width must be at least 1");
  168. }
  169. int right = left + width;
  170. int bottom = top + height;
  171. if (bottom > this.height || right > this.width)
  172. {
  173. throw new System.ArgumentException("The region must fit inside the matrix");
  174. }
  175. for (int y = top; y < bottom; y++)
  176. {
  177. int offset = y * rowSize;
  178. for (int x = left; x < right; x++)
  179. {
  180. bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
  181. }
  182. }
  183. }
  184. /// <summary> A fast method to retrieve one row of data from the matrix as a BitArray.
  185. ///
  186. /// </summary>
  187. /// <param name="y">The row to retrieve
  188. /// </param>
  189. /// <param name="row">An optional caller-allocated BitArray, will be allocated if null or too small
  190. /// </param>
  191. /// <returns> The resulting BitArray - this reference should always be used even when passing
  192. /// your own row
  193. /// </returns>
  194. public BitArray getRow(int y, BitArray row)
  195. {
  196. if (row == null || row.Size < width)
  197. {
  198. row = new BitArray(width);
  199. }
  200. else
  201. {
  202. row.clear();
  203. }
  204. int offset = y * rowSize;
  205. for (int x = 0; x < rowSize; x++)
  206. {
  207. row.setBulk(x << 5, bits[offset + x]);
  208. }
  209. return row;
  210. }
  211. /// <summary>
  212. /// Sets the row.
  213. /// </summary>
  214. /// <param name="y">row to set</param>
  215. /// <param name="row">{@link BitArray} to copy from</param>
  216. public void setRow(int y, BitArray row)
  217. {
  218. Array.Copy(row.Array, 0, bits, y * rowSize, rowSize);
  219. }
  220. /// <summary>
  221. /// Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
  222. /// </summary>
  223. public void rotate180()
  224. {
  225. int width = Width;
  226. int height = Height;
  227. BitArray topRow = new BitArray(width);
  228. BitArray bottomRow = new BitArray(width);
  229. for (int i = 0; i < (height + 1)/2; i++)
  230. {
  231. topRow = getRow(i, topRow);
  232. bottomRow = getRow(height - 1 - i, bottomRow);
  233. topRow.reverse();
  234. bottomRow.reverse();
  235. setRow(i, bottomRow);
  236. setRow(height - 1 - i, topRow);
  237. }
  238. }
  239. /// <summary>
  240. /// This is useful in detecting the enclosing rectangle of a 'pure' barcode.
  241. /// </summary>
  242. /// <returns>{left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white</returns>
  243. public int[] getEnclosingRectangle()
  244. {
  245. int left = width;
  246. int top = height;
  247. int right = -1;
  248. int bottom = -1;
  249. for (int y = 0; y < height; y++)
  250. {
  251. for (int x32 = 0; x32 < rowSize; x32++)
  252. {
  253. int theBits = bits[y * rowSize + x32];
  254. if (theBits != 0)
  255. {
  256. if (y < top)
  257. {
  258. top = y;
  259. }
  260. if (y > bottom)
  261. {
  262. bottom = y;
  263. }
  264. if (x32 * 32 < left)
  265. {
  266. int bit = 0;
  267. while ((theBits << (31 - bit)) == 0)
  268. {
  269. bit++;
  270. }
  271. if ((x32 * 32 + bit) < left)
  272. {
  273. left = x32 * 32 + bit;
  274. }
  275. }
  276. if (x32 * 32 + 31 > right)
  277. {
  278. int bit = 31;
  279. while (((int)((uint)theBits >> bit)) == 0) // (theBits >>> bit)
  280. {
  281. bit--;
  282. }
  283. if ((x32 * 32 + bit) > right)
  284. {
  285. right = x32 * 32 + bit;
  286. }
  287. }
  288. }
  289. }
  290. }
  291. int widthTmp = right - left;
  292. int heightTmp = bottom - top;
  293. if (widthTmp < 0 || heightTmp < 0)
  294. {
  295. return null;
  296. }
  297. return new int[] { left, top, widthTmp, heightTmp };
  298. }
  299. /// <summary>
  300. /// This is useful in detecting a corner of a 'pure' barcode.
  301. /// </summary>
  302. /// <returns>{x,y} coordinate of top-left-most 1 bit, or null if it is all white</returns>
  303. public int[] getTopLeftOnBit()
  304. {
  305. int bitsOffset = 0;
  306. while (bitsOffset < bits.Length && bits[bitsOffset] == 0)
  307. {
  308. bitsOffset++;
  309. }
  310. if (bitsOffset == bits.Length)
  311. {
  312. return null;
  313. }
  314. int y = bitsOffset / rowSize;
  315. int x = (bitsOffset % rowSize) << 5;
  316. int theBits = bits[bitsOffset];
  317. int bit = 0;
  318. while ((theBits << (31 - bit)) == 0)
  319. {
  320. bit++;
  321. }
  322. x += bit;
  323. return new int[] { x, y };
  324. }
  325. public int[] getBottomRightOnBit()
  326. {
  327. int bitsOffset = bits.Length - 1;
  328. while (bitsOffset >= 0 && bits[bitsOffset] == 0)
  329. {
  330. bitsOffset--;
  331. }
  332. if (bitsOffset < 0)
  333. {
  334. return null;
  335. }
  336. int y = bitsOffset / rowSize;
  337. int x = (bitsOffset % rowSize) << 5;
  338. int theBits = bits[bitsOffset];
  339. int bit = 31;
  340. while (((int)((uint)theBits >> bit)) == 0) // (theBits >>> bit)
  341. {
  342. bit--;
  343. }
  344. x += bit;
  345. return new int[] { x, y };
  346. }
  347. public override bool Equals(object obj)
  348. {
  349. if (!(obj is BitMatrix))
  350. {
  351. return false;
  352. }
  353. BitMatrix other = (BitMatrix)obj;
  354. if (width != other.width || height != other.height ||
  355. rowSize != other.rowSize || bits.Length != other.bits.Length)
  356. {
  357. return false;
  358. }
  359. for (int i = 0; i < bits.Length; i++)
  360. {
  361. if (bits[i] != other.bits[i])
  362. {
  363. return false;
  364. }
  365. }
  366. return true;
  367. }
  368. public override int GetHashCode()
  369. {
  370. int hash = width;
  371. hash = 31 * hash + width;
  372. hash = 31 * hash + height;
  373. hash = 31 * hash + rowSize;
  374. foreach (int bit in bits)
  375. {
  376. hash = 31 * hash + bit.GetHashCode();
  377. }
  378. return hash;
  379. }
  380. public override String ToString()
  381. {
  382. System.Text.StringBuilder result = new System.Text.StringBuilder(height * (width + 1));
  383. for (int y = 0; y < height; y++)
  384. {
  385. for (int x = 0; x < width; x++)
  386. {
  387. result.Append(this[x, y] ? "X " : " ");
  388. }
  389. #if WindowsCE
  390. result.Append("\r\n");
  391. #else
  392. result.AppendLine("");
  393. #endif
  394. }
  395. return result.ToString();
  396. }
  397. public object Clone()
  398. {
  399. return new BitMatrix(width, height, rowSize, (int[])bits.Clone());
  400. }
  401. }
  402. }