PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / bacon / bacon-qr-code / src / Common / BitMatrix.php
ameliabooking / vendor / bacon / bacon-qr-code / src / Common Last commit date
BitArray.php 8 months ago BitMatrix.php 8 months ago BitUtils.php 8 months ago CharacterSetEci.php 8 months ago EcBlock.php 8 months ago EcBlocks.php 8 months ago ErrorCorrectionLevel.php 8 months ago FormatInformation.php 8 months ago Mode.php 8 months ago ReedSolomonCodec.php 8 months ago Version.php 8 months ago
BitMatrix.php
314 lines
1 <?php
2 declare(strict_types = 1);
3
4 namespace BaconQrCode\Common;
5
6 use BaconQrCode\Exception\InvalidArgumentException;
7 use SplFixedArray;
8
9 /**
10 * Bit matrix.
11 *
12 * Represents a 2D matrix of bits. In function arguments below, and throughout
13 * the common module, x is the column position, and y is the row position. The
14 * ordering is always x, y. The origin is at the top-left.
15 */
16 class BitMatrix
17 {
18 /**
19 * Width of the bit matrix.
20 *
21 * @var int
22 */
23 private $width;
24
25 /**
26 * Height of the bit matrix.
27 *
28 * @var int
29 */
30 private $height;
31
32 /**
33 * Size in bits of each individual row.
34 *
35 * @var int
36 */
37 private $rowSize;
38
39 /**
40 * Bits representation.
41 *
42 * @var SplFixedArray<int>
43 */
44 private $bits;
45
46 /**
47 * @throws InvalidArgumentException if a dimension is smaller than zero
48 */
49 public function __construct(int $width, int $height = null)
50 {
51 if (null === $height) {
52 $height = $width;
53 }
54
55 if ($width < 1 || $height < 1) {
56 throw new InvalidArgumentException('Both dimensions must be greater than zero');
57 }
58
59 $this->width = $width;
60 $this->height = $height;
61 $this->rowSize = ($width + 31) >> 5;
62 $this->bits = SplFixedArray::fromArray(array_fill(0, $this->rowSize * $height, 0));
63 }
64
65 /**
66 * Gets the requested bit, where true means black.
67 */
68 public function get(int $x, int $y) : bool
69 {
70 $offset = $y * $this->rowSize + ($x >> 5);
71 return 0 !== (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1);
72 }
73
74 /**
75 * Sets the given bit to true.
76 */
77 public function set(int $x, int $y) : void
78 {
79 $offset = $y * $this->rowSize + ($x >> 5);
80 $this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f));
81 }
82
83 /**
84 * Flips the given bit.
85 */
86 public function flip(int $x, int $y) : void
87 {
88 $offset = $y * $this->rowSize + ($x >> 5);
89 $this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f));
90 }
91
92 /**
93 * Clears all bits (set to false).
94 */
95 public function clear() : void
96 {
97 $max = count($this->bits);
98
99 for ($i = 0; $i < $max; ++$i) {
100 $this->bits[$i] = 0;
101 }
102 }
103
104 /**
105 * Sets a square region of the bit matrix to true.
106 *
107 * @throws InvalidArgumentException if left or top are negative
108 * @throws InvalidArgumentException if width or height are smaller than 1
109 * @throws InvalidArgumentException if region does not fit into the matix
110 */
111 public function setRegion(int $left, int $top, int $width, int $height) : void
112 {
113 if ($top < 0 || $left < 0) {
114 throw new InvalidArgumentException('Left and top must be non-negative');
115 }
116
117 if ($height < 1 || $width < 1) {
118 throw new InvalidArgumentException('Width and height must be at least 1');
119 }
120
121 $right = $left + $width;
122 $bottom = $top + $height;
123
124 if ($bottom > $this->height || $right > $this->width) {
125 throw new InvalidArgumentException('The region must fit inside the matrix');
126 }
127
128 for ($y = $top; $y < $bottom; ++$y) {
129 $offset = $y * $this->rowSize;
130
131 for ($x = $left; $x < $right; ++$x) {
132 $index = $offset + ($x >> 5);
133 $this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f));
134 }
135 }
136 }
137
138 /**
139 * A fast method to retrieve one row of data from the matrix as a BitArray.
140 */
141 public function getRow(int $y, BitArray $row = null) : BitArray
142 {
143 if (null === $row || $row->getSize() < $this->width) {
144 $row = new BitArray($this->width);
145 }
146
147 $offset = $y * $this->rowSize;
148
149 for ($x = 0; $x < $this->rowSize; ++$x) {
150 $row->setBulk($x << 5, $this->bits[$offset + $x]);
151 }
152
153 return $row;
154 }
155
156 /**
157 * Sets a row of data from a BitArray.
158 */
159 public function setRow(int $y, BitArray $row) : void
160 {
161 $bits = $row->getBitArray();
162
163 for ($i = 0; $i < $this->rowSize; ++$i) {
164 $this->bits[$y * $this->rowSize + $i] = $bits[$i];
165 }
166 }
167
168 /**
169 * This is useful in detecting the enclosing rectangle of a 'pure' barcode.
170 *
171 * @return int[]|null
172 */
173 public function getEnclosingRectangle() : ?array
174 {
175 $left = $this->width;
176 $top = $this->height;
177 $right = -1;
178 $bottom = -1;
179
180 for ($y = 0; $y < $this->height; ++$y) {
181 for ($x32 = 0; $x32 < $this->rowSize; ++$x32) {
182 $bits = $this->bits[$y * $this->rowSize + $x32];
183
184 if (0 !== $bits) {
185 if ($y < $top) {
186 $top = $y;
187 }
188
189 if ($y > $bottom) {
190 $bottom = $y;
191 }
192
193 if ($x32 * 32 < $left) {
194 $bit = 0;
195
196 while (($bits << (31 - $bit)) === 0) {
197 $bit++;
198 }
199
200 if (($x32 * 32 + $bit) < $left) {
201 $left = $x32 * 32 + $bit;
202 }
203 }
204 }
205
206 if ($x32 * 32 + 31 > $right) {
207 $bit = 31;
208
209 while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
210 --$bit;
211 }
212
213 if (($x32 * 32 + $bit) > $right) {
214 $right = $x32 * 32 + $bit;
215 }
216 }
217 }
218 }
219
220 $width = $right - $left;
221 $height = $bottom - $top;
222
223 if ($width < 0 || $height < 0) {
224 return null;
225 }
226
227 return [$left, $top, $width, $height];
228 }
229
230 /**
231 * Gets the most top left set bit.
232 *
233 * This is useful in detecting a corner of a 'pure' barcode.
234 *
235 * @return int[]|null
236 */
237 public function getTopLeftOnBit() : ?array
238 {
239 $bitsOffset = 0;
240
241 while ($bitsOffset < count($this->bits) && 0 === $this->bits[$bitsOffset]) {
242 ++$bitsOffset;
243 }
244
245 if (count($this->bits) === $bitsOffset) {
246 return null;
247 }
248
249 $x = intdiv($bitsOffset, $this->rowSize);
250 $y = ($bitsOffset % $this->rowSize) << 5;
251
252 $bits = $this->bits[$bitsOffset];
253 $bit = 0;
254
255 while (0 === ($bits << (31 - $bit))) {
256 ++$bit;
257 }
258
259 $x += $bit;
260
261 return [$x, $y];
262 }
263
264 /**
265 * Gets the most bottom right set bit.
266 *
267 * This is useful in detecting a corner of a 'pure' barcode.
268 *
269 * @return int[]|null
270 */
271 public function getBottomRightOnBit() : ?array
272 {
273 $bitsOffset = count($this->bits) - 1;
274
275 while ($bitsOffset >= 0 && 0 === $this->bits[$bitsOffset]) {
276 --$bitsOffset;
277 }
278
279 if ($bitsOffset < 0) {
280 return null;
281 }
282
283 $x = intdiv($bitsOffset, $this->rowSize);
284 $y = ($bitsOffset % $this->rowSize) << 5;
285
286 $bits = $this->bits[$bitsOffset];
287 $bit = 0;
288
289 while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
290 --$bit;
291 }
292
293 $x += $bit;
294
295 return [$x, $y];
296 }
297
298 /**
299 * Gets the width of the matrix,
300 */
301 public function getWidth() : int
302 {
303 return $this->width;
304 }
305
306 /**
307 * Gets the height of the matrix.
308 */
309 public function getHeight() : int
310 {
311 return $this->height;
312 }
313 }
314