Skip to content

Commit

Permalink
:octocat: mask pattern testing performance improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
codemasher committed Oct 22, 2023
1 parent af7ba49 commit 3c2d23d
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 36 deletions.
76 changes: 47 additions & 29 deletions src/Common/MaskPattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use chillerlan\QRCode\Data\QRMatrix;
use chillerlan\QRCode\QRCodeException;
use Closure;
use function abs, array_search, count, intdiv, min;
use function abs, array_column, array_search, intdiv, min;

/**
* ISO/IEC 18004:2000 Section 8.8.1
Expand Down Expand Up @@ -55,6 +55,16 @@ final class MaskPattern{
self::PATTERN_111,
];

/*
* Penalty scores
*
* ISO/IEC 18004:2000 Section 8.8.1 - Table 24
*/
private const PENALTY_N1 = 3;
private const PENALTY_N2 = 3;
private const PENALTY_N3 = 40;
private const PENALTY_N4 = 10;

/**
* The current mask pattern value (0-7)
*/
Expand Down Expand Up @@ -114,14 +124,15 @@ public function getMask():Closure{
*/
public static function getBestPattern(QRMatrix $QRMatrix):self{
$penalties = [];
$size = $QRMatrix->getSize();

foreach(self::PATTERNS as $pattern){
$mp = new self($pattern);
$matrix = (clone $QRMatrix)->setFormatInfo($mp)->mask($mp)->getMatrix(true);
$penalty = 0;

for($level = 1; $level <= 4; $level++){
$penalty += self::{'testRule'.$level}($matrix, count($matrix), count($matrix[0]));
$penalty += self::{'testRule'.$level}($matrix, $size, $size);
}

$penalties[$pattern] = (int)$penalty;
Expand All @@ -135,42 +146,49 @@ public static function getBestPattern(QRMatrix $QRMatrix):self{
* give penalty to them. Example: 00000 or 11111.
*/
public static function testRule1(array $matrix, int $height, int $width):int{
return (self::applyRule1($matrix, $height, $width, true) + self::applyRule1($matrix, $height, $width, false));
$penalty = 0;

// horizontal
foreach($matrix as $row){
$penalty += self::applyRule1($row);
}

// vertical
for($x = 0; $x < $width; $x++){
$penalty += self::applyRule1(array_column($matrix, $x));
}

return $penalty;
}

/**
*
*/
private static function applyRule1(array $matrix, int $height, int $width, bool $isHorizontal):int{
$penalty = 0;
$yLimit = ($isHorizontal) ? $height : $width;
$xLimit = ($isHorizontal) ? $width : $height;
private static function applyRule1(array $rc):int{
$penalty = 0;
$numSameBitCells = 0;
$prevBit = null;

for($y = 0; $y < $yLimit; $y++){
$numSameBitCells = 0;
$prevBit = null;
foreach($rc as $val){

for($x = 0; $x < $xLimit; $x++){
$bit = ($isHorizontal) ? $matrix[$y][$x] : $matrix[$x][$y];
if($val === $prevBit){
$numSameBitCells++;
}
else{

if($bit === $prevBit){
$numSameBitCells++;
if($numSameBitCells >= 5){
$penalty += (self::PENALTY_N1 + $numSameBitCells - 5);
}
else{

if($numSameBitCells >= 5){
$penalty += (3 + ($numSameBitCells - 5));
}

$numSameBitCells = 1; // Include the cell itself.
$prevBit = $bit;
}
}
if($numSameBitCells >= 5){
$penalty += (3 + ($numSameBitCells - 5));
$numSameBitCells = 1; // Include the cell itself.
$prevBit = $val;
}
}

if($numSameBitCells >= 5){
$penalty += (self::PENALTY_N1 + $numSameBitCells - 5);
}

return $penalty;
}

Expand All @@ -195,7 +213,7 @@ public static function testRule2(array $matrix, int $height, int $width):int{
}

if(
$val === $row[($x + 1)]
$val === $row[($x + 1)]
&& $val === $matrix[($y + 1)][$x]
&& $val === $matrix[($y + 1)][($x + 1)]
){
Expand All @@ -204,7 +222,7 @@ public static function testRule2(array $matrix, int $height, int $width):int{
}
}

return (3 * $penalty);
return (self::PENALTY_N2 * $penalty);
}

/**
Expand Down Expand Up @@ -255,7 +273,7 @@ public static function testRule3(array $matrix, int $height, int $width):int{
}
}

return ($penalties * 40);
return ($penalties * self::PENALTY_N3);
}

/**
Expand Down Expand Up @@ -310,7 +328,7 @@ public static function testRule4(array $matrix, int $height, int $width):int{
}
}

return (intdiv((abs($darkCells * 2 - $totalCells) * 10), $totalCells) * 10);
return (intdiv((abs($darkCells * 2 - $totalCells) * 10), $totalCells) * self::PENALTY_N4);
}

}
10 changes: 3 additions & 7 deletions src/Data/QRMatrix.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,10 @@ public function getMatrix(bool $boolean = null):array{
return $this->matrix;
}

$matrix = [];
$matrix = $this->matrix;

for($y = 0; $y < $this->moduleCount; $y++){
$matrix[$y] = [];

for($x = 0; $x < $this->moduleCount; $x++){
$matrix[$y][$x] = $this->checkType($x, $y, $this::IS_DARK);
}
foreach($matrix as &$row){
$row = array_map([$this, 'isDark'], $row);
}

return $matrix;
Expand Down

0 comments on commit 3c2d23d

Please sign in to comment.