MaskPatternTester.php 4.3 KB

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