mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-24 23:07:55 +00:00
:octocat: extract MaskPattern
This commit is contained in:
@@ -193,16 +193,9 @@ final class EccLevel{
|
||||
|
||||
/**
|
||||
* returns the format pattern for the given $eccLevel and $maskPattern
|
||||
*
|
||||
* @throws \chillerlan\QRCode\QRCodeException
|
||||
*/
|
||||
public function getformatPattern(int $maskPattern):int{
|
||||
|
||||
if((0b111 & $maskPattern) !== $maskPattern){
|
||||
throw new QRCodeException('invalid mask pattern');
|
||||
}
|
||||
|
||||
return self::FORMAT_PATTERN[self::MODES[$this->eccLevel]][$maskPattern];
|
||||
public function getformatPattern(MaskPattern $maskPattern):int{
|
||||
return self::FORMAT_PATTERN[self::MODES[$this->eccLevel]][$maskPattern->getPattern()];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Class MaskPattern
|
||||
*
|
||||
* @filesource MaskPattern.php
|
||||
* @created 19.01.2021
|
||||
* @package chillerlan\QRCode\Common
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2021 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCode\Common;
|
||||
|
||||
use chillerlan\QRCode\QRCodeException;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class MaskPattern{
|
||||
|
||||
public const PATTERN_000 = 0b000;
|
||||
public const PATTERN_001 = 0b001;
|
||||
public const PATTERN_010 = 0b010;
|
||||
public const PATTERN_011 = 0b011;
|
||||
public const PATTERN_100 = 0b100;
|
||||
public const PATTERN_101 = 0b101;
|
||||
public const PATTERN_110 = 0b110;
|
||||
public const PATTERN_111 = 0b111;
|
||||
|
||||
public const PATTERNS = [
|
||||
self::PATTERN_000,
|
||||
self::PATTERN_001,
|
||||
self::PATTERN_010,
|
||||
self::PATTERN_011,
|
||||
self::PATTERN_100,
|
||||
self::PATTERN_101,
|
||||
self::PATTERN_110,
|
||||
self::PATTERN_111,
|
||||
];
|
||||
|
||||
private int $maskPattern;
|
||||
|
||||
/**
|
||||
* MaskPattern constructor.
|
||||
*
|
||||
* ISO/IEC 18004:2000 Section 8.8.1
|
||||
*
|
||||
* @throws \chillerlan\QRCode\QRCodeException
|
||||
*/
|
||||
public function __construct(int $maskPattern){
|
||||
|
||||
if((0b111 & $maskPattern) !== $maskPattern){
|
||||
throw new QRCodeException('invalid mask pattern'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$this->maskPattern = $maskPattern;
|
||||
}
|
||||
|
||||
public function getPattern():int{
|
||||
return $this->maskPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* ISO/IEC 18004:2000 Section 8.8.1
|
||||
*
|
||||
* Note that some versions of the QR code standard have had errors in the section about mask patterns.
|
||||
* The information below has been corrected. (https://www.thonky.com/qr-code-tutorial/mask-patterns)
|
||||
*/
|
||||
public function getMask():Closure{
|
||||
return [
|
||||
self::PATTERN_000 => fn($x, $y):int => ($x + $y) % 2,
|
||||
self::PATTERN_001 => fn($x, $y):int => $y % 2,
|
||||
self::PATTERN_010 => fn($x, $y):int => $x % 3,
|
||||
self::PATTERN_011 => fn($x, $y):int => ($x + $y) % 3,
|
||||
self::PATTERN_100 => fn($x, $y):int => ((int)($y / 2) + (int)($x / 3)) % 2,
|
||||
self::PATTERN_101 => fn($x, $y):int => (($x * $y) % 2) + (($x * $y) % 3),
|
||||
self::PATTERN_110 => fn($x, $y):int => ((($x * $y) % 2) + (($x * $y) % 3)) % 2,
|
||||
self::PATTERN_111 => fn($x, $y):int => ((($x * $y) % 3) + (($x + $y) % 2)) % 2,
|
||||
][$this->maskPattern];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\MaskPattern;
|
||||
use function abs, array_search, call_user_func_array, min;
|
||||
|
||||
/**
|
||||
@@ -45,14 +46,14 @@ final class MaskPatternTester{
|
||||
*
|
||||
* @see \chillerlan\QRCode\Data\MaskPatternTester
|
||||
*/
|
||||
public function getBestMaskPattern():int{
|
||||
public function getBestMaskPattern():MaskPattern{
|
||||
$penalties = [];
|
||||
|
||||
for($pattern = 0; $pattern < 8; $pattern++){
|
||||
$penalties[$pattern] = $this->testPattern($pattern);
|
||||
foreach(MaskPattern::PATTERNS as $pattern){
|
||||
$penalties[$pattern] = $this->testPattern(new MaskPattern($pattern));
|
||||
}
|
||||
|
||||
return array_search(min($penalties), $penalties, true);
|
||||
return new MaskPattern(array_search(min($penalties), $penalties, true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +62,7 @@ final class MaskPatternTester{
|
||||
* @see \chillerlan\QRCode\QROptions::$maskPattern
|
||||
* @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
|
||||
*/
|
||||
public function testPattern(int $pattern):int{
|
||||
public function testPattern(MaskPattern $pattern):int{
|
||||
$matrix = $this->qrData->writeMatrix($pattern, true);
|
||||
$penalty = 0;
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{BitBuffer, EccLevel, Mode, ReedSolomonEncoder, Version};
|
||||
use chillerlan\QRCode\Common\{BitBuffer, EccLevel, MaskPattern, Mode, ReedSolomonEncoder, Version};
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\Settings\SettingsContainerInterface;
|
||||
|
||||
@@ -100,7 +100,7 @@ final class QRData{
|
||||
/**
|
||||
* returns a fresh matrix object with the data written for the given $maskPattern
|
||||
*/
|
||||
public function writeMatrix(int $maskPattern, bool $test = null):QRMatrix{
|
||||
public function writeMatrix(MaskPattern $maskPattern, bool $test = null):QRMatrix{
|
||||
$data = (new ReedSolomonEncoder)->interleaveEcBytes($this->bitBuffer, $this->version, $this->eccLevel);
|
||||
|
||||
return (new QRMatrix($this->version, $this->eccLevel))
|
||||
|
||||
+8
-40
@@ -12,9 +12,7 @@
|
||||
|
||||
namespace chillerlan\QRCode\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{EccLevel, Version};
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use Closure;
|
||||
use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
|
||||
|
||||
use SplFixedArray;
|
||||
use function array_fill, array_push, array_unshift, floor, max, min, range;
|
||||
@@ -57,9 +55,9 @@ final class QRMatrix{
|
||||
public const IS_DARK = 0b100000000000;
|
||||
|
||||
/**
|
||||
* the used mask pattern, set via QRMatrix::mapData()
|
||||
* the used mask pattern, set via QRMatrix::mask()
|
||||
*/
|
||||
protected int $maskPattern = QRCode::MASK_PATTERN_AUTO;
|
||||
protected ?MaskPattern $maskPattern = null;
|
||||
|
||||
/**
|
||||
* the size (side length) of the matrix, including quiet zone (if created)
|
||||
@@ -96,7 +94,7 @@ final class QRMatrix{
|
||||
/**
|
||||
* shortcut to initialize the matrix
|
||||
*/
|
||||
public function init(int $maskPattern, bool $test = null):QRMatrix{
|
||||
public function init(MaskPattern $maskPattern, bool $test = null):QRMatrix{
|
||||
return $this
|
||||
->setFinderPattern()
|
||||
->setSeparators()
|
||||
@@ -149,7 +147,7 @@ final class QRMatrix{
|
||||
/**
|
||||
* Returns the current mask pattern
|
||||
*/
|
||||
public function maskPattern():int{
|
||||
public function maskPattern():?MaskPattern{
|
||||
return $this->maskPattern;
|
||||
}
|
||||
|
||||
@@ -365,7 +363,7 @@ final class QRMatrix{
|
||||
*
|
||||
* ISO/IEC 18004:2000 Section 8.9
|
||||
*/
|
||||
public function setFormatInfo(int $maskPattern, bool $test = null):QRMatrix{
|
||||
public function setFormatInfo(MaskPattern $maskPattern, bool $test = null):QRMatrix{
|
||||
$bits = $this->eccLevel->getformatPattern($maskPattern);
|
||||
|
||||
for($i = 0; $i < 15; $i++){
|
||||
@@ -569,9 +567,9 @@ final class QRMatrix{
|
||||
*
|
||||
* ISO/IEC 18004:2000 Section 8.8.1
|
||||
*/
|
||||
public function mask(int $maskPattern):QRMatrix{
|
||||
public function mask(MaskPattern $maskPattern):QRMatrix{
|
||||
$this->maskPattern = $maskPattern;
|
||||
$mask = $this->getMask($this->maskPattern);
|
||||
$mask = $this->maskPattern->getMask();
|
||||
|
||||
foreach($this->matrix as $y => &$row){
|
||||
foreach($row as $x => &$val){
|
||||
@@ -584,34 +582,4 @@ final class QRMatrix{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ISO/IEC 18004:2000 Section 8.8.1
|
||||
*
|
||||
* Note that some versions of the QR code standard have had errors in the section about mask patterns.
|
||||
* The information below has been corrected. (https://www.thonky.com/qr-code-tutorial/mask-patterns)
|
||||
*
|
||||
* @see \chillerlan\QRCode\QRMatrix::mapData()
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||
*/
|
||||
protected function getMask(int $maskPattern):Closure{
|
||||
|
||||
if((0b111 & $maskPattern) !== $maskPattern){
|
||||
throw new QRCodeDataException('invalid mask pattern'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return [
|
||||
0b000 => fn($x, $y):int => ($x + $y) % 2,
|
||||
0b001 => fn($x, $y):int => $y % 2,
|
||||
0b010 => fn($x, $y):int => $x % 3,
|
||||
0b011 => fn($x, $y):int => ($x + $y) % 3,
|
||||
0b100 => fn($x, $y):int => ((int)($y / 2) + (int)($x / 3)) % 2,
|
||||
0b101 => fn($x, $y):int => (($x * $y) % 2) + (($x * $y) % 3),
|
||||
0b110 => fn($x, $y):int => ((($x * $y) % 2) + (($x * $y) % 3)) % 2,
|
||||
0b111 => fn($x, $y):int => ((($x * $y) % 3) + (($x + $y) % 2)) % 2,
|
||||
][$maskPattern];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@
|
||||
namespace chillerlan\QRCode;
|
||||
|
||||
use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Kanji, MaskPatternTester, Number, QRData, QRCodeDataException, QRMatrix};
|
||||
use chillerlan\QRCode\Common\MaskPattern;
|
||||
use chillerlan\QRCode\Common\Mode;
|
||||
use chillerlan\QRCode\Output\{
|
||||
QRCodeOutputException, QRFpdf, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString
|
||||
@@ -156,7 +157,7 @@ class QRCode{
|
||||
|
||||
$maskPattern = $this->options->maskPattern === $this::MASK_PATTERN_AUTO
|
||||
? (new MaskPatternTester($this->dataInterface))->getBestMaskPattern()
|
||||
: $this->options->maskPattern;
|
||||
: new MaskPattern($this->options->maskPattern);
|
||||
|
||||
$matrix = $this->dataInterface->writeMatrix($maskPattern);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
namespace chillerlan\QRCodeTest\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\MaskPattern;
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -82,10 +83,10 @@ abstract class DatainterfaceTestAbstract extends TestCase{
|
||||
public function testInitMatrix(int $maskPattern):void{
|
||||
$this->dataInterface->setData([$this->testdata]);
|
||||
|
||||
$matrix = $this->dataInterface->writeMatrix($maskPattern);
|
||||
$matrix = $this->dataInterface->writeMatrix(new MaskPattern($maskPattern));
|
||||
|
||||
$this::assertInstanceOf(QRMatrix::class, $matrix);
|
||||
$this::assertSame($maskPattern, $matrix->maskPattern());
|
||||
$this::assertSame($maskPattern, $matrix->maskPattern()->getPattern());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
namespace chillerlan\QRCodeTest\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\MaskPattern;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use chillerlan\QRCode\Data\{Byte, MaskPatternTester, QRData};
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -27,7 +28,7 @@ final class MaskPatternTesterTest extends TestCase{
|
||||
public function testMaskpattern():void{
|
||||
$dataInterface = new QRData(new QROptions(['version' => 10]), [[Byte::class, 'test']]);
|
||||
|
||||
$this::assertSame(3, (new MaskPatternTester($dataInterface))->getBestMaskPattern());
|
||||
$this::assertSame(3, (new MaskPatternTester($dataInterface))->getBestMaskPattern()->getPattern());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +37,7 @@ final class MaskPatternTesterTest extends TestCase{
|
||||
public function testMaskpatternID():void{
|
||||
$dataInterface = new QRData(new QROptions(['version' => 10]), [[Byte::class, 'test']]);
|
||||
|
||||
$this::assertSame(4243, (new MaskPatternTester($dataInterface))->testPattern(3));
|
||||
$this::assertSame(4243, (new MaskPatternTester($dataInterface))->testPattern(new MaskPattern(MaskPattern::PATTERN_011)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace chillerlan\QRCodeTest\Data;
|
||||
|
||||
use chillerlan\QRCode\Common\{EccLevel, Version};
|
||||
use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -77,9 +77,12 @@ final class QRMatrixTest extends TestCase{
|
||||
* Tests if maskPattern() returns the current (or default) mask pattern
|
||||
*/
|
||||
public function testMaskPattern():void{
|
||||
$this::assertSame(-1, $this->matrix->maskPattern()); // default
|
||||
$this::assertSame(null, $this->matrix->maskPattern());
|
||||
|
||||
// @todo: actual mask pattern after mapData()
|
||||
$matrix = (new QRCode)->addByteSegment('testdata')->getMatrix();
|
||||
|
||||
$this::assertInstanceOf(MaskPattern::class, $matrix->maskPattern());
|
||||
$this::assertSame(MaskPattern::PATTERN_010, $matrix->maskPattern()->getPattern());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,7 +246,7 @@ final class QRMatrixTest extends TestCase{
|
||||
* @dataProvider versionProvider
|
||||
*/
|
||||
public function testSetFormatInfo(int $version):void{
|
||||
$matrix = $this->getMatrix($version)->setFormatInfo(0, true);
|
||||
$matrix = $this->getMatrix($version)->setFormatInfo(new MaskPattern(MaskPattern::PATTERN_000), true);
|
||||
|
||||
$this::assertSame(QRMatrix::M_FORMAT, $matrix->get(8, 0));
|
||||
$this::assertSame(QRMatrix::M_FORMAT, $matrix->get(0, 8));
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
namespace chillerlan\QRCodeTest\Output;
|
||||
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use chillerlan\QRCode\Common\MaskPattern;
|
||||
use chillerlan\QRCode\Data\{Byte, QRData, QRMatrix};
|
||||
use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -48,7 +49,8 @@ abstract class QROutputTestAbstract extends TestCase{
|
||||
}
|
||||
|
||||
$this->options = new QROptions;
|
||||
$this->matrix = (new QRData($this->options, [[Byte::class, 'testdata']]))->writeMatrix(0);
|
||||
$this->matrix = (new QRData($this->options, [[Byte::class, 'testdata']]))
|
||||
->writeMatrix(new MaskPattern(MaskPattern::PATTERN_010));
|
||||
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user