Completed
Branch master (2ed3f6)
by smiley
04:03 queued 47s
created

QRCode::getMatrix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
/**
3
 * Class QRCode
4
 *
5
 * @filesource   QRCode.php
6
 * @created      26.11.2015
7
 * @package      chillerlan\QRCode
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2015 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode;
14
15
use chillerlan\QRCode\Data\AlphaNum;
16
use chillerlan\QRCode\Data\Byte;
17
use chillerlan\QRCode\Data\Kanji;
18
use chillerlan\QRCode\Data\Number;
19
use chillerlan\QRCode\Data\QRDataInterface;
20
use chillerlan\QRCode\Output\QROutputInterface;
21
22
/**
23
 * @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
24
 * @link http://www.thonky.com/qr-code-tutorial/
25
 */
26
class QRCode{
27
28
	/**
29
	 * API constants
30
	 */
31
	const OUTPUT_STRING_TEXT = 0;
32
	const OUTPUT_STRING_JSON = 1;
33
	const OUTPUT_STRING_HTML = 2;
34
35
	const OUTPUT_IMAGE_PNG = 'png';
36
	const OUTPUT_IMAGE_JPG = 'jpg';
37
	const OUTPUT_IMAGE_GIF = 'gif';
38
39
	const ERROR_CORRECT_LEVEL_L = 1; // 7%.
40
	const ERROR_CORRECT_LEVEL_M = 0; // 15%.
41
	const ERROR_CORRECT_LEVEL_Q = 3; // 25%.
42
	const ERROR_CORRECT_LEVEL_H = 2; // 30%.
43
44
	// max bits @ ec level L:07 M:15 Q:25 H:30 %
45
	const TYPE_01 =  1; //  152  128  104   72
46
	const TYPE_02 =  2; //  272  224  176  128
47
	const TYPE_03 =  3; //  440  352  272  208
48
	const TYPE_04 =  4; //  640  512  384  288
49
	const TYPE_05 =  5; //  864  688  496  368
50
	const TYPE_06 =  6; // 1088  864  608  480
51
	const TYPE_07 =  7; // 1248  992  704  528
52
	const TYPE_08 =  8; // 1552 1232  880  688
53
	const TYPE_09 =  9; // 1856 1456 1056  800
54
	const TYPE_10 = 10; // 2192 1728 1232  976
55
56
	/**
57
	 * @var array
58
	 */
59
	protected $matrix = [];
60
61
	/**
62
	 * @var int
63
	 */
64
	protected $pixelCount = 0;
65
66
	/**
67
	 * @var int
68
	 */
69
	protected $typeNumber;
70
71
	/**
72
	 * @var int
73
	 */
74
	protected $errorCorrectLevel;
75
76
	/**
77
	 * @var \chillerlan\QRCode\BitBuffer
78
	 */
79
	protected $bitBuffer;
80
81
	/**
82
	 * @var \chillerlan\QRCode\Data\QRDataInterface
83
	 */
84
	protected $qrDataInterface;
85
86
	/**
87
	 * @var \chillerlan\QRCode\Output\QROutputInterface
88
	 */
89
	protected $qrOutputInterface;
90
91
	/**
92
	 * QRCode constructor.
93
	 *
94
	 * @param string                                      $data
95
	 * @param \chillerlan\QRCode\Output\QROutputInterface $output
96
	 * @param \chillerlan\QRCode\QROptions|null           $options
97
	 */
98
	public function __construct($data, QROutputInterface $output, QROptions $options = null){
99
		$this->qrOutputInterface = $output;
100
		$this->bitBuffer = new BitBuffer;
101
		$this->setData($data, $options);
102
	}
103
104
	/**
105
	 * @param string                            $data
106
	 * @param \chillerlan\QRCode\QROptions|null $options
107
	 *
108
	 * @return $this
109
	 * @throws \chillerlan\QRCode\QRCodeException
110
	 */
111
	public function setData($data, QROptions $options = null){
112
		$data = trim($data);
113
114
		if(empty($data)){
115
			throw new QRCodeException('No data given.');
116
		}
117
118
		if(!$options instanceof QROptions){
119
			$options = new QROptions;
120
		}
121
122
		if(!in_array($options->errorCorrectLevel, QRConst::RSBLOCK, true)){
123
			throw new QRCodeException('Invalid error correct level: '.$options->errorCorrectLevel);
124
		}
125
126
		$this->errorCorrectLevel = $options->errorCorrectLevel;
127
128
		switch(true){
129
			case Util::isAlphaNum($data):
130
				$mode = Util::isNumber($data) ? QRConst::MODE_NUMBER : QRConst::MODE_ALPHANUM;
131
				break;
132
			case Util::isKanji($data):
133
				$mode = QRConst::MODE_KANJI;
134
				break;
135
			default:
136
				$mode = QRConst::MODE_BYTE;
137
				break;
138
		}
139
140
		$qrDataInterface = [
141
			QRConst::MODE_ALPHANUM => AlphaNum::class,
142
			QRConst::MODE_BYTE     => Byte::class,
143
			QRConst::MODE_KANJI    => Kanji::class,
144
			QRConst::MODE_NUMBER   => Number::class,
145
		][$mode];
146
147
		$this->qrDataInterface = new $qrDataInterface($data);
148
149
		$this->typeNumber = intval($options->typeNumber);
150
151
		if($this->typeNumber < 1 || $this->typeNumber > 10){
152
			$this->typeNumber = $this->getTypeNumber($mode);
153
		}
154
155
		return $this;
156
	}
157
158
	/**
159
	 * @param $mode
160
	 *
161
	 * @return int
162
	 * @throws \chillerlan\QRCode\QRCodeException
163
	 */
164
	protected function getTypeNumber($mode){
165
166
		if($this->qrDataInterface instanceof QRDataInterface){
167
			/** @noinspection PhpUndefinedFieldInspection */
168
			$length = $this->qrDataInterface->mode === QRConst::MODE_KANJI
0 ignored issues
show
Bug introduced by
Accessing mode on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
169
				? floor($this->qrDataInterface->dataLength / 2)
0 ignored issues
show
Bug introduced by
Accessing dataLength on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
170
				: $this->qrDataInterface->dataLength;
0 ignored issues
show
Bug introduced by
Accessing dataLength on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
171
172
			foreach(range(1, 10) as $type){
173
				if($length <= Util::getMaxLength($type, $mode, $this->errorCorrectLevel)){
174
					return $type;
175
				}
176
			}
177
178
			throw new QRCodeException('Unable to determine type number.'); // @codeCoverageIgnore
179
		}
180
181
		throw new QRCodeException('$this->qrDataInterface does not implement QRDataInterface'); // @codeCoverageIgnore
182
	}
183
184
	/**
185
	 * @return mixed
186
	 */
187
	public function output(){
188
		$this->qrOutputInterface->setMatrix($this->getRawData());
189
		return $this->qrOutputInterface->dump();
190
	}
191
192
	/**
193
	 * @return array
194
	 */
195
	public function getRawData(){
196
		$minLostPoint = 0;
197
		$maskPattern = 0;
198
199
		for($pattern = 0; $pattern <= 7; $pattern++){
200
			$this->getMatrix(true, $pattern);
201
			$lostPoint = 0;
202
			$darkCount = 0;
203
204
			$range1 = range(0, $this->pixelCount-1);
205
			$range2 = range(0, $this->pixelCount-2);
206
			$range3 = range(0, $this->pixelCount-7);
207
			$range4 = range(-1, 1);
208
209
			// LEVEL1
210
			foreach($range1 as $row){
211
				foreach($range1 as $col){
212
					$sameCount = 0;
213
214
					foreach($range4 as $rr){
215
						if($row + $rr < 0 || $this->pixelCount <= $row + $rr){
216
							continue;
217
						}
218
219
						foreach($range4 as $cr){
220
221
							if(($rr === 0 && $cr === 0) || ($col + $cr < 0 || $this->pixelCount <= $col + $cr)){
222
								continue;
223
							}
224
225
							if($this->matrix[$row + $rr][$col + $cr] === $this->matrix[$row][$col]){
226
								$sameCount++;
227
							}
228
						}
229
					}
230
231
					if($sameCount > 5){
232
						$lostPoint += (3 + $sameCount - 5);
233
					}
234
				}
235
			}
236
237
			// LEVEL2
238
			foreach($range2 as $row){
239
				foreach($range2 as $col){
240
					$count = 0;
241
242
					if(
243
						   $this->matrix[$row    ][$col    ]
244
						|| $this->matrix[$row    ][$col + 1]
245
						|| $this->matrix[$row + 1][$col    ]
246
						|| $this->matrix[$row + 1][$col + 1]
247
					){
248
						$count++;
249
					}
250
251
					if($count === 0 || $count === 4){
252
						$lostPoint += 3;
253
					}
254
				}
255
			}
256
257
			// LEVEL3
258 View Code Duplication
			foreach($range1 as $row){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
259
				foreach($range3 as $col){
260
					if(
261
						    $this->matrix[$row][$col    ]
262
						&& !$this->matrix[$row][$col + 1]
263
						&&  $this->matrix[$row][$col + 2]
264
						&&  $this->matrix[$row][$col + 3]
265
						&&  $this->matrix[$row][$col + 4]
266
						&& !$this->matrix[$row][$col + 5]
267
						&&  $this->matrix[$row][$col + 6]
268
					){
269
						$lostPoint += 40;
270
					}
271
				}
272
			}
273
274 View Code Duplication
			foreach($range1 as $col){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
275
				foreach($range3 as $row){
276
					if(
277
						    $this->matrix[$row    ][$col]
278
						&& !$this->matrix[$row + 1][$col]
279
						&&  $this->matrix[$row + 2][$col]
280
						&&  $this->matrix[$row + 3][$col]
281
						&&  $this->matrix[$row + 4][$col]
282
						&& !$this->matrix[$row + 5][$col]
283
						&&  $this->matrix[$row + 6][$col]
284
					){
285
						$lostPoint += 40;
286
					}
287
				}
288
			}
289
290
			// LEVEL4
291
			foreach($range1 as $col){
292
				foreach($range1 as $row){
293
					if($this->matrix[$row][$col]){
294
						$darkCount++;
295
					}
296
				}
297
			}
298
299
			$lostPoint += (abs(100 * $darkCount / $this->pixelCount / $this->pixelCount - 50) / 5) * 10;
300
301
			if($pattern === 0 || $minLostPoint > $lostPoint){
302
				$minLostPoint = $lostPoint;
303
				$maskPattern = $pattern;
304
			}
305
306
		}
307
308
		$this->getMatrix(false, $maskPattern);
309
310
		return $this->matrix;
311
	}
312
313
	/**
314
	 * @param bool $test
315
	 */
316
	protected function setTypeNumber($test){
317
		$bits = Util::getBCHTypeNumber($this->typeNumber);
318
319
		for($i = 0; $i < 18; $i++){
320
			$a = (int)floor($i / 3);
321
			$b = $i % 3 + $this->pixelCount - 8 - 3;
322
323
			$this->matrix[$a][$b] = $this->matrix[$b][$a] = !$test && (($bits >> $i) & 1) === 1;
324
		}
325
326
	}
327
328
	/**
329
	 * @param bool $test
330
	 * @param int  $pattern
331
	 */
332
	protected function setTypeInfo($test, $pattern){
333
		$this->setPattern();
334
		$bits = Util::getBCHTypeInfo(($this->errorCorrectLevel << 3) | $pattern);
335
336
		for($i = 0; $i < 15; $i++){
337
			$mod = !$test && (($bits >> $i) & 1) === 1;
338
339
			switch(true){
340
				case $i < 6: $this->matrix[$i    ][8] = $mod; break;
341
				case $i < 8: $this->matrix[$i + 1][8] = $mod; break;
342
				default:
343
					$this->matrix[$this->pixelCount - 15 + $i][8] = $mod;
344
			}
345
346
			switch(true){
347 View Code Duplication
				case $i < 8: $this->matrix[8][$this->pixelCount - $i - 1] = $mod; break;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
348 View Code Duplication
				case $i < 9: $this->matrix[8][           15 + 1 - $i - 1] = $mod; break;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
349
				default:
350
					$this->matrix[8][15 - $i - 1] = $mod;
351
			}
352
353
		}
354
355
		$this->matrix[$this->pixelCount - 8][8] = !$test;
356
	}
357
358
	/**
359
	 * @throws \chillerlan\QRCode\QRCodeException
360
	 */
361
	protected function createData(){
362
		$this->bitBuffer->clear();
363
364
		$MAX_BITS = QRConst::MAX_BITS; // php5 compat
365
		$MAX_BITS = $MAX_BITS[$this->typeNumber][$this->errorCorrectLevel];
366
367
		/** @noinspection PhpUndefinedFieldInspection */
368
		$this->bitBuffer->put($this->qrDataInterface->mode, 4);
0 ignored issues
show
Bug introduced by
Accessing mode on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
369
		/** @noinspection PhpUndefinedFieldInspection */
370
		$this->bitBuffer->put(
371
			$this->qrDataInterface->mode === QRConst::MODE_KANJI ? floor($this->qrDataInterface->dataLength / 2) : $this->qrDataInterface->dataLength,
0 ignored issues
show
Bug introduced by
Accessing mode on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
Accessing dataLength on the interface chillerlan\QRCode\Data\QRDataInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
372
			$this->qrDataInterface->getLengthInBits($this->typeNumber)
373
		);
374
375
		$this->qrDataInterface->write($this->bitBuffer);
376
377
		if($this->bitBuffer->length > $MAX_BITS){
378
			throw new QRCodeException('code length overflow. ('.$this->bitBuffer->length.' > '.$MAX_BITS.'bit)');
379
		}
380
381
		// end code.
382
		if($this->bitBuffer->length + 4 <= $MAX_BITS){
383
			$this->bitBuffer->put(0, 4);
384
		}
385
386
		// padding
387
		while($this->bitBuffer->length % 8 !== 0){
388
			$this->bitBuffer->putBit(false);
389
		}
390
391
		// padding
392
		while(true){
393
394
			if($this->bitBuffer->length >= $MAX_BITS){
395
				break;
396
			}
397
398
			$this->bitBuffer->put(QRConst::PAD0, 8);
399
400
			if($this->bitBuffer->length >= $MAX_BITS){
401
				break;
402
			}
403
404
			$this->bitBuffer->put(QRConst::PAD1, 8);
405
		}
406
407
	}
408
409
	/**
410
	 * @return array
411
	 * @throws \chillerlan\QRCode\QRCodeException
412
	 */
413
	protected function createBytes(){
414
		$totalCodeCount = $maxDcCount = $maxEcCount = $offset = $index = 0;
415
		$rsBlocks = Util::getRSBlocks($this->typeNumber, $this->errorCorrectLevel);
416
		$rsBlockCount = count($rsBlocks);
417
		$dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
418
419
		foreach($rsBlocks as $key => $value){
420
			$rsBlockTotal = $value[0];
421
			$rsBlockDataCount = $value[1];
422
423
			$maxDcCount = max($maxDcCount, $rsBlockDataCount);
424
			$maxEcCount = max($maxEcCount, $rsBlockTotal - $rsBlockDataCount);
425
426
			$dcdata[$key] = array_fill(0, $rsBlockDataCount, null);
427
428
			foreach($dcdata[$key] as $i => &$_dcdata){
429
				$bdata = $this->bitBuffer->buffer;
430
				$_dcdata = 0xff & $bdata[$i + $offset];
431
			}
432
433
			$offset += $rsBlockDataCount;
434
435
			$rsPoly = new Polynomial;
436
			$modPoly = new Polynomial;
437
438
			foreach(range(0, $rsBlockTotal - $rsBlockDataCount - 1) as $i){
439
				$modPoly->setNum([1, $modPoly->gexp($i)]);
440
				$rsPoly->multiply($modPoly->num);
441
			}
442
443
			$rsPolyCount = count($rsPoly->num);
444
			$modPoly->setNum($dcdata[$key], $rsPolyCount - 1)->mod($rsPoly->num);
445
			$ecdata[$key] = array_fill(0, $rsPolyCount - 1, null);
446
			$add = count($modPoly->num) - count($ecdata[$key]);
447
448
			foreach($ecdata[$key] as $i => &$_ecdata){
449
				$modIndex = $i + $add;
450
				$_ecdata = $modIndex >= 0 ? $modPoly->num[$modIndex] : 0;
451
			}
452
453
			$totalCodeCount += $rsBlockTotal;
454
		}
455
456
		$data = array_fill(0, $totalCodeCount, null);
457
		$rsrange = range(0, $rsBlockCount - 1);
458
459 View Code Duplication
		foreach(range(0, $maxDcCount - 1) as $i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
460
			foreach($rsrange as $key){
461
				if($i < count($dcdata[$key])){
462
					$data[$index++] = $dcdata[$key][$i];
463
				}
464
			}
465
		}
466
467 View Code Duplication
		foreach(range(0, $maxEcCount - 1) as $i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
468
			foreach($rsrange as $key){
469
				if($i < count($ecdata[$key])){
470
					$data[$index++] = $ecdata[$key][$i];
471
				}
472
			}
473
		}
474
475
		return $data;
476
	}
477
478
	/**
479
	 * @param int $pattern
480
	 *
481
	 * @throws \chillerlan\QRCode\QRCodeException
482
	 */
483
	protected function mapData($pattern){
484
		$this->createData();
485
		$data = $this->createBytes();
486
		$inc = -1;
487
		$row = $this->pixelCount - 1;
488
		$bitIndex = 7;
489
		$byteIndex = 0;
490
		$dataCount = count($data);
491
492
		for($col = $this->pixelCount - 1; $col > 0; $col -= 2){
493
			if($col === 6){
494
				$col--;
495
			}
496
497
			while(true){
498
				foreach([0, 1] as $c){
499
					$_col = $col - $c;
500
501
					if($this->matrix[$row][$_col] === null){
502
						$dark = false;
503
504
						if($byteIndex < $dataCount){
505
							$dark = (($data[$byteIndex] >> $bitIndex) & 1) === 1;
506
						}
507
508
						$a = $row + $_col;
509
						$m = $row * $_col;
510
						$MASK_PATTERN = [
511
							QRConst::MASK_PATTERN000 => $a % 2,
512
							QRConst::MASK_PATTERN001 => $row % 2,
513
							QRConst::MASK_PATTERN010 => $_col % 3,
514
							QRConst::MASK_PATTERN011 => $a % 3,
515
							QRConst::MASK_PATTERN100 => (floor($row / 2) + floor($_col / 3)) % 2,
516
							QRConst::MASK_PATTERN101 => $m % 2 + $m % 3,
517
							QRConst::MASK_PATTERN110 => ($m % 2 + $m % 3) % 2,
518
							QRConst::MASK_PATTERN111 => ($m % 3 + $a % 2) % 2,
519
						][$pattern];
520
521
						if($MASK_PATTERN === 0){
522
							$dark = !$dark;
523
						}
524
525
						$this->matrix[$row][$_col] = $dark;
526
527
						$bitIndex--;
528
						if($bitIndex === -1){
529
							$byteIndex++;
530
							$bitIndex = 7;
531
						}
532
					}
533
				}
534
535
				$row += $inc;
536
				if($row < 0 || $this->pixelCount <= $row){
537
					$row -= $inc;
538
					$inc = -$inc;
539
					break;
540
				}
541
			}
542
		}
543
544
	}
545
546
	/**
547
	 * @throws \chillerlan\QRCode\QRCodeException
548
	 */
549
	protected function setPattern(){
550
551
		// setupPositionProbePattern
552
		$range = range(-1, 7);
553
		foreach([[0, 0], [$this->pixelCount - 7, 0], [0, $this->pixelCount - 7]] as $grid){
554
			$row = $grid[0];
555
			$col = $grid[1];
556
557
			foreach($range as $r){
558
				foreach($range as $c){
559
560
					if($row + $r <= -1 || $this->pixelCount <= $row + $r || $col + $c <= -1 || $this->pixelCount <= $col + $c){
561
						continue;
562
					}
563
564
					$this->matrix[$row + $r][$col + $c] =
565
						(0 <= $r && $r <= 6 && ($c === 0 || $c === 6))
566
						|| (0 <= $c && $c <= 6 && ($r === 0 || $r === 6))
567
						|| (2 <= $c && $c <= 4 && 2 <= $r && $r <= 4);
568
				}
569
			}
570
		}
571
572
		// setupPositionAdjustPattern
573
		$PATTERN_POSITION = QRConst::PATTERN_POSITION; // PHP5 compat
574
		$pos = $PATTERN_POSITION[$this->typeNumber - 1];
575
		$range = range(-2, 2);
576
		foreach($pos as $i => $posI){
577
			foreach($pos as $j => $posJ){
578
				if($this->matrix[$posI][$posJ] !== null){
579
					continue;
580
				}
581
582
				foreach($range as $row){
583
					foreach($range as $col){
584
						$this->matrix[$posI + $row][$posJ + $col] =
585
							   $row === -2 || $row === 2
586
							|| $col === -2 || $col === 2
587
							||($row ===  0 && $col === 0);
588
					}
589
				}
590
			}
591
		}
592
593
		// setupTimingPattern
594
		foreach(range(8, $this->pixelCount - 8) as $i){
595
			if($this->matrix[$i][6] !== null){
596
				continue; // @codeCoverageIgnore
597
			}
598
599
			$this->matrix[$i][6] = $this->matrix[6][$i] = $i % 2 === 0;
600
		}
601
602
	}
603
604
	/**
605
	 * @param bool $test
606
	 * @param int  $maskPattern
607
	 *
608
	 * @throws \chillerlan\QRCode\QRCodeException
609
	 */
610
	protected function getMatrix($test, $maskPattern){
611
		$this->pixelCount = $this->typeNumber * 4 + 17;
612
		$this->matrix = array_fill(0, $this->pixelCount, array_fill(0, $this->pixelCount, null));
613
		$this->setTypeInfo($test, $maskPattern);
614
615
		if($this->typeNumber >= 7){
616
			$this->setTypeNumber($test);
617
		}
618
619
		$this->mapData($maskPattern);
620
	}
621
622
}
623