1 module qrcode.common.bitmatrix;
2
3 import qrcode.common.bitutils;
4 import qrcode.common.bitarray;
5
6 /**
7 * Bit matrix.
8 *
9 * Represents a 2D matrix of bits. In function arguments below, and throughout
10 * the common module, x is the column position, and y is the row position. The
11 * ordering is always x, y. The origin is at the top-left.
12 */
13 class BitMatrix
14 {
15
16 private
17 {
18 int width, height, rowSize;
19 BitArrayBitType[] bits;
20 }
21
22 public @property int Width()
23 {
24 return this.width;
25 }
26
27 public @property int Height()
28 {
29 return this.height;
30 }
31
32 public @property int RowSize()
33 {
34 return this.rowSize;
35 }
36
37 public @property BitArrayBitType[] Bits()
38 {
39 return this.bits;
40 }
41
42 this(int width)
43 {
44 this(width, width);
45 }
46
47 this(int width, int height)
48 {
49 if (width < 1 || height < 1)
50 {
51 throw new Exception("Both dimensions must be greater than zero");
52 }
53 this.width = width;
54 this.height = height;
55 this.rowSize = (width + 31) >> 5;
56 this.bits = new BitArrayBitType[rowSize * height];
57 }
58
59 /**
60 * Gets the requested bit, where true means black.
61 *
62 * @param integer $x
63 * @param integer $y
64 * @return boolean
65 */
66 public bool get(int x, int y)
67 {
68 auto offset = y * this.rowSize + (x >> 5);
69 return (BitUtils.unsignedRightShift(this.bits[offset], (x & 0x1f)) & 1) != 0;
70 }
71 /**
72 * Sets the given bit to true.
73 *
74 * @param integer $x
75 * @param integer $y
76 * @return void
77 */
78 public void set(int x, int y)
79 {
80 auto offset = y * this.rowSize + (x >> 5);
81 this.bits[offset] = this.bits[offset] | (1 << (x & 0x1f));
82 }
83
84 /**
85 * Flips the given bit.
86 *
87 * @param integer x
88 * @param integer y
89 * @return void
90 */
91 public void flip(int x, int y)
92 {
93 auto offset = y * this.rowSize + (x >> 5);
94 this.bits[offset] = this.bits[offset] ^ (1 << (x & 0x1f));
95 }
96 /**
97 * Clears all bits (set to false).
98 *
99 * @return void
100 */
101 public void clear()
102 {
103 this.bits[] = 0;
104 }
105 /**
106 * Sets a square region of the bit matrix to true.
107 *
108 * @param integer left
109 * @param integer top
110 * @param integer width
111 * @param integer height
112 * @return void
113 */
114 public void setRegion(int left, int top, int width, int height)
115 {
116 if (top < 0 || left < 0)
117 {
118 throw new Exception("Left and top must be non-negative");
119 }
120 if (height < 1 || width < 1)
121 {
122 throw new Exception("Width and height must be at least 1");
123 }
124 auto right = left + width;
125 auto bottom = top + height;
126 if (bottom > this.height || right > this.width)
127 {
128 throw new Exception("The region must fit inside the matrix");
129 }
130 for (int y = top; y < bottom; y++)
131 {
132 auto offset = y * this.rowSize;
133 for (auto x = left; x < right; x++)
134 {
135 auto index = offset + (x >> 5);
136 this.bits[index] = this.bits[index] | (1 << (x & 0x1f));
137 }
138 }
139 }
140 /**
141 * A fast method to retrieve one row of data from the matrix as a BitArray.
142 *
143 * @param integer y
144 * @param BitArray row
145 * @return BitArray
146 */
147 public BitArray getRow(int y, BitArray row)
148 {
149 if (row is null || row.getSize() < this.width)
150 {
151 row = new BitArray(this.width);
152 }
153 auto offset = y * this.rowSize;
154 for (auto x = 0; x < this.rowSize; x++)
155 {
156 row.setBulk(x << 5, this.bits[offset + x]);
157 }
158 return row;
159 }
160
161 public BitArray getRow(int y)
162 {
163 return getRow(y, new BitArray(this.width));
164 }
165
166 /**
167 * Sets a row of data from a BitArray.
168 *
169 * @param integer $y
170 * @param BitArray $row
171 * @return void
172 */
173 public void setRow(int y, BitArray row)
174 {
175 auto bits = row.getBitArray();
176 for (auto i = 0; i < this.rowSize; i++)
177 {
178 this.bits[y * this.rowSize + i] = bits[i];
179 }
180 }
181
182 /**
183 * This is useful in detecting the enclosing rectangle of a 'pure' barcode.
184 *
185 * @return SplFixedArray
186 */
187 public int[] getEnclosingRectangle()
188 {
189 auto left = this.width;
190 auto top = this.height;
191 auto right = -1;
192 auto bottom = -1;
193 for (auto y = 0; y < this.height; y++)
194 {
195 for (auto x32 = 0; x32 < this.rowSize; x32++)
196 {
197 auto _bits = this.bits[y * this.rowSize + x32];
198 if (_bits != 0)
199 {
200 if (y < top)
201 {
202 top = y;
203 }
204 if (y > bottom)
205 {
206 bottom = y;
207 }
208 if (x32 * 32 < left)
209 {
210 auto bit = 0;
211 while ((_bits << (31 - bit)) == 0)
212 {
213 bit++;
214 }
215 if ((x32 * 32 + bit) < left)
216 {
217 left = x32 * 32 + bit;
218 }
219 }
220 }
221 if (x32 * 32 + 31 > right)
222 {
223 auto bit = 31;
224 while (BitUtils.unsignedRightShift(_bits, bit) == 0)
225 {
226 bit--;
227 }
228 if ((x32 * 32 + bit) > right)
229 {
230 right = x32 * 32 + bit;
231 }
232 }
233 }
234 }
235 width = right - left;
236 height = bottom - top;
237 if (width < 0 || height < 0)
238 {
239 return null;
240 }
241 return [left, top, width, height];
242 }
243 /**
244 * Gets the most top left set bit.
245 *
246 * This is useful in detecting a corner of a 'pure' barcode.
247 *
248 * @return SplFixedArray
249 */
250 public int[] getTopLeftOnBit()
251 {
252 auto bitsOffset = 0;
253 while (bitsOffset < this.bits.length && this.bits[bitsOffset] == 0)
254 {
255 bitsOffset++;
256 }
257 if (bitsOffset == this.bits.length)
258 {
259 return null;
260 }
261 import std.conv;
262
263 auto x = to!int(bitsOffset / this.rowSize);
264 auto y = (bitsOffset % this.rowSize) << 5;
265 auto _bits = this.bits[bitsOffset];
266 auto bit = 0;
267 while ((_bits << (31 - bit)) == 0)
268 {
269 bit++;
270 }
271 x += bit;
272 return [x, y];
273 }
274 /**
275 * Gets the most bottom right set bit.
276 *
277 * This is useful in detecting a corner of a 'pure' barcode.
278 *
279 * @return SplFixedArray
280 */
281 public int[] getBottomRightOnBit()
282 {
283 auto bitsOffset = this.bits.length - 1;
284 while (bitsOffset >= 0 && this.bits[bitsOffset] == 0)
285 {
286 bitsOffset--;
287 }
288 if (bitsOffset < 0)
289 {
290 return null;
291 }
292 import std.conv;
293
294 int x = to!int(bitsOffset / this.rowSize);
295 int y = to!int((bitsOffset % this.rowSize) << 5);
296 auto _bits = this.bits[bitsOffset];
297 auto bit = 0;
298 while (BitUtils.unsignedRightShift(_bits, bit) == 0)
299 {
300 bit--;
301 }
302 x += bit;
303 return [x, y];
304 }
305
306 }