PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / vendor_prefixed / chillerlan / php-qrcode / src / Data / QRMatrix.php

QRMatrix.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at vendor_prefixed/chillerlan/php-qrcode/src/Data/QRMatrix.php

683 lines 22.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class QRMatrix
5 *
6 * @created 15.11.2017
7 * @author Smiley <[email protected]>
8 * @copyright 2017 Smiley
9 * @license MIT
10 */
11 namespace WCPOS\Vendor\chillerlan\QRCode\Data;
12
13 use WCPOS\Vendor\chillerlan\QRCode\Common\BitBuffer;
14 use WCPOS\Vendor\chillerlan\QRCode\Common\EccLevel;
15 use WCPOS\Vendor\chillerlan\QRCode\Common\MaskPattern;
16 use WCPOS\Vendor\chillerlan\QRCode\Common\Version;
17 use function array_fill, array_map, array_reverse, count, intdiv;
18 /**
19 * Holds an array representation of the final QR Code that contains numerical values for later output modifications;
20 * maps the ECC coded binary data and applies the mask pattern
21 *
22 * @see http://www.thonky.com/qr-code-tutorial/format-version-information
23 */
24 class QRMatrix
25 {
26 /*
27 * special values
28 */
29 /** @var int */
30 public const IS_DARK = 0b100000000000;
31 /** @var int */
32 public const M_NULL = 0b0;
33 /** @var int */
34 public const M_LOGO = 0b1000000000;
35 /** @var int */
36 public const M_LOGO_DARK = 0b101000000000;
37 /*
38 * light values
39 */
40 /** @var int */
41 public const M_DATA = 0b10;
42 /** @var int */
43 public const M_FINDER = 0b100;
44 /** @var int */
45 public const M_SEPARATOR = 0b1000;
46 /** @var int */
47 public const M_ALIGNMENT = 0b10000;
48 /** @var int */
49 public const M_TIMING = 0b100000;
50 /** @var int */
51 public const M_FORMAT = 0b1000000;
52 /** @var int */
53 public const M_VERSION = 0b10000000;
54 /** @var int */
55 public const M_QUIETZONE = 0b100000000;
56 /*
57 * dark values
58 */
59 /** @var int */
60 public const M_DARKMODULE = 0b100000000001;
61 /** @var int */
62 public const M_DATA_DARK = 0b100000000010;
63 /** @var int */
64 public const M_FINDER_DARK = 0b100000000100;
65 /** @var int */
66 public const M_ALIGNMENT_DARK = 0b100000010000;
67 /** @var int */
68 public const M_TIMING_DARK = 0b100000100000;
69 /** @var int */
70 public const M_FORMAT_DARK = 0b100001000000;
71 /** @var int */
72 public const M_VERSION_DARK = 0b100010000000;
73 /** @var int */
74 public const M_FINDER_DOT = 0b110000000000;
75 /*
76 * values used for reversed reflectance
77 */
78 /** @var int */
79 public const M_DARKMODULE_LIGHT = 0b1;
80 /** @var int */
81 public const M_FINDER_DOT_LIGHT = 0b10000000000;
82 /** @var int */
83 public const M_SEPARATOR_DARK = 0b100000001000;
84 /** @var int */
85 public const M_QUIETZONE_DARK = 0b100100000000;
86 /**
87 * Map of flag => coord
88 *
89 * @see \chillerlan\QRCode\Data\QRMatrix::checkNeighbours()
90 *
91 * @var array
92 */
93 protected const neighbours = [0b1 => [-1, -1], 0b10 => [0, -1], 0b100 => [1, -1], 0b1000 => [1, 0], 0b10000 => [1, 1], 0b100000 => [0, 1], 0b1000000 => [-1, 1], 0b10000000 => [-1, 0]];
94 /**
95 * the matrix version - always set in QRMatrix, may be null in BitMatrix
96 */
97 protected ?Version $version = null;
98 /**
99 * the current ECC level - always set in QRMatrix, may be null in BitMatrix
100 */
101 protected ?EccLevel $eccLevel = null;
102 /**
103 * the mask pattern that was used in the most recent operation, set via:
104 *
105 * - QRMatrix::setFormatInfo()
106 * - QRMatrix::mask()
107 * - BitMatrix::readFormatInformation()
108 */
109 protected ?MaskPattern $maskPattern = null;
110 /**
111 * the size (side length) of the matrix, including quiet zone (if created)
112 */
113 protected int $moduleCount;
114 /**
115 * the actual matrix data array
116 *
117 * @var int[][]
118 */
119 protected array $matrix;
120 /**
121 * QRMatrix constructor.
122 */
123 public function __construct(Version $version, EccLevel $eccLevel)
124 {
125 $this->version = $version;
126 $this->eccLevel = $eccLevel;
127 $this->moduleCount = $this->version->getDimension();
128 $this->matrix = $this->createMatrix($this->moduleCount, $this::M_NULL);
129 }
130 /**
131 * Creates a 2-dimensional array (square) of the given $size
132 */
133 protected function createMatrix(int $size, int $value) : array
134 {
135 return array_fill(0, $size, array_fill(0, $size, $value));
136 }
137 /**
138 * shortcut to initialize the functional patterns
139 */
140 public function initFunctionalPatterns() : self
141 {
142 return $this->setFinderPattern()->setSeparators()->setAlignmentPattern()->setTimingPattern()->setDarkModule()->setVersionNumber()->setFormatInfo();
143 }
144 /**
145 * Returns the data matrix, returns a pure boolean representation if $boolean is set to true
146 *
147 * @return int[][]|bool[][]
148 */
149 public function getMatrix(?bool $boolean = null) : array
150 {
151 if ($boolean !== \true) {
152 return $this->matrix;
153 }
154 $matrix = $this->matrix;
155 foreach ($matrix as &$row) {
156 $row = array_map([$this, 'isDark'], $row);
157 }
158 return $matrix;
159 }
160 /**
161 * @deprecated 5.0.0 use QRMatrix::getMatrix() instead
162 * @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
163 * @codeCoverageIgnore
164 */
165 public function matrix(?bool $boolean = null) : array
166 {
167 return $this->getMatrix($boolean);
168 }
169 /**
170 * Returns the current version number
171 */
172 public function getVersion() : ?Version
173 {
174 return $this->version;
175 }
176 /**
177 * @deprecated 5.0.0 use QRMatrix::getVersion() instead
178 * @see \chillerlan\QRCode\Data\QRMatrix::getVersion()
179 * @codeCoverageIgnore
180 */
181 public function version() : ?Version
182 {
183 return $this->getVersion();
184 }
185 /**
186 * Returns the current ECC level
187 */
188 public function getEccLevel() : ?EccLevel
189 {
190 return $this->eccLevel;
191 }
192 /**
193 * @deprecated 5.0.0 use QRMatrix::getEccLevel() instead
194 * @see \chillerlan\QRCode\Data\QRMatrix::getEccLevel()
195 * @codeCoverageIgnore
196 */
197 public function eccLevel() : ?EccLevel
198 {
199 return $this->getEccLevel();
200 }
201 /**
202 * Returns the current mask pattern
203 */
204 public function getMaskPattern() : ?MaskPattern
205 {
206 return $this->maskPattern;
207 }
208 /**
209 * @deprecated 5.0.0 use QRMatrix::getMaskPattern() instead
210 * @see \chillerlan\QRCode\Data\QRMatrix::getMaskPattern()
211 * @codeCoverageIgnore
212 */
213 public function maskPattern() : ?MaskPattern
214 {
215 return $this->getMaskPattern();
216 }
217 /**
218 * Returns the absoulute size of the matrix, including quiet zone (after setting it).
219 *
220 * size = version * 4 + 17 [ + 2 * quietzone size]
221 */
222 public function getSize() : int
223 {
224 return $this->moduleCount;
225 }
226 /**
227 * @deprecated 5.0.0 use QRMatrix::getSize() instead
228 * @see \chillerlan\QRCode\Data\QRMatrix::getSize()
229 * @codeCoverageIgnore
230 */
231 public function size() : int
232 {
233 return $this->getSize();
234 }
235 /**
236 * Returns the value of the module at position [$x, $y] or -1 if the coordinate is outside the matrix
237 */
238 public function get(int $x, int $y) : int
239 {
240 if (!isset($this->matrix[$y][$x])) {
241 return -1;
242 }
243 return $this->matrix[$y][$x];
244 }
245 /**
246 * Sets the $M_TYPE value for the module at position [$x, $y]
247 *
248 * true => $M_TYPE | 0x800
249 * false => $M_TYPE
250 */
251 public function set(int $x, int $y, bool $value, int $M_TYPE) : self
252 {
253 if (isset($this->matrix[$y][$x])) {
254 // we don't know whether the input is dark, so we remove the dark bit
255 $M_TYPE &= ~$this::IS_DARK;
256 if ($value === \true) {
257 $M_TYPE |= $this::IS_DARK;
258 }
259 $this->matrix[$y][$x] = $M_TYPE;
260 }
261 return $this;
262 }
263 /**
264 * Fills an area of $width * $height, from the given starting point [$startX, $startY] (top left) with $value for $M_TYPE.
265 */
266 public function setArea(int $startX, int $startY, int $width, int $height, bool $value, int $M_TYPE) : self
267 {
268 for ($y = $startY; $y < $startY + $height; $y++) {
269 for ($x = $startX; $x < $startX + $width; $x++) {
270 $this->set($x, $y, $value, $M_TYPE);
271 }
272 }
273 return $this;
274 }
275 /**
276 * Flips the value of the module at ($x, $y)
277 */
278 public function flip(int $x, int $y) : self
279 {
280 if (isset($this->matrix[$y][$x])) {
281 $this->matrix[$y][$x] ^= $this::IS_DARK;
282 }
283 return $this;
284 }
285 /**
286 * Checks whether the module at ($x, $y) is of the given $M_TYPE
287 *
288 * true => $value & $M_TYPE === $M_TYPE
289 *
290 * Also, returns false if the given coordinates are out of range.
291 */
292 public function checkType(int $x, int $y, int $M_TYPE) : bool
293 {
294 if (isset($this->matrix[$y][$x])) {
295 return ($this->matrix[$y][$x] & $M_TYPE) === $M_TYPE;
296 }
297 return \false;
298 }
299 /**
300 * Checks whether the module at ($x, $y) is in the given array of $M_TYPES,
301 * returns true if a match is found, otherwise false.
302 */
303 public function checkTypeIn(int $x, int $y, array $M_TYPES) : bool
304 {
305 foreach ($M_TYPES as $type) {
306 if ($this->checkType($x, $y, $type)) {
307 return \true;
308 }
309 }
310 return \false;
311 }
312 /**
313 * Checks whether the module at ($x, $y) is true (dark) or false (light)
314 *
315 * Also, returns false if the given coordinates are out of range.
316 */
317 public function check(int $x, int $y) : bool
318 {
319 if (isset($this->matrix[$y][$x])) {
320 return $this->isDark($this->matrix[$y][$x]);
321 }
322 return \false;
323 }
324 /**
325 * Checks whether the given $M_TYPE is a dark value
326 */
327 public function isDark(int $M_TYPE) : bool
328 {
329 return ($M_TYPE & $this::IS_DARK) === $this::IS_DARK;
330 }
331 /**
332 * Checks the status of the neighbouring modules for the module at ($x, $y) and returns a bitmask with the results.
333 *
334 * The 8 flags of the bitmask represent the status of each of the neighbouring fields,
335 * starting with the lowest bit for top left, going clockwise:
336 *
337 * 0 1 2
338 * 7 # 3
339 * 6 5 4
340 */
341 public function checkNeighbours(int $x, int $y, ?int $M_TYPE = null) : int
342 {
343 $bits = 0;
344 foreach ($this::neighbours as $bit => [$ix, $iy]) {
345 $ix += $x;
346 $iy += $y;
347 // $M_TYPE is given, skip if the field is not the same type
348 if ($M_TYPE !== null && !$this->checkType($ix, $iy, $M_TYPE)) {
349 continue;
350 }
351 if ($this->checkType($ix, $iy, $this::IS_DARK)) {
352 $bits |= $bit;
353 }
354 }
355 return $bits;
356 }
357 /**
358 * Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
359 *
360 * 4 * version + 9 or moduleCount - 8
361 */
362 public function setDarkModule() : self
363 {
364 $this->set(8, $this->moduleCount - 8, \true, $this::M_DARKMODULE);
365 return $this;
366 }
367 /**
368 * Draws the 7x7 finder patterns in the corners top left/right and bottom left
369 *
370 * ISO/IEC 18004:2000 Section 7.3.2
371 */
372 public function setFinderPattern() : self
373 {
374 $pos = [
375 [0, 0],
376 // top left
377 [$this->moduleCount - 7, 0],
378 // top right
379 [0, $this->moduleCount - 7],
380 ];
381 foreach ($pos as $c) {
382 $this->setArea($c[0], $c[1], 7, 7, \true, $this::M_FINDER)->setArea($c[0] + 1, $c[1] + 1, 5, 5, \false, $this::M_FINDER)->setArea($c[0] + 2, $c[1] + 2, 3, 3, \true, $this::M_FINDER_DOT);
383 }
384 return $this;
385 }
386 /**
387 * Draws the separator lines around the finder patterns
388 *
389 * ISO/IEC 18004:2000 Section 7.3.3
390 */
391 public function setSeparators() : self
392 {
393 $h = [[7, 0], [$this->moduleCount - 8, 0], [7, $this->moduleCount - 8]];
394 $v = [[7, 7], [$this->moduleCount - 1, 7], [7, $this->moduleCount - 8]];
395 for ($c = 0; $c < 3; $c++) {
396 for ($i = 0; $i < 8; $i++) {
397 // phpcs:ignore
398 $this->set($h[$c][0], $h[$c][1] + $i, \false, $this::M_SEPARATOR);
399 $this->set($v[$c][0] - $i, $v[$c][1], \false, $this::M_SEPARATOR);
400 }
401 }
402 return $this;
403 }
404 /**
405 * Draws the 5x5 alignment patterns
406 *
407 * ISO/IEC 18004:2000 Section 7.3.5
408 */
409 public function setAlignmentPattern() : self
410 {
411 $alignmentPattern = $this->version->getAlignmentPattern();
412 foreach ($alignmentPattern as $y) {
413 foreach ($alignmentPattern as $x) {
414 // skip existing patterns
415 if ($this->matrix[$y][$x] !== $this::M_NULL) {
416 continue;
417 }
418 $this->setArea($x - 2, $y - 2, 5, 5, \true, $this::M_ALIGNMENT)->setArea($x - 1, $y - 1, 3, 3, \false, $this::M_ALIGNMENT)->set($x, $y, \true, $this::M_ALIGNMENT);
419 }
420 }
421 return $this;
422 }
423 /**
424 * Draws the timing pattern (h/v checkered line between the finder patterns)
425 *
426 * ISO/IEC 18004:2000 Section 7.3.4
427 */
428 public function setTimingPattern() : self
429 {
430 for ($i = 8; $i < $this->moduleCount - 8; $i++) {
431 if ($this->matrix[6][$i] !== $this::M_NULL || $this->matrix[$i][6] !== $this::M_NULL) {
432 continue;
433 }
434 $v = $i % 2 === 0;
435 $this->set($i, 6, $v, $this::M_TIMING);
436 // h
437 $this->set(6, $i, $v, $this::M_TIMING);
438 // v
439 }
440 return $this;
441 }
442 /**
443 * Draws the version information, 2x 3x6 pixel
444 *
445 * ISO/IEC 18004:2000 Section 8.10
446 */
447 public function setVersionNumber() : self
448 {
449 $bits = $this->version->getVersionPattern();
450 if ($bits !== null) {
451 for ($i = 0; $i < 18; $i++) {
452 $a = intdiv($i, 3);
453 $b = $i % 3 + ($this->moduleCount - 8 - 3);
454 $v = ($bits >> $i & 1) === 1;
455 $this->set($b, $a, $v, $this::M_VERSION);
456 // ne
457 $this->set($a, $b, $v, $this::M_VERSION);
458 // sw
459 }
460 }
461 return $this;
462 }
463 /**
464 * Draws the format info along the finder patterns. If no $maskPattern, all format info modules will be set to false.
465 *
466 * ISO/IEC 18004:2000 Section 8.9
467 */
468 public function setFormatInfo(?MaskPattern $maskPattern = null) : self
469 {
470 $this->maskPattern = $maskPattern;
471 $bits = 0;
472 // sets all format fields to false (test mode)
473 if ($this->maskPattern instanceof MaskPattern) {
474 $bits = $this->eccLevel->getformatPattern($this->maskPattern);
475 }
476 for ($i = 0; $i < 15; $i++) {
477 $v = ($bits >> $i & 1) === 1;
478 if ($i < 6) {
479 $this->set(8, $i, $v, $this::M_FORMAT);
480 } elseif ($i < 8) {
481 $this->set(8, $i + 1, $v, $this::M_FORMAT);
482 } else {
483 $this->set(8, $this->moduleCount - 15 + $i, $v, $this::M_FORMAT);
484 }
485 if ($i < 8) {
486 $this->set($this->moduleCount - $i - 1, 8, $v, $this::M_FORMAT);
487 } elseif ($i < 9) {
488 $this->set(15 - $i, 8, $v, $this::M_FORMAT);
489 } else {
490 $this->set(15 - $i - 1, 8, $v, $this::M_FORMAT);
491 }
492 }
493 return $this;
494 }
495 /**
496 * Draws the "quiet zone" of $size around the matrix
497 *
498 * ISO/IEC 18004:2000 Section 7.3.7
499 *
500 * @throws \chillerlan\QRCode\Data\QRCodeDataException
501 */
502 public function setQuietZone(int $quietZoneSize) : self
503 {
504 // early exit if there's nothing to add
505 if ($quietZoneSize < 1) {
506 return $this;
507 }
508 if ($this->matrix[$this->moduleCount - 1][$this->moduleCount - 1] === $this::M_NULL) {
509 throw new QRCodeDataException('use only after writing data');
510 }
511 // create a matrix with the new size
512 $newSize = $this->moduleCount + $quietZoneSize * 2;
513 $newMatrix = $this->createMatrix($newSize, $this::M_QUIETZONE);
514 // copy over the current matrix
515 foreach ($this->matrix as $y => $row) {
516 foreach ($row as $x => $val) {
517 $newMatrix[$y + $quietZoneSize][$x + $quietZoneSize] = $val;
518 }
519 }
520 // set the new values
521 $this->moduleCount = $newSize;
522 $this->matrix = $newMatrix;
523 return $this;
524 }
525 /**
526 * Rotates the matrix by 90 degrees clock wise
527 */
528 public function rotate90() : self
529 {
530 /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
531 $this->matrix = array_map(fn(int ...$a): array => array_reverse($a), ...$this->matrix);
532 return $this;
533 }
534 /**
535 * Inverts the values of the whole matrix
536 *
537 * ISO/IEC 18004:2015 Section 6.2 - Reflectance reversal
538 */
539 public function invert() : self
540 {
541 foreach ($this->matrix as $y => $row) {
542 foreach ($row as $x => $val) {
543 // skip null fields
544 if ($val === $this::M_NULL) {
545 continue;
546 }
547 $this->flip($x, $y);
548 }
549 }
550 return $this;
551 }
552 /**
553 * Clears a space of $width * $height in order to add a logo or text.
554 * If no $height is given, the space will be assumed a square of $width.
555 *
556 * Additionally, the logo space can be positioned within the QR Code using $startX and $startY.
557 * If either of these are null, the logo space will be centered in that direction.
558 * ECC level "H" (30%) is required.
559 *
560 * The coordinates of $startX and $startY do not include the quiet zone:
561 * [0, 0] is always the top left module of the top left finder pattern, negative values go into the quiet zone top and left.
562 *
563 * Please note that adding a logo space minimizes the error correction capacity of the QR Code and
564 * created images may become unreadable, especially when printed with a chance to receive damage.
565 * Please test thoroughly before using this feature in production.
566 *
567 * This method should be called from within an output module (after the matrix has been filled with data).
568 * Note that there is no restiction on how many times this method could be called on the same matrix instance.
569 *
570 * @link https://github.com/chillerlan/php-qrcode/issues/52
571 *
572 * @throws \chillerlan\QRCode\Data\QRCodeDataException
573 */
574 public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null) : self
575 {
576 $height ??= $width;
577 // if width and height happen to be negative or 0 (default value), just return - nothing to do
578 if ($width <= 0 || $height <= 0) {
579 return $this;
580 // @codeCoverageIgnore
581 }
582 // for logos, we operate in ECC H (30%) only
583 if ($this->eccLevel->getLevel() !== EccLevel::H) {
584 throw new QRCodeDataException('ECC level "H" required to add logo space');
585 }
586 // $this->moduleCount includes the quiet zone (if created), we need the QR size here
587 $dimension = $this->version->getDimension();
588 // throw if the size exceeds the qrcode size
589 if ($width > $dimension || $height > $dimension) {
590 throw new QRCodeDataException('logo dimensions exceed matrix size');
591 }
592 // we need uneven sizes to center the logo space, adjust if needed
593 if ($startX === null && $width % 2 === 0) {
594 $width++;
595 }
596 if ($startY === null && $height % 2 === 0) {
597 $height++;
598 }
599 // throw if the logo space exceeds the maximum error correction capacity
600 if ($width * $height > (int) ($dimension * $dimension * 0.25)) {
601 throw new QRCodeDataException('logo space exceeds the maximum error correction capacity');
602 }
603 $quietzone = ($this->moduleCount - $dimension) / 2;
604 $end = $this->moduleCount - $quietzone;
605 // determine start coordinates
606 $startX ??= ($dimension - $width) / 2;
607 $startY ??= ($dimension - $height) / 2;
608 $endX = $quietzone + $startX + $width;
609 $endY = $quietzone + $startY + $height;
610 // clear the space
611 for ($y = $quietzone + $startY; $y < $endY; $y++) {
612 for ($x = $quietzone + $startX; $x < $endX; $x++) {
613 // out of bounds, skip
614 if ($x < $quietzone || $y < $quietzone || $x >= $end || $y >= $end) {
615 continue;
616 }
617 $this->set($x, $y, \false, $this::M_LOGO);
618 }
619 }
620 return $this;
621 }
622 /**
623 * Maps the interleaved binary $data on the matrix
624 */
625 public function writeCodewords(BitBuffer $bitBuffer) : self
626 {
627 $data = (new ReedSolomonEncoder($this->version, $this->eccLevel))->interleaveEcBytes($bitBuffer);
628 $byteCount = count($data);
629 $iByte = 0;
630 $iBit = 7;
631 $direction = \true;
632 for ($i = $this->moduleCount - 1; $i > 0; $i -= 2) {
633 // skip vertical alignment pattern
634 if ($i === 6) {
635 $i--;
636 }
637 for ($count = 0; $count < $this->moduleCount; $count++) {
638 $y = $count;
639 if ($direction) {
640 $y = $this->moduleCount - 1 - $count;
641 }
642 for ($col = 0; $col < 2; $col++) {
643 $x = $i - $col;
644 // skip functional patterns
645 if ($this->matrix[$y][$x] !== $this::M_NULL) {
646 continue;
647 }
648 $this->matrix[$y][$x] = $this::M_DATA;
649 if ($iByte < $byteCount && ($data[$iByte] >> $iBit-- & 1) === 1) {
650 $this->matrix[$y][$x] |= $this::IS_DARK;
651 }
652 if ($iBit === -1) {
653 $iByte++;
654 $iBit = 7;
655 }
656 }
657 }
658 $direction = !$direction;
659 // switch directions
660 }
661 return $this;
662 }
663 /**
664 * Applies/reverses the mask pattern
665 *
666 * ISO/IEC 18004:2000 Section 8.8.1
667 */
668 public function mask(MaskPattern $maskPattern) : self
669 {
670 $this->maskPattern = $maskPattern;
671 $mask = $this->maskPattern->getMask();
672 foreach ($this->matrix as $y => $row) {
673 foreach ($row as $x => $val) {
674 // skip non-data modules
675 if (($val & $this::M_DATA) === $this::M_DATA && $mask($x, $y)) {
676 $this->flip($x, $y);
677 }
678 }
679 }
680 return $this;
681 }
682 }
683