MaskPatternTester.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <?php
  2. /**
  3. * Class MaskPatternTester
  4. *
  5. * @filesource MaskPatternTester.php
  6. * @created 22.11.2017
  7. * @package chillerlan\QRCode\Data
  8. * @author Smiley <smiley@chillerlan.net>
  9. * @copyright 2017 Smiley
  10. * @license MIT
  11. */
  12. namespace chillerlan\QRCode\Data;
  13. use function abs, call_user_func;
  14. /**
  15. * The sole purpose of this class is to receive a QRMatrix object and run the pattern tests on it.
  16. *
  17. * @link http://www.thonky.com/qr-code-tutorial/data-masking
  18. */
  19. class MaskPatternTester{
  20. protected QRMatrix $matrix;
  21. protected int $moduleCount;
  22. /**
  23. * Receives the matrix an sets the module count
  24. *
  25. * @see \chillerlan\QRCode\QROptions::$maskPattern
  26. * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
  27. * @see \chillerlan\QRCode\QRCode::getBestMaskPattern()
  28. */
  29. public function __construct(QRMatrix $matrix){
  30. $this->matrix = $matrix;
  31. $this->moduleCount = $this->matrix->size();
  32. }
  33. /**
  34. * Returns the penalty for the given mask pattern
  35. *
  36. * @see \chillerlan\QRCode\QROptions::$maskPattern
  37. * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
  38. * @see \chillerlan\QRCode\QRCode::getBestMaskPattern()
  39. */
  40. public function testPattern():int{
  41. $penalty = 0;
  42. for($level = 1; $level <= 4; $level++){
  43. $penalty += call_user_func([$this, 'testLevel'.$level]);
  44. }
  45. return (int)$penalty;
  46. }
  47. /**
  48. * Checks for each group of five or more same-colored modules in a row (or column)
  49. */
  50. protected function testLevel1():float{
  51. $penalty = 0;
  52. foreach($this->matrix->matrix() as $y => $row){
  53. foreach($row as $x => $val){
  54. $count = 0;
  55. for($ry = -1; $ry <= 1; $ry++){
  56. if($y + $ry < 0 || $this->moduleCount <= $y + $ry){
  57. continue;
  58. }
  59. for($rx = -1; $rx <= 1; $rx++){
  60. if(($ry === 0 && $rx === 0) || ($x + $rx < 0 || $this->moduleCount <= $x + $rx)){
  61. continue;
  62. }
  63. if($this->matrix->check($x + $rx, $y + $ry) === ($val >> 8 > 0)){
  64. $count++;
  65. }
  66. }
  67. }
  68. if($count > 5){
  69. $penalty += (3 + $count - 5);
  70. }
  71. }
  72. }
  73. return $penalty;
  74. }
  75. /**
  76. * Checks for each 2x2 area of same-colored modules in the matrix
  77. */
  78. protected function testLevel2():float{
  79. $penalty = 0;
  80. foreach($this->matrix->matrix() as $y => $row){
  81. if($y > $this->moduleCount - 2){
  82. break;
  83. }
  84. foreach($row as $x => $val){
  85. if($x > $this->moduleCount - 2){
  86. break;
  87. }
  88. $count = 0;
  89. if($val >> 8 > 0){
  90. $count++;
  91. }
  92. if($this->matrix->check($y, $x + 1)){
  93. $count++;
  94. }
  95. if($this->matrix->check($y + 1, $x)){
  96. $count++;
  97. }
  98. if($this->matrix->check($y + 1, $x + 1)){
  99. $count++;
  100. }
  101. if($count === 0 || $count === 4){
  102. $penalty += 3;
  103. }
  104. }
  105. }
  106. return $penalty;
  107. }
  108. /**
  109. * Checks if there are patterns that look similar to the finder patterns
  110. */
  111. protected function testLevel3():float{
  112. $penalty = 0;
  113. foreach($this->matrix->matrix() as $y => $row){
  114. foreach($row as $x => $val){
  115. if($x <= $this->moduleCount - 7){
  116. if(
  117. $this->matrix->check($x , $y)
  118. && !$this->matrix->check($x + 1, $y)
  119. && $this->matrix->check($x + 2, $y)
  120. && $this->matrix->check($x + 3, $y)
  121. && $this->matrix->check($x + 4, $y)
  122. && !$this->matrix->check($x + 5, $y)
  123. && $this->matrix->check($x + 6, $y)
  124. ){
  125. $penalty += 40;
  126. }
  127. }
  128. if($y <= $this->moduleCount - 7){
  129. if(
  130. $this->matrix->check($x, $y)
  131. && !$this->matrix->check($x, $y + 1)
  132. && $this->matrix->check($x, $y + 2)
  133. && $this->matrix->check($x, $y + 3)
  134. && $this->matrix->check($x, $y + 4)
  135. && !$this->matrix->check($x, $y + 5)
  136. && $this->matrix->check($x, $y + 6)
  137. ){
  138. $penalty += 40;
  139. }
  140. }
  141. }
  142. }
  143. return $penalty;
  144. }
  145. /**
  146. * Checks if more than half of the modules are dark or light, with a larger penalty for a larger difference
  147. */
  148. protected function testLevel4():float{
  149. $count = 0;
  150. foreach($this->matrix->matrix() as $y => $row){
  151. foreach($row as $x => $val){
  152. if($val >> 8 > 0){
  153. $count++;
  154. }
  155. }
  156. }
  157. return (abs(100 * $count / $this->moduleCount / $this->moduleCount - 50) / 5) * 10;
  158. }
  159. }