:octocat: significant output performance improvements

This commit is contained in:
smiley
2023-10-16 18:27:45 +02:00
parent 2327690e99
commit 90f4a4bc66
13 changed files with 116 additions and 62 deletions
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
/**