国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
12KB

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