* @copyright 2017 Smiley * @license MIT */ namespace YoastSEO_Vendor\chillerlan\QRCode\Data; /** * The sole purpose of this class is to receive a QRMatrix object and run the pattern tests on it. * * @link http://www.thonky.com/qr-code-tutorial/data-masking */ class MaskPatternTester { /** * @var \chillerlan\QRCode\Data\QRMatrix */ protected $matrix; /** * @var int */ protected $moduleCount; /** * Receives the matrix an sets the module count * * @see \chillerlan\QRCode\QROptions::$maskPattern * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern * @see \chillerlan\QRCode\QRCode::getBestMaskPattern() * * @param \chillerlan\QRCode\Data\QRMatrix $matrix * * @return \chillerlan\QRCode\Data\MaskPatternTester */ public function setMatrix(\YoastSEO_Vendor\chillerlan\QRCode\Data\QRMatrix $matrix) { $this->matrix = $matrix; $this->moduleCount = $this->matrix->size(); return $this; } /** * Returns the penalty for the given mask pattern * * @see \chillerlan\QRCode\QROptions::$maskPattern * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern * @see \chillerlan\QRCode\QRCode::getBestMaskPattern() * * @return int */ public function testPattern() { $penalty = 0; for ($level = 1; $level <= 4; $level++) { $penalty += \call_user_func_array([$this, 'testLevel' . $level], [$this->matrix->matrix(\true)]); } return (int) $penalty; } /** * Checks for each group of five or more same-colored modules in a row (or column) * * @return int */ protected function testLevel1(array $m) { $penalty = 0; foreach ($m as $y => $row) { foreach ($row as $x => $val) { $count = 0; for ($ry = -1; $ry <= 1; $ry++) { if ($y + $ry < 0 || $this->moduleCount <= $y + $ry) { continue; } for ($rx = -1; $rx <= 1; $rx++) { if ($ry === 0 && $rx === 0 || ($x + $rx < 0 || $this->moduleCount <= $x + $rx)) { continue; } if ($m[$y + $ry][$x + $rx] === $val) { $count++; } } } if ($count > 5) { $penalty += 3 + $count - 5; } } } return $penalty; } /** * Checks for each 2x2 area of same-colored modules in the matrix * * @return int */ protected function testLevel2(array $m) { $penalty = 0; foreach ($m as $y => $row) { if ($y > $this->moduleCount - 2) { break; } foreach ($row as $x => $val) { if ($x > $this->moduleCount - 2) { break; } if ($val === $m[$y][$x + 1] && $val === $m[$y + 1][$x] && $val === $m[$y + 1][$x + 1]) { $penalty++; } } } return 3 * $penalty; } /** * Checks if there are patterns that look similar to the finder patterns (1:1:3:1:1 ratio) * * @return int */ protected function testLevel3(array $m) { $penalties = 0; foreach ($m as $y => $row) { foreach ($row as $x => $val) { if ($x + 6 < $this->moduleCount && $val && !$m[$y][$x + 1] && $m[$y][$x + 2] && $m[$y][$x + 3] && $m[$y][$x + 4] && !$m[$y][$x + 5] && $m[$y][$x + 6]) { $penalties++; } if ($y + 6 < $this->moduleCount && $val && !$m[$y + 1][$x] && $m[$y + 2][$x] && $m[$y + 3][$x] && $m[$y + 4][$x] && !$m[$y + 5][$x] && $m[$y + 6][$x]) { $penalties++; } } } return $penalties * 40; } /** * Checks if more than half of the modules are dark or light, with a larger penalty for a larger difference * * @return float */ protected function testLevel4(array $m) { $count = 0; foreach ($m as $y => $row) { foreach ($row as $x => $val) { if ($val) { $count++; } } } return \abs(100 * $count / $this->moduleCount / $this->moduleCount - 50) / 5 * 10; } }