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.

BitMatrix.cs 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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 ZXing.Common
  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. public 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. private 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. /// <summary> <p>Gets the requested bit, where true means black.</p>
  96. ///
  97. /// </summary>
  98. /// <param name="x">The horizontal component (i.e. which column)
  99. /// </param>
  100. /// <param name="y">The vertical component (i.e. which row)
  101. /// </param>
  102. /// <returns> value of given bit in matrix
  103. /// </returns>
  104. public bool this[int x, int y]
  105. {
  106. get
  107. {
  108. int offset = y * rowSize + (x >> 5);
  109. return (((int)((uint)(bits[offset]) >> (x & 0x1f))) & 1) != 0;
  110. }
  111. set
  112. {
  113. if (value)
  114. {
  115. int offset = y * rowSize + (x >> 5);
  116. bits[offset] |= 1 << (x & 0x1f);
  117. }
  118. }
  119. }
  120. /// <summary> <p>Flips the given bit.</p>
  121. ///
  122. /// </summary>
  123. /// <param name="x">The horizontal component (i.e. which column)
  124. /// </param>
  125. /// <param name="y">The vertical component (i.e. which row)
  126. /// </param>
  127. public void flip(int x, int y)
  128. {
  129. int offset = y * rowSize + (x >> 5);
  130. bits[offset] ^= 1 << (x & 0x1f);
  131. }
  132. /// <summary> Clears all bits (sets to false).</summary>
  133. public void clear()
  134. {
  135. int max = bits.Length;
  136. for (int i = 0; i < max; i++)
  137. {
  138. bits[i] = 0;
  139. }
  140. }
  141. /// <summary> <p>Sets a square region of the bit matrix to true.</p>
  142. ///
  143. /// </summary>
  144. /// <param name="left">The horizontal position to begin at (inclusive)
  145. /// </param>
  146. /// <param name="top">The vertical position to begin at (inclusive)
  147. /// </param>
  148. /// <param name="width">The width of the region
  149. /// </param>
  150. /// <param name="height">The height of the region
  151. /// </param>
  152. public void setRegion(int left, int top, int width, int height)
  153. {
  154. if (top < 0 || left < 0)
  155. {
  156. throw new System.ArgumentException("Left and top must be nonnegative");
  157. }
  158. if (height < 1 || width < 1)
  159. {
  160. throw new System.ArgumentException("Height and width must be at least 1");
  161. }
  162. int right = left + width;
  163. int bottom = top + height;
  164. if (bottom > this.height || right > this.width)
  165. {
  166. throw new System.ArgumentException("The region must fit inside the matrix");
  167. }
  168. for (int y = top; y < bottom; y++)
  169. {
  170. int offset = y * rowSize;
  171. for (int x = left; x < right; x++)
  172. {
  173. bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
  174. }
  175. }
  176. }
  177. /// <summary> A fast method to retrieve one row of data from the matrix as a BitArray.
  178. ///
  179. /// </summary>
  180. /// <param name="y">The row to retrieve
  181. /// </param>
  182. /// <param name="row">An optional caller-allocated BitArray, will be allocated if null or too small
  183. /// </param>
  184. /// <returns> The resulting BitArray - this reference should always be used even when passing
  185. /// your own row
  186. /// </returns>
  187. public BitArray getRow(int y, BitArray row)
  188. {
  189. if (row == null || row.Size < width)
  190. {
  191. row = new BitArray(width);
  192. }
  193. else
  194. {
  195. row.clear();
  196. }
  197. int offset = y * rowSize;
  198. for (int x = 0; x < rowSize; x++)
  199. {
  200. row.setBulk(x << 5, bits[offset + x]);
  201. }
  202. return row;
  203. }
  204. /// <summary>
  205. /// Sets the row.
  206. /// </summary>
  207. /// <param name="y">row to set</param>
  208. /// <param name="row">{@link BitArray} to copy from</param>
  209. public void setRow(int y, BitArray row)
  210. {
  211. Array.Copy(row.Array, 0, bits, y * rowSize, rowSize);
  212. }
  213. /// <summary>
  214. /// Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
  215. /// </summary>
  216. public void rotate180()
  217. {
  218. var width = Width;
  219. var height = Height;
  220. var topRow = new BitArray(width);
  221. var bottomRow = new BitArray(width);
  222. for (int i = 0; i < (height + 1)/2; i++)
  223. {
  224. topRow = getRow(i, topRow);
  225. bottomRow = getRow(height - 1 - i, bottomRow);
  226. topRow.reverse();
  227. bottomRow.reverse();
  228. setRow(i, bottomRow);
  229. setRow(height - 1 - i, topRow);
  230. }
  231. }
  232. /// <summary>
  233. /// This is useful in detecting the enclosing rectangle of a 'pure' barcode.
  234. /// </summary>
  235. /// <returns>{left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white</returns>
  236. public int[] getEnclosingRectangle()
  237. {
  238. int left = width;
  239. int top = height;
  240. int right = -1;
  241. int bottom = -1;
  242. for (int y = 0; y < height; y++)
  243. {
  244. for (int x32 = 0; x32 < rowSize; x32++)
  245. {
  246. int theBits = bits[y * rowSize + x32];
  247. if (theBits != 0)
  248. {
  249. if (y < top)
  250. {
  251. top = y;
  252. }
  253. if (y > bottom)
  254. {
  255. bottom = y;
  256. }
  257. if (x32 * 32 < left)
  258. {
  259. int bit = 0;
  260. while ((theBits << (31 - bit)) == 0)
  261. {
  262. bit++;
  263. }
  264. if ((x32 * 32 + bit) < left)
  265. {
  266. left = x32 * 32 + bit;
  267. }
  268. }
  269. if (x32 * 32 + 31 > right)
  270. {
  271. int bit = 31;
  272. while (((int)((uint)theBits >> bit)) == 0) // (theBits >>> bit)
  273. {
  274. bit--;
  275. }
  276. if ((x32 * 32 + bit) > right)
  277. {
  278. right = x32 * 32 + bit;
  279. }
  280. }
  281. }
  282. }
  283. }
  284. int widthTmp = right - left;
  285. int heightTmp = bottom - top;
  286. if (widthTmp < 0 || heightTmp < 0)
  287. {
  288. return null;
  289. }
  290. return new [] { left, top, widthTmp, heightTmp };
  291. }
  292. /// <summary>
  293. /// This is useful in detecting a corner of a 'pure' barcode.
  294. /// </summary>
  295. /// <returns>{x,y} coordinate of top-left-most 1 bit, or null if it is all white</returns>
  296. public int[] getTopLeftOnBit()
  297. {
  298. int bitsOffset = 0;
  299. while (bitsOffset < bits.Length && bits[bitsOffset] == 0)
  300. {
  301. bitsOffset++;
  302. }
  303. if (bitsOffset == bits.Length)
  304. {
  305. return null;
  306. }
  307. int y = bitsOffset / rowSize;
  308. int x = (bitsOffset % rowSize) << 5;
  309. int theBits = bits[bitsOffset];
  310. int bit = 0;
  311. while ((theBits << (31 - bit)) == 0)
  312. {
  313. bit++;
  314. }
  315. x += bit;
  316. return new[] { x, y };
  317. }
  318. public int[] getBottomRightOnBit()
  319. {
  320. int bitsOffset = bits.Length - 1;
  321. while (bitsOffset >= 0 && bits[bitsOffset] == 0)
  322. {
  323. bitsOffset--;
  324. }
  325. if (bitsOffset < 0)
  326. {
  327. return null;
  328. }
  329. int y = bitsOffset / rowSize;
  330. int x = (bitsOffset % rowSize) << 5;
  331. int theBits = bits[bitsOffset];
  332. int bit = 31;
  333. while (((int)((uint)theBits >> bit)) == 0) // (theBits >>> bit)
  334. {
  335. bit--;
  336. }
  337. x += bit;
  338. return new int[] { x, y };
  339. }
  340. public override bool Equals(object obj)
  341. {
  342. if (!(obj is BitMatrix))
  343. {
  344. return false;
  345. }
  346. BitMatrix other = (BitMatrix)obj;
  347. if (width != other.width || height != other.height ||
  348. rowSize != other.rowSize || bits.Length != other.bits.Length)
  349. {
  350. return false;
  351. }
  352. for (int i = 0; i < bits.Length; i++)
  353. {
  354. if (bits[i] != other.bits[i])
  355. {
  356. return false;
  357. }
  358. }
  359. return true;
  360. }
  361. public override int GetHashCode()
  362. {
  363. int hash = width;
  364. hash = 31 * hash + width;
  365. hash = 31 * hash + height;
  366. hash = 31 * hash + rowSize;
  367. foreach (var bit in bits)
  368. {
  369. hash = 31 * hash + bit.GetHashCode();
  370. }
  371. return hash;
  372. }
  373. public override String ToString()
  374. {
  375. var result = new System.Text.StringBuilder(height * (width + 1));
  376. for (int y = 0; y < height; y++)
  377. {
  378. for (int x = 0; x < width; x++)
  379. {
  380. result.Append(this[x, y] ? "X " : " ");
  381. }
  382. #if WindowsCE
  383. result.Append("\r\n");
  384. #else
  385. result.AppendLine("");
  386. #endif
  387. }
  388. return result.ToString();
  389. }
  390. public object Clone()
  391. {
  392. return new BitMatrix(width, height, rowSize, (int[])bits.Clone());
  393. }
  394. }
  395. }