Issues (1270)

lib/phpqrcode/qrmask.php (6 issues)

1
<?php
2
/*
3
 * PHP QR Code encoder
4
 *
5
 * Masking
6
 *
7
 * Based on libqrencode C library distributed under LGPL 2.1
8
 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <[email protected]>
9
 *
10
 * PHP QR Code is distributed under LGPL 3
11
 * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
12
 *
13
 * This library is free software; you can redistribute it and/or
14
 * modify it under the terms of the GNU Lesser General Public
15
 * License as published by the Free Software Foundation; either
16
 * version 3 of the License, or any later version.
17
 *
18
 * This library is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
 * Lesser General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Lesser General Public
24
 * License along with this library; if not, write to the Free Software
25
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
 */
27
 
28
    define('N1', 3);
29
    define('N2', 3);
30
    define('N3', 40);
31
    define('N4', 10);
32
33
    class QRmask {
34
	
35
        public $runLength = array();
36
		
37
        //----------------------------------------------------------------------
38
        public function __construct() 
39
        {
40
            $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
41
        }
42
        
43
        //----------------------------------------------------------------------
44
        public function writeFormatInformation($width, &$frame, $mask, $level)
45
        {
46
            $blacks = 0;
47
            $format = QRspec::getFormatInfo($mask, $level);
48
49
            for ($i = 0; $i < 8; $i++) {
50
                if ($format & 1) {
51
                    $blacks += 2;
52
                    $v = 0x85;
53
                } else {
54
                    $v = 0x84;
55
                }
56
                
57
                $frame[8][$width - 1 - $i] = chr($v);
58
                if ($i < 6) {
59
                    $frame[$i][8] = chr($v);
60
                } else {
61
                    $frame[$i + 1][8] = chr($v);
62
                }
63
                $format = $format >> 1;
64
            }
65
            
66
            for ($i = 0; $i < 7; $i++) {
67
                if ($format & 1) {
68
                    $blacks += 2;
69
                    $v = 0x85;
70
                } else {
71
                    $v = 0x84;
72
                }
73
                
74
                $frame[$width - 7 + $i][8] = chr($v);
75
                if ($i == 0) {
76
                    $frame[8][7] = chr($v);
77
                } else {
78
                    $frame[8][6 - $i] = chr($v);
79
                }
80
                
81
                $format = $format >> 1;
82
            }
83
84
            return $blacks;
85
        }
86
        
87
        //----------------------------------------------------------------------
88
        public function mask0($x, $y) { return ($x + $y) & 1; }
89
        public function mask1($x, $y) { return ($y & 1); }
90
        public function mask2($x, $y) { return ($x % 3); }
91
        public function mask3($x, $y) { return ($x + $y) % 3; }
92
        public function mask4($x, $y) { return (((int) ($y / 2)) + ((int) ($x / 3))) & 1; }
93
        public function mask5($x, $y) { return (($x * $y) & 1) + ($x * $y) % 3; }
94
        public function mask6($x, $y) { return ((($x * $y) & 1) + ($x * $y) % 3) & 1; }
95
        public function mask7($x, $y) { return ((($x * $y) % 3) + (($x + $y) & 1)) & 1; }
96
        
97
        //----------------------------------------------------------------------
98
        private function generateMaskNo($maskNo, $width, $frame)
99
        {
100
            $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
101
            
102
            for ($y = 0; $y < $width; $y++) {
103
                for ($x = 0; $x < $width; $x++) {
104
                    if (ord($frame[$y][$x]) & 0x80) {
105
                        $bitMask[$y][$x] = 0;
106
                    } else {
107
                        $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
108
                        $bitMask[$y][$x] = ($maskFunc == 0) ? 1 : 0;
109
                    }
110
                    
111
                }
112
            }
113
            
114
            return $bitMask;
115
        }
116
        
117
        //----------------------------------------------------------------------
118
        public static function serial($bitFrame)
119
        {
120
            $codeArr = array();
121
            
122
            foreach ($bitFrame as $line) {
123
                            $codeArr[] = join('', $line);
124
            }
125
                
126
            return gzcompress(join("\n", $codeArr), 9);
127
        }
128
        
129
        //----------------------------------------------------------------------
130
        public static function unserial($code)
131
        {
132
            $codeArr = array();
133
            
134
            $codeLines = explode("\n", gzuncompress($code));
135
            foreach ($codeLines as $line) {
136
                            $codeArr[] = str_split($line);
137
            }
138
            
139
            return $codeArr;
140
        }
141
        
142
        //----------------------------------------------------------------------
143
        public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false) 
144
        {
145
            $b = 0;
146
            $bitMask = array();
147
            
148
            $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
149
150
            if (QR_CACHEABLE) {
151
                if (file_exists($fileName)) {
152
                    $bitMask = self::unserial(file_get_contents($fileName));
153
                } else {
154
                    $bitMask = $this->generateMaskNo($maskNo, $width, $s);
155
                    if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo)) {
156
                                            mkdir(QR_CACHE_DIR.'mask_'.$maskNo);
157
                    }
158
                    file_put_contents($fileName, self::serial($bitMask));
159
                }
160
            } else {
161
                $bitMask = $this->generateMaskNo($maskNo, $width, $s);
162
            }
163
164
            if ($maskGenOnly) {
165
                            return;
166
            }
167
                
168
            $d = $s;
169
170
            for ($y = 0; $y < $width; $y++) {
171
                for ($x = 0; $x < $width; $x++) {
172
                    if ($bitMask[$y][$x] == 1) {
173
                        $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int) $bitMask[$y][$x]);
174
                    }
175
                    $b += (int) (ord($d[$y][$x]) & 1);
176
                }
177
            }
178
179
            return $b;
180
        }
181
        
182
        //----------------------------------------------------------------------
183
        public function makeMask($width, $frame, $maskNo, $level)
184
        {
185
            $masked = array_fill(0, $width, str_repeat("\0", $width));
186
            $this->makeMaskNo($maskNo, $width, $frame, $masked);
187
            $this->writeFormatInformation($width, $masked, $maskNo, $level);
188
       
189
            return $masked;
190
        }
191
        
192
        //----------------------------------------------------------------------
193
        public function calcN1N3($length)
194
        {
195
            $demerit = 0;
196
197
            for ($i = 0; $i < $length; $i++) {
198
                
199
                if ($this->runLength[$i] >= 5) {
200
                    $demerit += (N1 + ($this->runLength[$i] - 5));
201
                }
202
                if ($i & 1) {
203
                    if (($i >= 3) && ($i < ($length - 2)) && ($this->runLength[$i] % 3 == 0)) {
204
                        $fact = (int) ($this->runLength[$i] / 3);
205
                        if (($this->runLength[$i - 2] == $fact) &&
206
                           ($this->runLength[$i - 1] == $fact) &&
207
                           ($this->runLength[$i + 1] == $fact) &&
208
                           ($this->runLength[$i + 2] == $fact)) {
209
                            if (($this->runLength[$i - 3] < 0) || ($this->runLength[$i - 3] >= (4 * $fact))) {
210
                                $demerit += N3;
211
                            } else if ((($i + 3) >= $length) || ($this->runLength[$i + 3] >= (4 * $fact))) {
212
                                $demerit += N3;
213
                            }
214
                        }
215
                    }
216
                }
217
            }
218
            return $demerit;
219
        }
220
        
221
        //----------------------------------------------------------------------
222
        public function evaluateSymbol($width, $frame)
223
        {
224
            $head = 0;
225
            $demerit = 0;
226
227
            for ($y = 0; $y < $width; $y++) {
228
                $head = 0;
229
                $this->runLength[0] = 1;
230
                
231
                $frameY = $frame[$y];
232
                
233
                if ($y > 0) {
234
                                    $frameYM = $frame[$y - 1];
235
                }
236
                
237
                for ($x = 0; $x < $width; $x++) {
238
                    if (($x > 0) && ($y > 0)) {
239
                        $b22 = ord($frameY[$x]) & ord($frameY[$x - 1]) & ord($frameYM[$x]) & ord($frameYM[$x - 1]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $frameYM does not seem to be defined for all execution paths leading up to this point.
Loading history...
240
                        $w22 = ord($frameY[$x]) | ord($frameY[$x - 1]) | ord($frameYM[$x]) | ord($frameYM[$x - 1]);
241
                        
242
                        if (($b22 | ($w22 ^ 1)) & 1) {                                                                     
243
                            $demerit += N2;
244
                        }
245
                    }
246
                    if (($x == 0) && (ord($frameY[$x]) & 1)) {
247
                        $this->runLength[0] = -1;
248
                        $head = 1;
249
                        $this->runLength[$head] = 1;
250
                    } else if ($x > 0) {
251
                        if ((ord($frameY[$x]) ^ ord($frameY[$x - 1])) & 1) {
252
                            $head++;
253
                            $this->runLength[$head] = 1;
254
                        } else {
255
                            $this->runLength[$head]++;
256
                        }
257
                    }
258
                }
259
    
260
                $demerit += $this->calcN1N3($head + 1);
261
            }
262
263
            for ($x = 0; $x < $width; $x++) {
264
                $head = 0;
265
                $this->runLength[0] = 1;
266
                
267
                for ($y = 0; $y < $width; $y++) {
268
                    if ($y == 0 && (ord($frame[$y][$x]) & 1)) {
269
                        $this->runLength[0] = -1;
270
                        $head = 1;
271
                        $this->runLength[$head] = 1;
272
                    } else if ($y > 0) {
273
                        if ((ord($frame[$y][$x]) ^ ord($frame[$y - 1][$x])) & 1) {
274
                            $head++;
275
                            $this->runLength[$head] = 1;
276
                        } else {
277
                            $this->runLength[$head]++;
278
                        }
279
                    }
280
                }
281
            
282
                $demerit += $this->calcN1N3($head + 1);
283
            }
284
285
            return $demerit;
286
        }
287
        
288
        
289
        //----------------------------------------------------------------------
290
        public function mask($width, $frame, $level)
291
        {
292
            $minDemerit = PHP_INT_MAX;
293
            $bestMaskNum = 0;
0 ignored issues
show
The assignment to $bestMaskNum is dead and can be removed.
Loading history...
294
            $bestMask = array();
0 ignored issues
show
The assignment to $bestMask is dead and can be removed.
Loading history...
295
            
296
            $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
297
            
298
            if (QR_FIND_FROM_RANDOM !== false) {
0 ignored issues
show
The condition QR_FIND_FROM_RANDOM !== false is always false.
Loading history...
299
            
300
                $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9);
301
                for ($i = 0; $i < $howManuOut; $i++) {
302
                    $remPos = rand(0, count($checked_masks) - 1);
303
                    unset($checked_masks[$remPos]);
304
                    $checked_masks = array_values($checked_masks);
305
                }
306
            
307
            }
308
            
309
            $bestMask = $frame;
310
             
311
            foreach ($checked_masks as $i) {
312
                $mask = array_fill(0, $width, str_repeat("\0", $width));
313
314
                $demerit = 0;
0 ignored issues
show
The assignment to $demerit is dead and can be removed.
Loading history...
315
                $blacks = 0;
0 ignored issues
show
The assignment to $blacks is dead and can be removed.
Loading history...
316
                $blacks  = $this->makeMaskNo($i, $width, $frame, $mask);
317
                $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
318
                $blacks  = (int) (100 * $blacks / ($width * $width));
319
                $demerit = (int) ((int) (abs($blacks - 50) / 5) * N4);
320
                $demerit += $this->evaluateSymbol($width, $mask);
321
                
322
                if ($demerit < $minDemerit) {
323
                    $minDemerit = $demerit;
324
                    $bestMask = $mask;
325
                    $bestMaskNum = $i;
326
                }
327
            }
328
            
329
            return $bestMask;
330
        }
331
        
332
        //----------------------------------------------------------------------
333
    }
334