Explorar o código

:octocat: extract MaskPattern

codemasher %!s(int64=5) %!d(string=hai) anos
pai
achega
182ebf4e70

+ 2 - 9
src/Common/EccLevel.php

@@ -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()];
 	}
 
 	/**

+ 84 - 0
src/Common/MaskPattern.php

@@ -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];
+	}
+
+}

+ 6 - 5
src/Data/MaskPatternTester.php

@@ -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
src/Data/QRData.php

@@ -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
src/Data/QRMatrix.php

@@ -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
src/QRCode.php

@@ -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);
 

+ 3 - 2
tests/Data/DatainterfaceTestAbstract.php

@@ -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());
 	}
 
 	/**

+ 3 - 2
tests/Data/MaskPatternTesterTest.php

@@ -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)));
 	}
 
 }

+ 7 - 4
tests/Data/QRMatrixTest.php

@@ -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));

+ 3 - 1
tests/Output/QROutputTestAbstract.php

@@ -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);
 	}