Issues (4542)

config/qrcode/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 = [];
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)
89
        {
90
            return ($x + $y) & 1;
91
        }
92
93
        public function mask1($x, $y)
94
        {
95
            return $y & 1;
96
        }
97
98
        public function mask2($x, $y)
99
        {
100
            return $x % 3;
101
        }
102
103
        public function mask3($x, $y)
104
        {
105
            return ($x + $y) % 3;
106
        }
107
108
        public function mask4($x, $y)
109
        {
110
            return (((int) ($y / 2)) + ((int) ($x / 3))) & 1;
111
        }
112
113
        public function mask5($x, $y)
114
        {
115
            return (($x * $y) & 1) + ($x * $y) % 3;
116
        }
117
118
        public function mask6($x, $y)
119
        {
120
            return ((($x * $y) & 1) + ($x * $y) % 3) & 1;
121
        }
122
123
        public function mask7($x, $y)
124
        {
125
            return ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
126
        }
127
128
        //----------------------------------------------------------------------
129
        private function generateMaskNo($maskNo, $width, $frame)
130
        {
131
            $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
132
133
            for ($y = 0; $y < $width; $y++) {
134
                for ($x = 0; $x < $width; $x++) {
135
                    if (ord($frame[$y][$x]) & 0x80) {
136
                        $bitMask[$y][$x] = 0;
137
                    } else {
138
                        $maskFunc = call_user_func([$this, 'mask'.$maskNo], $x, $y);
139
                        $bitMask[$y][$x] = ($maskFunc == 0) ? 1 : 0;
140
                    }
141
                }
142
            }
143
144
            return $bitMask;
145
        }
146
147
        //----------------------------------------------------------------------
148
        public static function serial($bitFrame)
149
        {
150
            $codeArr = [];
151
152
            foreach ($bitFrame as $line) {
153
                $codeArr[] = implode('', $line);
154
            }
155
156
            return gzcompress(implode("\n", $codeArr), 9);
157
        }
158
159
        //----------------------------------------------------------------------
160
        public static function unserial($code)
161
        {
162
            $codeArr = [];
163
164
            $codeLines = explode("\n", gzuncompress($code));
165
            foreach ($codeLines as $line) {
166
                $codeArr[] = str_split($line);
167
            }
168
169
            return $codeArr;
170
        }
171
172
        //----------------------------------------------------------------------
173
        public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
174
        {
175
            $b = 0;
176
            $bitMask = [];
177
178
            $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
179
180
            if (QR_CACHEABLE) {
181
                if (file_exists($fileName)) {
182
                    $bitMask = self::unserial(file_get_contents($fileName));
183
                } else {
184
                    $bitMask = $this->generateMaskNo($maskNo, $width, $s);
185
                    if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo)) {
186
                        mkdir(QR_CACHE_DIR.'mask_'.$maskNo);
187
                    }
188
                    file_put_contents($fileName, self::serial($bitMask));
189
                }
190
            } else {
191
                $bitMask = $this->generateMaskNo($maskNo, $width, $s);
192
            }
193
194
            if ($maskGenOnly) {
195
                return;
196
            }
197
198
            $d = $s;
199
200
            for ($y = 0; $y < $width; $y++) {
201
                for ($x = 0; $x < $width; $x++) {
202
                    if ($bitMask[$y][$x] == 1) {
203
                        $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int) $bitMask[$y][$x]);
204
                    }
205
                    $b += (int) (ord($d[$y][$x]) & 1);
206
                }
207
            }
208
209
            return $b;
210
        }
211
212
        //----------------------------------------------------------------------
213
        public function makeMask($width, $frame, $maskNo, $level)
214
        {
215
            $masked = array_fill(0, $width, str_repeat("\0", $width));
216
            $this->makeMaskNo($maskNo, $width, $frame, $masked);
217
            $this->writeFormatInformation($width, $masked, $maskNo, $level);
218
219
            return $masked;
220
        }
221
222
        //----------------------------------------------------------------------
223
        public function calcN1N3($length)
224
        {
225
            $demerit = 0;
226
227
            for ($i = 0; $i < $length; $i++) {
228
                if ($this->runLength[$i] >= 5) {
229
                    $demerit += (N1 + ($this->runLength[$i] - 5));
230
                }
231
                if ($i & 1) {
232
                    if (($i >= 3) && ($i < ($length - 2)) && ($this->runLength[$i] % 3 == 0)) {
233
                        $fact = (int) ($this->runLength[$i] / 3);
234
                        if (($this->runLength[$i - 2] == $fact) &&
235
                           ($this->runLength[$i - 1] == $fact) &&
236
                           ($this->runLength[$i + 1] == $fact) &&
237
                           ($this->runLength[$i + 2] == $fact)) {
238
                            if (($this->runLength[$i - 3] < 0) || ($this->runLength[$i - 3] >= (4 * $fact))) {
239
                                $demerit += N3;
240
                            } elseif ((($i + 3) >= $length) || ($this->runLength[$i + 3] >= (4 * $fact))) {
241
                                $demerit += N3;
242
                            }
243
                        }
244
                    }
245
                }
246
            }
247
248
            return $demerit;
249
        }
250
251
        //----------------------------------------------------------------------
252
        public function evaluateSymbol($width, $frame)
253
        {
254
            $head = 0;
255
            $demerit = 0;
256
257
            for ($y = 0; $y < $width; $y++) {
258
                $head = 0;
259
                $this->runLength[0] = 1;
260
261
                $frameY = $frame[$y];
262
263
                if ($y > 0) {
264
                    $frameYM = $frame[$y - 1];
265
                }
266
267
                for ($x = 0; $x < $width; $x++) {
268
                    if (($x > 0) && ($y > 0)) {
269
                        $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...
270
                        $w22 = ord($frameY[$x]) | ord($frameY[$x - 1]) | ord($frameYM[$x]) | ord($frameYM[$x - 1]);
271
272
                        if (($b22 | ($w22 ^ 1)) & 1) {
273
                            $demerit += N2;
274
                        }
275
                    }
276
                    if (($x == 0) && (ord($frameY[$x]) & 1)) {
277
                        $this->runLength[0] = -1;
278
                        $head = 1;
279
                        $this->runLength[$head] = 1;
280
                    } elseif ($x > 0) {
281
                        if ((ord($frameY[$x]) ^ ord($frameY[$x - 1])) & 1) {
282
                            $head++;
283
                            $this->runLength[$head] = 1;
284
                        } else {
285
                            $this->runLength[$head]++;
286
                        }
287
                    }
288
                }
289
290
                $demerit += $this->calcN1N3($head + 1);
291
            }
292
293
            for ($x = 0; $x < $width; $x++) {
294
                $head = 0;
295
                $this->runLength[0] = 1;
296
297
                for ($y = 0; $y < $width; $y++) {
298
                    if ($y == 0 && (ord($frame[$y][$x]) & 1)) {
299
                        $this->runLength[0] = -1;
300
                        $head = 1;
301
                        $this->runLength[$head] = 1;
302
                    } elseif ($y > 0) {
303
                        if ((ord($frame[$y][$x]) ^ ord($frame[$y - 1][$x])) & 1) {
304
                            $head++;
305
                            $this->runLength[$head] = 1;
306
                        } else {
307
                            $this->runLength[$head]++;
308
                        }
309
                    }
310
                }
311
312
                $demerit += $this->calcN1N3($head + 1);
313
            }
314
315
            return $demerit;
316
        }
317
318
        //----------------------------------------------------------------------
319
        public function mask($width, $frame, $level)
320
        {
321
            $minDemerit = PHP_INT_MAX;
322
            $bestMaskNum = 0;
0 ignored issues
show
The assignment to $bestMaskNum is dead and can be removed.
Loading history...
323
            $bestMask = [];
0 ignored issues
show
The assignment to $bestMask is dead and can be removed.
Loading history...
324
325
            $checked_masks = [0, 1, 2, 3, 4, 5, 6, 7];
326
327
            if (QR_FIND_FROM_RANDOM !== false) {
0 ignored issues
show
The condition QR_FIND_FROM_RANDOM !== false is always false.
Loading history...
328
                $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9);
329
                for ($i = 0; $i < $howManuOut; $i++) {
330
                    $remPos = rand(0, count($checked_masks) - 1);
331
                    unset($checked_masks[$remPos]);
332
                    $checked_masks = array_values($checked_masks);
333
                }
334
            }
335
336
            $bestMask = $frame;
337
338
            foreach ($checked_masks as $i) {
339
                $mask = array_fill(0, $width, str_repeat("\0", $width));
340
341
                $demerit = 0;
0 ignored issues
show
The assignment to $demerit is dead and can be removed.
Loading history...
342
                $blacks = 0;
0 ignored issues
show
The assignment to $blacks is dead and can be removed.
Loading history...
343
                $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
344
                $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
345
                $blacks = (int) (100 * $blacks / ($width * $width));
346
                $demerit = (int) ((int) (abs($blacks - 50) / 5) * N4);
347
                $demerit += $this->evaluateSymbol($width, $mask);
348
349
                if ($demerit < $minDemerit) {
350
                    $minDemerit = $demerit;
351
                    $bestMask = $mask;
352
                    $bestMaskNum = $i;
353
                }
354
            }
355
356
            return $bestMask;
357
        }
358
359
        //----------------------------------------------------------------------
360
    }
361