1414use chillerlan \QRCode \Data \QRMatrix ;
1515use chillerlan \QRCode \QRCodeException ;
1616use Closure ;
17- use function abs , array_search , count , intdiv , min ;
17+ use function abs , array_column , array_search , intdiv , min ;
1818
1919/**
2020 * ISO/IEC 18004:2000 Section 8.8.1
@@ -55,6 +55,16 @@ final class MaskPattern{
5555 self ::PATTERN_111 ,
5656 ];
5757
58+ /*
59+ * Penalty scores
60+ *
61+ * ISO/IEC 18004:2000 Section 8.8.1 - Table 24
62+ */
63+ private const PENALTY_N1 = 3 ;
64+ private const PENALTY_N2 = 3 ;
65+ private const PENALTY_N3 = 40 ;
66+ private const PENALTY_N4 = 10 ;
67+
5868 /**
5969 * The current mask pattern value (0-7)
6070 */
@@ -114,14 +124,15 @@ public function getMask():Closure{
114124 */
115125 public static function getBestPattern (QRMatrix $ QRMatrix ):self {
116126 $ penalties = [];
127+ $ size = $ QRMatrix ->getSize ();
117128
118129 foreach (self ::PATTERNS as $ pattern ){
119130 $ mp = new self ($ pattern );
120131 $ matrix = (clone $ QRMatrix )->setFormatInfo ($ mp )->mask ($ mp )->getMatrix (true );
121132 $ penalty = 0 ;
122133
123134 for ($ level = 1 ; $ level <= 4 ; $ level ++){
124- $ penalty += self ::{'testRule ' .$ level }($ matrix , count ( $ matrix ), count ( $ matrix [ 0 ]) );
135+ $ penalty += self ::{'testRule ' .$ level }($ matrix , $ size , $ size );
125136 }
126137
127138 $ penalties [$ pattern ] = (int )$ penalty ;
@@ -135,42 +146,49 @@ public static function getBestPattern(QRMatrix $QRMatrix):self{
135146 * give penalty to them. Example: 00000 or 11111.
136147 */
137148 public static function testRule1 (array $ matrix , int $ height , int $ width ):int {
138- return (self ::applyRule1 ($ matrix , $ height , $ width , true ) + self ::applyRule1 ($ matrix , $ height , $ width , false ));
149+ $ penalty = 0 ;
150+
151+ // horizontal
152+ foreach ($ matrix as $ row ){
153+ $ penalty += self ::applyRule1 ($ row );
154+ }
155+
156+ // vertical
157+ for ($ x = 0 ; $ x < $ width ; $ x ++){
158+ $ penalty += self ::applyRule1 (array_column ($ matrix , $ x ));
159+ }
160+
161+ return $ penalty ;
139162 }
140163
141164 /**
142165 *
143166 */
144- private static function applyRule1 (array $ matrix , int $ height , int $ width , bool $ isHorizontal ):int {
145- $ penalty = 0 ;
146- $ yLimit = ( $ isHorizontal ) ? $ height : $ width ;
147- $ xLimit = ( $ isHorizontal ) ? $ width : $ height ;
167+ private static function applyRule1 (array $ rc ):int {
168+ $ penalty = 0 ;
169+ $ numSameBitCells = 0 ;
170+ $ prevBit = null ;
148171
149- for ($ y = 0 ; $ y < $ yLimit ; $ y ++){
150- $ numSameBitCells = 0 ;
151- $ prevBit = null ;
172+ foreach ($ rc as $ val ){
152173
153- for ($ x = 0 ; $ x < $ xLimit ; $ x ++){
154- $ bit = ($ isHorizontal ) ? $ matrix [$ y ][$ x ] : $ matrix [$ x ][$ y ];
174+ if ($ val === $ prevBit ){
175+ $ numSameBitCells ++;
176+ }
177+ else {
155178
156- if ($ bit === $ prevBit ){
157- $ numSameBitCells ++ ;
179+ if ($ numSameBitCells >= 5 ){
180+ $ penalty += ( self :: PENALTY_N1 + $ numSameBitCells - 5 ) ;
158181 }
159- else {
160-
161- if ($ numSameBitCells >= 5 ){
162- $ penalty += (3 + ($ numSameBitCells - 5 ));
163- }
164182
165- $ numSameBitCells = 1 ; // Include the cell itself.
166- $ prevBit = $ bit ;
167- }
168- }
169- if ($ numSameBitCells >= 5 ){
170- $ penalty += (3 + ($ numSameBitCells - 5 ));
183+ $ numSameBitCells = 1 ; // Include the cell itself.
184+ $ prevBit = $ val ;
171185 }
172186 }
173187
188+ if ($ numSameBitCells >= 5 ){
189+ $ penalty += (self ::PENALTY_N1 + $ numSameBitCells - 5 );
190+ }
191+
174192 return $ penalty ;
175193 }
176194
@@ -195,7 +213,7 @@ public static function testRule2(array $matrix, int $height, int $width):int{
195213 }
196214
197215 if (
198- $ val === $ row [($ x + 1 )]
216+ $ val === $ row [($ x + 1 )]
199217 && $ val === $ matrix [($ y + 1 )][$ x ]
200218 && $ val === $ matrix [($ y + 1 )][($ x + 1 )]
201219 ){
@@ -204,7 +222,7 @@ public static function testRule2(array $matrix, int $height, int $width):int{
204222 }
205223 }
206224
207- return (3 * $ penalty );
225+ return (self :: PENALTY_N2 * $ penalty );
208226 }
209227
210228 /**
@@ -255,7 +273,7 @@ public static function testRule3(array $matrix, int $height, int $width):int{
255273 }
256274 }
257275
258- return ($ penalties * 40 );
276+ return ($ penalties * self :: PENALTY_N3 );
259277 }
260278
261279 /**
@@ -310,7 +328,7 @@ public static function testRule4(array $matrix, int $height, int $width):int{
310328 }
311329 }
312330
313- return (intdiv ((abs ($ darkCells * 2 - $ totalCells ) * 10 ), $ totalCells ) * 10 );
331+ return (intdiv ((abs ($ darkCells * 2 - $ totalCells ) * 10 ), $ totalCells ) * self :: PENALTY_N4 );
314332 }
315333
316334}
0 commit comments