Ver Fonte

:octocat: significant output performance improvements

smiley há 2 anos atrás
pai
commit
90f4a4bc66

+ 6 - 6
examples/svgMeltedModules.php

@@ -39,25 +39,25 @@ class MeltedSVGQRCodeOutput extends QRMarkupSVG{
 	 */
 	protected function collectModules(Closure $transform):array{
 		$paths = [];
+		$melt  = $this->options->melt; // avoid magic getter in long loops
 
 		// collect the modules for each type
-		for($y = 0; $y < $this->moduleCount; $y++){
-			for($x = 0; $x < $this->moduleCount; $x++){
-				$M_TYPE       = $this->matrix->get($x, $y);
+		foreach($this->matrix->getMatrix() as $y => $row){
+			foreach($row as $x => $M_TYPE){
 				$M_TYPE_LAYER = $M_TYPE;
 
-				if($this->options->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->options->excludeFromConnect)){
+				if($this->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->excludeFromConnect)){
 					// to connect paths we'll redeclare the $M_TYPE_LAYER to data only
 					$M_TYPE_LAYER = QRMatrix::M_DATA;
 
-					if($this->matrix->check($x, $y)){
+					if($this->matrix->isDark($M_TYPE)){
 						$M_TYPE_LAYER = QRMatrix::M_DATA_DARK;
 					}
 				}
 
 				// if we're going to "melt" the matrix, we'll declare *all* modules as dark,
 				// so that light modules with dark parts are rendered in the same path
-				if($this->options->melt){
+				if($melt){
 					$M_TYPE_LAYER |= QRMatrix::IS_DARK;
 				}
 

+ 11 - 9
examples/svgRandomColoredDots.php

@@ -40,26 +40,28 @@ class RandomDotsSVGOutput extends QRMarkupSVG{
 	 * @inheritDoc
 	 */
 	protected function collectModules(Closure $transform):array{
-		$paths = [];
+		$paths     = [];
+		$dotColors = $this->options->dotColors; // avoid magic getter in long loops
 
 		// collect the modules for each type
-		for($y = 0; $y < $this->moduleCount; $y++){
-			for($x = 0; $x < $this->moduleCount; $x++){
-				$M_TYPE       = $this->matrix->get($x, $y);
+		foreach($this->matrix->getMatrix() as $y => $row){
+			foreach($row as $x => $M_TYPE){
 				$M_TYPE_LAYER = $M_TYPE;
 
-				if($this->options->connectPaths
-				   && !$this->matrix->checkTypeIn($x, $y, $this->options->excludeFromConnect)
-				){
+				if($this->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->excludeFromConnect)){
 					// to connect paths we'll redeclare the $M_TYPE_LAYER to data only
-					$M_TYPE_LAYER = $this->matrix->check($x, $y) ? QRMatrix::M_DATA_DARK : QRMatrix::M_DATA;
+					$M_TYPE_LAYER = QRMatrix::M_DATA;
+
+					if($this->matrix->isDark($M_TYPE)){
+						$M_TYPE_LAYER = QRMatrix::M_DATA_DARK;
+					}
 				}
 
 				// randomly assign another $M_TYPE_LAYER for the given types
 				// note that the layer id has to be an integer value,
 				// ideally outside the several bitmask values
 				if($M_TYPE_LAYER === QRMatrix::M_DATA_DARK){
-					$M_TYPE_LAYER = array_rand($this->options->dotColors);
+					$M_TYPE_LAYER = array_rand($dotColors);
 				}
 
 				// collect the modules per $M_TYPE

+ 7 - 7
examples/svgRoundQuietzone.php

@@ -174,19 +174,19 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{
 	 * @inheritDoc
 	 */
 	protected function collectModules(Closure $transform):array{
-		$paths = [];
+		$paths     = [];
+		$dotColors = $this->options->dotColors; // avoid magic getter in long loops
 
 		// collect the modules for each type
-		for($y = 0; $y < $this->moduleCount; $y++){
-			for($x = 0; $x < $this->moduleCount; $x++){
-				$M_TYPE       = $this->matrix->get($x, $y);
+		foreach($this->matrix->getMatrix() as $y => $row){
+			foreach($row as $x => $M_TYPE){
 				$M_TYPE_LAYER = $M_TYPE;
 
-				if(!$this->matrix->checkTypeIn($x, $y, $this->options->excludeFromConnect)){
+				if($this->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->excludeFromConnect)){
 					// to connect paths we'll redeclare the $M_TYPE_LAYER to data only
 					$M_TYPE_LAYER = QRMatrix::M_DATA;
 
-					if($this->matrix->check($x, $y)){
+					if($this->matrix->isDark($M_TYPE)){
 						$M_TYPE_LAYER = QRMatrix::M_DATA_DARK;
 					}
 				}
@@ -195,7 +195,7 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{
 				// note that the layer id has to be an integer value,
 				// ideally outside the several bitmask values
 				if($M_TYPE_LAYER === QRMatrix::M_DATA_DARK){
-					$M_TYPE_LAYER = array_rand($this->options->dotColors);
+					$M_TYPE_LAYER = array_rand($dotColors);
 				}
 
 				// collect the modules per $M_TYPE

+ 1 - 1
examples/svgWithLogoAndCustomShapes.php

@@ -64,7 +64,7 @@ class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG{
 	protected function module(int $x, int $y, int $M_TYPE):string{
 
 		if(
-			!$this->matrix->check($x, $y)
+			!$this->matrix->isDark($M_TYPE)
 			// we're skipping the finder patterns here
 			|| $this->matrix->checkType($x, $y, QRMatrix::M_FINDER)
 			|| $this->matrix->checkType($x, $y, QRMatrix::M_FINDER_DOT)

+ 13 - 1
src/Data/QRMatrix.php

@@ -356,7 +356,19 @@ class QRMatrix{
 	 * Checks whether the module at ($x, $y) is true (dark) or false (light)
 	 */
 	public function check(int $x, int $y):bool{
-		return $this->checkType($x, $y, $this::IS_DARK);
+
+		if(!isset($this->matrix[$y][$x])){
+			return false;
+		}
+
+		return $this->isDark($this->get($x, $y));
+	}
+
+	/**
+	 * Checks whether the given $M_TYPE is a dark value
+	 */
+	public function isDark(int $M_TYPE):bool{
+		return ($M_TYPE & $this::IS_DARK) === $this::IS_DARK;
 	}
 
 	/**

+ 1 - 1
src/Output/QREps.php

@@ -155,7 +155,7 @@ class QREps extends QROutputAbstract{
 	 */
 	protected function module(int $x, int $y, int $M_TYPE):string{
 
-		if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){
+		if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
 			return '';
 		}
 

+ 1 - 1
src/Output/QRFpdf.php

@@ -157,7 +157,7 @@ class QRFpdf extends QROutputAbstract{
 	 */
 	protected function module(int $x, int $y, int $M_TYPE):void{
 
-		if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){
+		if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
 			return;
 		}
 

+ 6 - 5
src/Output/QRGdImage.php

@@ -66,6 +66,7 @@ class QRGdImage extends QROutputAbstract{
 			$this->matrix->invert();
 		}
 
+		$this->copyVars();
 		$this->setMatrixDimensions();
 	}
 
@@ -227,7 +228,7 @@ class QRGdImage extends QROutputAbstract{
 	 */
 	protected function createImage(){
 
-		if($this->options->drawCircularModules && $this->options->scale < 20){
+		if($this->drawCircularModules && $this->options->scale < 20){
 			// increase the initial image size by 10
 			$this->length   *= 10;
 			$this->scale    *= 10;
@@ -289,19 +290,19 @@ class QRGdImage extends QROutputAbstract{
 	 */
 	protected function module(int $x, int $y, int $M_TYPE):void{
 
-		if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){
+		if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
 			return;
 		}
 
 		$color = $this->getModuleValue($M_TYPE);
 
-		if($this->options->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->options->keepAsSquare)){
+		if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
 			imagefilledellipse(
 				$this->image,
 				(($x * $this->scale) + intdiv($this->scale, 2)),
 				(($y * $this->scale) + intdiv($this->scale, 2)),
-				(int)(2 * $this->options->circleRadius * $this->scale),
-				(int)(2 * $this->options->circleRadius * $this->scale),
+				(int)(2 * $this->circleRadius * $this->scale),
+				(int)(2 * $this->circleRadius * $this->scale),
 				$color
 			);
 

+ 3 - 3
src/Output/QRImagick.php

@@ -209,17 +209,17 @@ class QRImagick extends QROutputAbstract{
 	 */
 	protected function module(int $x, int $y, int $M_TYPE):void{
 
-		if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){
+		if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
 			return;
 		}
 
 		$this->imagickDraw->setFillColor($this->getModuleValue($M_TYPE));
 
-		if($this->options->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->options->keepAsSquare)){
+		if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
 			$this->imagickDraw->circle(
 				(($x + 0.5) * $this->scale),
 				(($y + 0.5) * $this->scale),
-				(($x + 0.5 + $this->options->circleRadius) * $this->scale),
+				(($x + 0.5 + $this->circleRadius) * $this->scale),
 				(($y + 0.5) * $this->scale)
 			);
 

+ 3 - 3
src/Output/QRMarkupHTML.php

@@ -27,14 +27,14 @@ class QRMarkupHTML extends QRMarkup{
 			$element = '<span style="background: %s;"></span>';
 			$modules = array_map(fn(int $M_TYPE):string => sprintf($element, $this->getModuleValue($M_TYPE)), $row);
 
-			$rows[]  = sprintf('<div>%s</div>%s', implode('', $modules), $this->options->eol);
+			$rows[]  = sprintf('<div>%s</div>%s', implode('', $modules), $this->eol);
 		}
 
 		$html = sprintf(
 			'<div class="%1$s">%3$s%2$s</div>%3$s',
 			$this->getCssClass(),
 			implode('', $rows),
-			$this->options->eol
+			$this->eol
 		);
 
 		// wrap the snippet into a body when saving to file
@@ -43,7 +43,7 @@ class QRMarkupHTML extends QRMarkup{
 				'<!DOCTYPE html><html lang="none">%2$s<head>%2$s<meta charset="UTF-8">%2$s'.
 					'<title>QR Code</title></head>%2$s<body>%1$s</body>%2$s</html>',
 				$html,
-				$this->options->eol
+				$this->eol
 			);
 		}
 

+ 10 - 10
src/Output/QRMarkupSVG.php

@@ -60,13 +60,13 @@ class QRMarkupSVG extends QRMarkup{
 		$svg = $this->header();
 
 		if(!empty($this->options->svgDefs)){
-			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
+			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->eol);
 		}
 
 		$svg .= $this->paths();
 
 		// close svg
-		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
+		$svg .= sprintf('%1$s</svg>%1$s', $this->eol);
 
 		// transform to data URI only when not saving to file
 		if(!$saveToFile && $this->options->outputBase64){
@@ -100,11 +100,11 @@ class QRMarkupSVG extends QRMarkup{
 			$this->options->cssClass,
 			$this->getViewBox(),
 			$this->options->svgPreserveAspectRatio,
-			$this->options->eol
+			$this->eol
 		);
 
 		if($this->options->svgAddXmlHeader){
-			$header = sprintf('<?xml version="1.0" encoding="UTF-8"?>%s%s', $this->options->eol, $header);
+			$header = sprintf('<?xml version="1.0" encoding="UTF-8"?>%s%s', $this->eol, $header);
 		}
 
 		return $header;
@@ -127,7 +127,7 @@ class QRMarkupSVG extends QRMarkup{
 				$chonks[] = implode(' ', $chunk);
 			}
 
-			$path = implode($this->options->eol, $chonks);
+			$path = implode($this->eol, $chonks);
 
 			if(empty($path)){
 				continue;
@@ -136,7 +136,7 @@ class QRMarkupSVG extends QRMarkup{
 			$svg[] = $this->path($path, $M_TYPE);
 		}
 
-		return implode($this->options->eol, $svg);
+		return implode($this->eol, $svg);
 	}
 
 	/**
@@ -164,7 +164,7 @@ class QRMarkupSVG extends QRMarkup{
 	protected function getCssClass(int $M_TYPE = 0):string{
 		return implode(' ', [
 			'qr-'.($this::LAYERNAMES[$M_TYPE] ?? $M_TYPE),
-			(($M_TYPE & QRMatrix::IS_DARK) === QRMatrix::IS_DARK) ? 'dark' : 'light',
+			$this->matrix->isDark($M_TYPE) ? 'dark' : 'light',
 			$this->options->cssClass,
 		]);
 	}
@@ -176,12 +176,12 @@ class QRMarkupSVG extends QRMarkup{
 	 */
 	protected function module(int $x, int $y, int $M_TYPE):string{
 
-		if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){
+		if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
 			return '';
 		}
 
-		if($this->options->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->options->keepAsSquare)){
-			$r = $this->options->circleRadius;
+		if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
+			$r = $this->circleRadius;
 
 			return sprintf(
 				'M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z',

+ 50 - 12
src/Output/QROutputAbstract.php

@@ -27,13 +27,6 @@ abstract class QROutputAbstract implements QROutputInterface{
 	 */
 	protected int $moduleCount;
 
-	/**
-	 * the current scaling for a QR pixel
-	 *
-	 * @see \chillerlan\QRCode\QROptions::$scale
-	 */
-	protected int $scale;
-
 	/**
 	 * the side length of the QR image (modules * scale)
 	 */
@@ -54,6 +47,23 @@ abstract class QROutputAbstract implements QROutputInterface{
 	 */
 	protected SettingsContainerInterface $options;
 
+	/** @see \chillerlan\QRCode\QROptions::$scale */
+	protected int $scale;
+	/** @see \chillerlan\QRCode\QROptions::$connectPaths */
+	protected bool $connectPaths;
+	/** @see \chillerlan\QRCode\QROptions::$excludeFromConnect */
+	protected array $excludeFromConnect;
+	/** @see \chillerlan\QRCode\QROptions::$eol */
+	protected string $eol;
+	/** @see \chillerlan\QRCode\QROptions::$drawLightModules */
+	protected bool $drawLightModules;
+	/** @see \chillerlan\QRCode\QROptions::$drawCircularModules */
+	protected bool $drawCircularModules;
+	/** @see \chillerlan\QRCode\QROptions::$keepAsSquare */
+	protected array $keepAsSquare;
+	/** @see \chillerlan\QRCode\QROptions::$circleRadius */
+	protected float $circleRadius;
+
 	/**
 	 * QROutputAbstract constructor.
 	 */
@@ -65,10 +75,35 @@ abstract class QROutputAbstract implements QROutputInterface{
 			$this->matrix->invert();
 		}
 
+		$this->copyVars();
 		$this->setMatrixDimensions();
 		$this->setModuleValues();
 	}
 
+	/**
+	 * Creates copies of several QROptions values to avoid calling the magic getters
+	 * in long loops for a significant performance increase.
+	 *
+	 * These variables are usually used in the "module" methods and are called up to 31329 times (at version 40).
+	 */
+	protected function copyVars():void{
+
+		$vars = [
+			'connectPaths',
+			'excludeFromConnect',
+			'eol',
+			'drawLightModules',
+			'drawCircularModules',
+			'keepAsSquare',
+			'circleRadius',
+		];
+
+		foreach($vars as $property){
+			$this->{$property} = $this->options->{$property};
+		}
+
+	}
+
 	/**
 	 * Sets/updates the matrix dimensions
 	 *
@@ -193,14 +228,17 @@ abstract class QROutputAbstract implements QROutputInterface{
 		$paths = [];
 
 		// collect the modules for each type
-		for($y = 0; $y < $this->moduleCount; $y++){
-			for($x = 0; $x < $this->moduleCount; $x++){
-				$M_TYPE       = $this->matrix->get($x, $y);
+		foreach($this->matrix->getMatrix() as $y => $row){
+			foreach($row as $x => $M_TYPE){
 				$M_TYPE_LAYER = $M_TYPE;
 
-				if($this->options->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->options->excludeFromConnect)){
+				if($this->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->excludeFromConnect)){
 					// to connect paths we'll redeclare the $M_TYPE_LAYER to data only
-					$M_TYPE_LAYER = $this->matrix->check($x, $y) ? QRMatrix::M_DATA_DARK : QRMatrix::M_DATA;
+					$M_TYPE_LAYER = QRMatrix::M_DATA;
+
+					if($this->matrix->isDark($M_TYPE)){
+						$M_TYPE_LAYER = QRMatrix::M_DATA_DARK;
+					}
 				}
 
 				// collect the modules per $M_TYPE

+ 4 - 3
src/Output/QRString.php

@@ -64,7 +64,8 @@ class QRString extends QROutputAbstract{
 	 * string output
 	 */
 	protected function text():string{
-		$lines = [];
+		$lines     = [];
+		$linestart = $this->options->textLineStart;
 
 		for($y = 0; $y < $this->moduleCount; $y++){
 			$r = [];
@@ -73,10 +74,10 @@ class QRString extends QROutputAbstract{
 				$r[] = $this->getModuleValueAt($x, $y);
 			}
 
-			$lines[] = $this->options->textLineStart.implode('', $r);
+			$lines[] = $linestart.implode('', $r);
 		}
 
-		return implode($this->options->eol, $lines);
+		return implode($this->eol, $lines);
 	}
 
 	/**