mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-09-16 04:36:26 +00:00
:octocat: significant output performance improvements
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 '';
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
|
||||
@@ -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
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user