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