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.
 
 
 
 

1101 lines
32 KiB

  1. <?php
  2. namespace Kuxin\Helper;
  3. /**
  4. * Class Image
  5. *
  6. * @package Kuxin\Helper
  7. * @author Pakey <pakey@qq.com>
  8. */
  9. class Image
  10. {
  11. public $img;
  12. public $info;
  13. /* 水印相关常量定义 */
  14. //常量,标识左上角水印
  15. const IMAGE_WATER_NORTHWEST = 1;
  16. //常量,标识上居中水印
  17. const IMAGE_WATER_NORTH = 2;
  18. //常量,标识右上角水印
  19. const IMAGE_WATER_NORTHEAST = 3;
  20. //常量,标识左居中水印
  21. const IMAGE_WATER_WEST = 4;
  22. //常量,标识居中水印
  23. const IMAGE_WATER_CENTER = 5;
  24. //常量,标识右居中水印
  25. const IMAGE_WATER_EAST = 6;
  26. //常量,标识左下角水印
  27. const IMAGE_WATER_SOUTHWEST = 7;
  28. //常量,标识下居中水印
  29. const IMAGE_WATER_SOUTH = 8;
  30. //常量,标识右下角水印
  31. const IMAGE_WATER_SOUTHEAST = 9;
  32. public function __construct($var)
  33. {
  34. if (stripos($var, 'http') === 0) {
  35. $content = Http::get($var);
  36. } elseif (strlen($var) < 100 && file_exists($var)) {
  37. $content = file_get_contents($var);
  38. } else {
  39. $content = (string)$var;
  40. }
  41. $this->info['type'] = $this->gettype($content);
  42. $this->info['mime'] = 'image/' . $this->info['type'];
  43. if ('gif' == $this->info['type']) {
  44. $this->gif = new Image_GIF($content);
  45. $this->img = imagecreatefromstring($this->gif->image());
  46. } else {
  47. $this->img = imagecreatefromstring($content);
  48. }
  49. $this->info['width'] = imagesx($this->img);
  50. $this->info['height'] = imagesy($this->img);
  51. }
  52. /**
  53. * 返回图像宽度
  54. *
  55. * @return integer 图像宽度
  56. */
  57. public function width()
  58. {
  59. return $this->info['width'];
  60. }
  61. /**
  62. * 返回图像高度
  63. *
  64. * @return integer 图像高度
  65. */
  66. public function height()
  67. {
  68. return $this->info['height'];
  69. }
  70. /**
  71. * 返回图像类型
  72. *
  73. * @return string 图像类型
  74. */
  75. public function type()
  76. {
  77. return $this->info['type'];
  78. }
  79. /**
  80. * 返回图像MIME类型
  81. *
  82. * @return string 图像MIME类型
  83. */
  84. public function mime()
  85. {
  86. return $this->info['mime'];
  87. }
  88. /**
  89. * 返回图像尺寸数组 0 - 图像宽度,1 - 图像高度
  90. *
  91. * @return array 图像尺寸
  92. */
  93. public function size()
  94. {
  95. return [$this->info['width'], $this->info['height']];
  96. }
  97. /**
  98. * 按宽度缩放
  99. * @param int $width
  100. * @param bool $force 是否强制 如果小的不扩大
  101. * @return resource
  102. */
  103. public function resizeByWidth(int $width, $force = false)
  104. {
  105. if ($force === false && $this->info['width'] <= $width) {
  106. return $this->img;
  107. }
  108. $height = ceil($width * $this->info['height'] / $this->info['width']);
  109. do {
  110. //创建新图像
  111. $img = imagecreatetruecolor($width, $height);
  112. // 调整默认颜色
  113. $color = imagecolorallocate($img, 255, 255, 255);
  114. imagefill($img, 0, 0, $color);
  115. //裁剪
  116. imagecopyresampled($img, $this->img, 0, 0, 0, 0, $width, $height, $this->info['width'], $this->info['height']);
  117. //销毁原图
  118. imagedestroy($this->img);
  119. //设置新图像
  120. $this->img = $img;
  121. } while (!empty($this->gif) && $this->gifNext());
  122. $this->info['width'] = $width;
  123. $this->info['height'] = $height;
  124. return $this->img;
  125. }
  126. public function thumb($width, $height)
  127. {
  128. //判断尺寸
  129. if ($this->info['width'] < $width && $this->info['height'] < $height) {
  130. //创建图像资源 需要填充的
  131. $img = imagecreatetruecolor($width, $height);
  132. imagefill($img, 0, 0, imagecolorallocate($img, 255, 255, 255));
  133. //全都小于指定缩略图尺寸
  134. if ($width / $this->info['width'] > $height / $this->info['height']) {
  135. //按高放大
  136. $this->resize(floor($this->info['width'] * $height / $this->info['height']));
  137. $x = ceil(($width - $this->info['width']) / 2);
  138. imagecopyresampled($img, $this->img, $x, 0, 0, 0, $this->info['width'], $height, $this->info['width'], $this->info['height']);
  139. } else {
  140. //按宽放大
  141. $this->resize($width);
  142. $y = ceil(($height - $this->info['height']) / 2);
  143. imagecopyresampled($img, $this->img, 0, $y, 0, 0, $width, $this->info['height'], $this->info['width'], $this->info['height']);
  144. }
  145. //销毁原图
  146. imagedestroy($this->img);
  147. //设置新图像
  148. $this->img = $img;
  149. $this->info['width'] = $width;
  150. $this->info['height'] = $height;
  151. } else {
  152. if ($width / $this->info['width'] > $height / $this->info['height']) {
  153. //按宽缩小
  154. $this->resize($width);
  155. $y = ($this->info['height'] - $height) / 2;
  156. $this->crop($width, $height, 0, $y);
  157. } else {
  158. //按高缩小
  159. $this->resize(floor($this->info['width'] * $height / $this->info['height']));
  160. $x = ($this->info['width'] - $width) / 2;
  161. $this->crop($width, $height, $x, 0);
  162. }
  163. }
  164. }
  165. public function crop($w, $h, $x = 0, $y = 0)
  166. {
  167. //设置保存尺寸
  168. do {
  169. //创建新图像
  170. $img = imagecreatetruecolor($w, $h);
  171. // 调整默认颜色
  172. $color = imagecolorallocate($img, 255, 255, 255);
  173. imagefill($img, 0, 0, $color);
  174. //裁剪
  175. imagecopyresampled($img, $this->img, 0, 0, $x, $y, $w, $h, $w, $h);
  176. //销毁原图
  177. imagedestroy($this->img);
  178. //设置新图像
  179. $this->img = $img;
  180. } while (!empty($this->gif) && $this->gifNext());
  181. $this->info['width'] = $w;
  182. $this->info['height'] = $h;
  183. return $this->img;
  184. }
  185. public function water($source, $posotion = Image::IMAGE_WATER_SOUTHEAST, $alpha = 60)
  186. {
  187. //资源检测
  188. if (!is_file($source))
  189. return false;
  190. //获取水印图像信息
  191. $info = getimagesize($source);
  192. if ($info === false || $this->info['width'] < $info['0'] || $this->info['height'] < $info['1'])
  193. return false;
  194. //创建水印图像资源
  195. $fun = 'imagecreatefrom' . image_type_to_extension($info[2], false);
  196. $water = $fun($source);
  197. //设定水印图像的混色模式
  198. imagealphablending($water, true);
  199. switch ($posotion) {
  200. /* 右下角水印 */
  201. case Image::IMAGE_WATER_SOUTHEAST:
  202. $x = $this->info['width'] - $info[0];
  203. $y = $this->info['height'] - $info[1];
  204. break;
  205. /* 左下角水印 */
  206. case Image::IMAGE_WATER_SOUTHWEST:
  207. $x = 0;
  208. $y = $this->info['height'] - $info[1];
  209. break;
  210. /* 左上角水印 */
  211. case Image::IMAGE_WATER_NORTHWEST:
  212. $x = $y = 0;
  213. break;
  214. /* 右上角水印 */
  215. case Image::IMAGE_WATER_NORTHEAST:
  216. $x = $this->info['width'] - $info[0];
  217. $y = 0;
  218. break;
  219. /* 居中水印 */
  220. case Image::IMAGE_WATER_CENTER:
  221. $x = ($this->info['width'] - $info[0]) / 2;
  222. $y = ($this->info['height'] - $info[1]) / 2;
  223. break;
  224. /* 下居中水印 */
  225. case Image::IMAGE_WATER_SOUTH:
  226. $x = ($this->info['width'] - $info[0]) / 2;
  227. $y = $this->info['height'] - $info[1];
  228. break;
  229. /* 右居中水印 */
  230. case Image::IMAGE_WATER_EAST:
  231. $x = $this->info['width'] - $info[0];
  232. $y = ($this->info['height'] - $info[1]) / 2;
  233. break;
  234. /* 上居中水印 */
  235. case Image::IMAGE_WATER_NORTH:
  236. $x = ($this->info['width'] - $info[0]) / 2;
  237. $y = 0;
  238. break;
  239. /* 左居中水印 */
  240. case Image::IMAGE_WATER_WEST:
  241. $x = 0;
  242. $y = ($this->info['height'] - $info[1]) / 2;
  243. break;
  244. default:
  245. /* 自定义水印坐标 */
  246. if (is_array($posotion)) {
  247. list($x, $y) = $posotion;
  248. } else {
  249. return false;
  250. }
  251. }
  252. do {
  253. //添加水印
  254. $src = imagecreatetruecolor($info[0], $info[1]);
  255. // 调整默认颜色
  256. $color = imagecolorallocate($src, 255, 255, 255);
  257. imagefill($src, 0, 0, $color);
  258. imagecopy($src, $this->img, 0, 0, $x, $y, $info[0], $info[1]);
  259. imagecopy($src, $water, 0, 0, 0, 0, $info[0], $info[1]);
  260. imagecopymerge($this->img, $src, $x, $y, 0, 0, $info[0], $info[1], $alpha);
  261. //销毁零时图片资源
  262. imagedestroy($src);
  263. } while (!empty($this->gif) && $this->gifNext());
  264. //销毁水印资源
  265. imagedestroy($water);
  266. return true;
  267. }
  268. /**
  269. * 图像添加文字
  270. *
  271. * @param string $text 添加的文字
  272. * @param string $font 字体路径
  273. * @param integer $size 字号
  274. * @param string $color 文字颜色
  275. * @param integer $locate 文字写入位置
  276. * @param string $margin 文字边距
  277. * @param integer $offset 文字相对当前位置的偏移量
  278. * @param integer $angle 文字倾斜角度
  279. * @return mixed
  280. */
  281. public function text($text, $font, $size = 20, $color = '#ff0000', $locate = Image::IMAGE_WATER_SOUTHEAST, $margin = '', $offset = 0, $angle = 0)
  282. {
  283. //资源检测
  284. //if (!is_file($font)) return false;
  285. if ($margin === '')
  286. $margin = ceil($size / 2);
  287. //获取文字信息
  288. $info = imagettfbbox($size, $angle, $font, $text);
  289. $minx = min($info[0], $info[2], $info[4], $info[6]);
  290. $maxx = max($info[0], $info[2], $info[4], $info[6]);
  291. $miny = min($info[1], $info[3], $info[5], $info[7]);
  292. $maxy = max($info[1], $info[3], $info[5], $info[7]);
  293. /* 计算文字初始坐标和尺寸 */
  294. $x = $minx;
  295. $y = abs($miny);
  296. $w = $maxx - $minx;
  297. $h = $maxy - $miny;
  298. /* 设定文字位置 */
  299. switch ($locate) {
  300. /* 右下角文字 */
  301. case Image::IMAGE_WATER_SOUTHEAST:
  302. $x += $this->info['width'] - $w - $margin;
  303. $y += $this->info['height'] - $h - $margin;
  304. break;
  305. /* 左下角文字 */
  306. case Image::IMAGE_WATER_SOUTHWEST:
  307. $x += $margin;
  308. $y += $this->info['height'] - $h - $margin;
  309. break;
  310. /* 左上角文字 */
  311. case Image::IMAGE_WATER_NORTHWEST:
  312. // 起始坐标即为左上角坐标,无需调整
  313. $x += $margin;
  314. $y += $margin;
  315. break;
  316. /* 右上角文字 */
  317. case Image::IMAGE_WATER_NORTHEAST:
  318. $x += $this->info['width'] - $w - $margin;
  319. $y += $margin;
  320. break;
  321. /* 居中文字 */
  322. case Image::IMAGE_WATER_CENTER:
  323. $x += ($this->info['width'] - $w) / 2;
  324. $y += ($this->info['height'] - $h) / 2;
  325. break;
  326. /* 下居中文字 */
  327. case Image::IMAGE_WATER_SOUTH:
  328. $x += ($this->info['width'] - $w) / 2;
  329. $y += $this->info['height'] - $h - $margin;
  330. break;
  331. /* 右居中文字 */
  332. case Image::IMAGE_WATER_EAST:
  333. $x += $this->info['width'] - $w - $margin;
  334. $y += ($this->info['height'] - $h) / 2;
  335. break;
  336. /* 上居中文字 */
  337. case Image::IMAGE_WATER_NORTH:
  338. $x += ($this->info['width'] - $w) / 2;
  339. $y += $margin;
  340. break;
  341. /* 左居中文字 */
  342. case Image::IMAGE_WATER_WEST:
  343. $x += $margin;
  344. $y += ($this->info['height'] - $h) / 2;
  345. break;
  346. default:
  347. /* 自定义文字坐标 */
  348. if (is_array($locate)) {
  349. list($posx, $posy) = $locate;
  350. $x += $posx;
  351. $y += $posy;
  352. } else {
  353. $this->crop($this->info['width'], $this->info['height'] + ceil($size * 1.4));
  354. $x += $this->info['width'] - $w;
  355. $y += $this->info['height'] - $h;
  356. }
  357. }
  358. /* 设置偏移量 */
  359. if (is_array($offset)) {
  360. $offset = array_map('intval', $offset);
  361. list($ox, $oy) = $offset;
  362. } else {
  363. $offset = intval($offset);
  364. $ox = $oy = $offset;
  365. }
  366. /* 设置颜色 */
  367. if (is_string($color) && 0 === strpos($color, '#')) {
  368. $color = str_split(substr($color, 1), 2);
  369. $color = array_map('hexdec', $color);
  370. if (empty($color[3]) || $color[3] > 127) {
  371. $color[3] = 0;
  372. }
  373. } elseif (!is_array($color)) {
  374. return false;
  375. }
  376. do {
  377. /* 写入文字 */
  378. $col = imagecolorallocatealpha($this->img, $color[0], $color[1], $color[2], $color[3]);
  379. imagettftext($this->img, $size, $angle, $x + $ox, $y + $oy, $col, $font, $text);
  380. } while (!empty($this->gif) && $this->gifNext());
  381. return true;
  382. }
  383. /**
  384. * 保存图像
  385. *
  386. * @param string $type 图像类型
  387. * @param boolean $interlace 是否对JPEG类型图像设置隔行扫描
  388. * @return string
  389. */
  390. public function save($type = null, $interlace = true)
  391. {
  392. if (empty($this->img))
  393. return '';
  394. //自动获取图像类型
  395. if (is_null($type)) {
  396. $type = $this->info['type'];
  397. } else {
  398. $type = strtolower($type);
  399. }
  400. //保存图像
  401. if ('jpeg' == $type || 'jpg' == $type) {
  402. //JPEG图像设置隔行扫描
  403. imageinterlace($this->img, $interlace);
  404. ob_start();
  405. imagejpeg($this->img);
  406. $content = ob_get_contents();
  407. ob_end_clean();
  408. return $content;
  409. } elseif ('gif' == $type && !empty($this->gif)) {
  410. return $this->gif->save();
  411. } else {
  412. $fun = 'image' . $type;
  413. ob_start();
  414. $fun($this->img);
  415. $content = ob_get_contents();
  416. ob_end_clean();
  417. return $content;
  418. }
  419. }
  420. protected function gettype($content)
  421. {
  422. switch (substr($content, 0, 2)) {
  423. case chr('137') . 'P':
  424. return 'png';
  425. break;
  426. case 'GI':
  427. return 'gif';
  428. break;
  429. case chr('255') . chr('216'):
  430. return 'jpg';
  431. break;
  432. default:
  433. return 'jpeg';
  434. exit('error image type [' . ord(substr($content, 0, 1)) . ' ' . ord(substr($content, 1, 1)) . ']');
  435. }
  436. }
  437. /* 切换到GIF的下一帧并保存当前帧,内部使用 */
  438. private function gifNext()
  439. {
  440. ob_start();
  441. ob_implicit_flush(0);
  442. imagegif($this->img);
  443. $img = ob_get_clean();
  444. $this->gif->image($img);
  445. $next = $this->gif->nextImage();
  446. if ($next) {
  447. $this->img = imagecreatefromstring($next);
  448. return $next;
  449. } else {
  450. $this->img = imagecreatefromstring($this->gif->image());
  451. return false;
  452. }
  453. }
  454. /**
  455. * 析构方法,用于销毁图像资源
  456. */
  457. public function __destruct()
  458. {
  459. empty($this->img) || imagedestroy($this->img);
  460. return true;
  461. }
  462. }
  463. class Image_GIF
  464. {
  465. /**
  466. * GIF帧列表
  467. *
  468. * @var array
  469. */
  470. private $frames = [];
  471. /**
  472. * 每帧等待时间列表
  473. *
  474. * @var array
  475. */
  476. private $delays = [];
  477. /**
  478. * 构造方法,用于解码GIF图片
  479. *
  480. * @param string $src GIF图片数据
  481. */
  482. public function __construct($src = null)
  483. {
  484. if (!is_null($src)) {
  485. /* 解码GIF图片 */
  486. try {
  487. $de = new GIFDecoder($src);
  488. $this->frames = $de->GIFGetFrames();
  489. $this->delays = $de->GIFGetDelays();
  490. } catch (\Exception $e) {
  491. trigger_error("解码GIF图片出错", E_USER_ERROR);
  492. }
  493. }
  494. }
  495. /**
  496. * 设置或获取当前帧的数据
  497. *
  498. * @param string $stream 二进制数据流
  499. * @return mixed 获取到的数据
  500. */
  501. public function image($stream = null)
  502. {
  503. if (is_null($stream)) {
  504. $current = current($this->frames);
  505. return false === $current ? reset($this->frames) : $current;
  506. } else {
  507. $this->frames[key($this->frames)] = $stream;
  508. return true;
  509. }
  510. }
  511. /**
  512. * 将当前帧移动到下一帧
  513. *
  514. * @return string 当前帧数据
  515. */
  516. public function nextImage()
  517. {
  518. return next($this->frames);
  519. }
  520. /**
  521. * 编码并保存当前GIF图片
  522. *
  523. * @return string
  524. */
  525. public function save()
  526. {
  527. $gif = new GIFEncoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin');
  528. return $gif->GetAnimation();
  529. }
  530. }
  531. /*
  532. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  533. ::
  534. :: GIFEncoder Version 2.0 by László Zsidi, http://gifs.hu
  535. ::
  536. :: This class is a rewritten 'GifMerge.class.php' version.
  537. ::
  538. :: Modification:
  539. :: - Simplified and easy code,
  540. :: - Ultra fast encoding,
  541. :: - Built-in errors,
  542. :: - Stable working
  543. ::
  544. ::
  545. :: Updated at 2007. 02. 13. '00.05.AM'
  546. ::
  547. ::
  548. ::
  549. :: Try on-line GIFBuilder Form demo based on GIFEncoder.
  550. ::
  551. :: http://gifs.hu/phpclasses/demos/GifBuilder/
  552. ::
  553. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  554. */
  555. Class GIFEncoder
  556. {
  557. private $GIF = "GIF89a"; /* GIF header 6 bytes */
  558. private $VER = "GIFEncoder V2.05"; /* Encoder version */
  559. private $BUF = [];
  560. private $LOP = 0;
  561. private $DIS = 2;
  562. private $COL = -1;
  563. private $IMG = -1;
  564. private $ERR = [
  565. 'ERR00' => "Does not supported function for only one image!",
  566. 'ERR01' => "Source is not a GIF image!",
  567. 'ERR02' => "Unintelligible flag ",
  568. 'ERR03' => "Does not make animation from animated GIF source",
  569. ];
  570. /*
  571. :::::::::::::::::::::::::::::::::::::::::::::::::::
  572. ::
  573. :: GIFEncoder...
  574. ::
  575. */
  576. public function __construct($GIF_src, $GIF_dly, $GIF_lop, $GIF_dis, $GIF_red, $GIF_grn, $GIF_blu, $GIF_mod)
  577. {
  578. if (!is_array($GIF_src) && !is_array($GIF_dly)) {
  579. printf("%s: %s", $this->VER, $this->ERR ['ERR00']);
  580. exit (0);
  581. }
  582. $this->LOP = ($GIF_lop > -1) ? $GIF_lop : 0;
  583. $this->DIS = ($GIF_dis > -1) ? (($GIF_dis < 3) ? $GIF_dis : 3) : 2;
  584. $this->COL = ($GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1) ?
  585. ($GIF_red | ($GIF_grn << 8) | ($GIF_blu << 16)) : -1;
  586. for ($i = 0; $i < count($GIF_src); $i++) {
  587. if (strtolower($GIF_mod) == "url") {
  588. $this->BUF [] = fread(fopen($GIF_src [$i], "rb"), filesize($GIF_src [$i]));
  589. } elseif (strtolower($GIF_mod) == "bin") {
  590. $this->BUF [] = $GIF_src [$i];
  591. } else {
  592. printf("%s: %s ( %s )!", $this->VER, $this->ERR ['ERR02'], $GIF_mod);
  593. exit (0);
  594. }
  595. if (substr($this->BUF [$i], 0, 6) != "GIF87a" && substr($this->BUF [$i], 0, 6) != "GIF89a") {
  596. printf("%s: %d %s", $this->VER, $i, $this->ERR ['ERR01']);
  597. exit (0);
  598. }
  599. for ($j = (13 + 3 * (2 << (ord($this->BUF [$i]{10}) & 0x07))), $k = true; $k; $j++) {
  600. switch ($this->BUF [$i]{$j}) {
  601. case "!":
  602. if ((substr($this->BUF [$i], ($j + 3), 8)) == "NETSCAPE") {
  603. printf("%s: %s ( %s source )!", $this->VER, $this->ERR ['ERR03'], ($i + 1));
  604. exit (0);
  605. }
  606. break;
  607. case ";":
  608. $k = false;
  609. break;
  610. }
  611. }
  612. }
  613. $this->GIFAddHeader();
  614. for ($i = 0; $i < count($this->BUF); $i++) {
  615. $this->GIFAddFrames($i, $GIF_dly [$i]);
  616. }
  617. $this->GIFAddFooter();
  618. return true;
  619. }
  620. /*
  621. :::::::::::::::::::::::::::::::::::::::::::::::::::
  622. ::
  623. :: GIFAddHeader...
  624. ::
  625. */
  626. private function GIFAddHeader()
  627. {
  628. if (ord($this->BUF [0]{10}) & 0x80) {
  629. $cmap = 3 * (2 << (ord($this->BUF [0]{10}) & 0x07));
  630. $this->GIF .= substr($this->BUF [0], 6, 7);
  631. $this->GIF .= substr($this->BUF [0], 13, $cmap);
  632. $this->GIF .= "!\377\13NETSCAPE2.0\3\1" . $this->GIFWord($this->LOP) . "\0";
  633. }
  634. }
  635. /*
  636. :::::::::::::::::::::::::::::::::::::::::::::::::::
  637. ::
  638. :: GIFAddFrames...
  639. ::
  640. */
  641. private function GIFAddFrames($i, $d)
  642. {
  643. $Locals_str = 13 + 3 * (2 << (ord($this->BUF [$i]{10}) & 0x07));
  644. $Locals_end = strlen($this->BUF [$i]) - $Locals_str - 1;
  645. $Locals_tmp = substr($this->BUF [$i], $Locals_str, $Locals_end);
  646. $Global_len = 2 << (ord($this->BUF [0]{10}) & 0x07);
  647. $Locals_len = 2 << (ord($this->BUF [$i]{10}) & 0x07);
  648. $Global_rgb = substr($this->BUF [0], 13,
  649. 3 * (2 << (ord($this->BUF [0]{10}) & 0x07)));
  650. $Locals_rgb = substr($this->BUF [$i], 13,
  651. 3 * (2 << (ord($this->BUF [$i]{10}) & 0x07)));
  652. $Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 0) .
  653. chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . "\x0\x0";
  654. if ($this->COL > -1 && ord($this->BUF [$i]{10}) & 0x80) {
  655. for ($j = 0; $j < (2 << (ord($this->BUF [$i]{10}) & 0x07)); $j++) {
  656. if (
  657. ord($Locals_rgb{3 * $j + 0}) == (($this->COL >> 16) & 0xFF) &&
  658. ord($Locals_rgb{3 * $j + 1}) == (($this->COL >> 8) & 0xFF) &&
  659. ord($Locals_rgb{3 * $j + 2}) == (($this->COL >> 0) & 0xFF)
  660. ) {
  661. $Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 1) .
  662. chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . chr($j) . "\x0";
  663. break;
  664. }
  665. }
  666. }
  667. switch ($Locals_tmp{0}) {
  668. case "!":
  669. $Locals_img = substr($Locals_tmp, 8, 10);
  670. $Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18);
  671. break;
  672. case ",":
  673. $Locals_img = substr($Locals_tmp, 0, 10);
  674. $Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10);
  675. break;
  676. default:
  677. $Locals_img = '';
  678. }
  679. if (ord($this->BUF [$i]{10}) & 0x80 && $this->IMG > -1) {
  680. if ($Global_len == $Locals_len) {
  681. if ($this->GIFBlockCompare($Global_rgb, $Locals_rgb, $Global_len)) {
  682. $this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
  683. } else {
  684. $byte = ord($Locals_img{9});
  685. $byte |= 0x80;
  686. $byte &= 0xF8;
  687. $byte |= (ord($this->BUF [0]{10}) & 0x07);
  688. $Locals_img{9} = chr($byte);
  689. $this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
  690. }
  691. } else {
  692. $byte = ord($Locals_img{9});
  693. $byte |= 0x80;
  694. $byte &= 0xF8;
  695. $byte |= (ord($this->BUF [$i]{10}) & 0x07);
  696. $Locals_img{9} = chr($byte);
  697. $this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
  698. }
  699. } else {
  700. $this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
  701. }
  702. $this->IMG = 1;
  703. }
  704. /*
  705. :::::::::::::::::::::::::::::::::::::::::::::::::::
  706. ::
  707. :: GIFAddFooter...
  708. ::
  709. */
  710. private function GIFAddFooter()
  711. {
  712. $this->GIF .= ";";
  713. }
  714. /*
  715. :::::::::::::::::::::::::::::::::::::::::::::::::::
  716. ::
  717. :: GIFBlockCompare...
  718. ::
  719. */
  720. private function GIFBlockCompare($GlobalBlock, $LocalBlock, $Len)
  721. {
  722. for ($i = 0; $i < $Len; $i++) {
  723. if (
  724. $GlobalBlock{3 * $i + 0} != $LocalBlock{3 * $i + 0} ||
  725. $GlobalBlock{3 * $i + 1} != $LocalBlock{3 * $i + 1} ||
  726. $GlobalBlock{3 * $i + 2} != $LocalBlock{3 * $i + 2}
  727. ) {
  728. return (0);
  729. }
  730. }
  731. return (1);
  732. }
  733. /*
  734. :::::::::::::::::::::::::::::::::::::::::::::::::::
  735. ::
  736. :: GIFWord...
  737. ::
  738. */
  739. private function GIFWord($int)
  740. {
  741. return (chr($int & 0xFF) . chr(($int >> 8) & 0xFF));
  742. }
  743. /*
  744. :::::::::::::::::::::::::::::::::::::::::::::::::::
  745. ::
  746. :: GetAnimation...
  747. ::
  748. */
  749. public function GetAnimation()
  750. {
  751. return ($this->GIF);
  752. }
  753. }
  754. /*
  755. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  756. ::
  757. :: GIFDecoder Version 2.0 by László Zsidi, http://gifs.hu
  758. ::
  759. :: Created at 2007. 02. 01. '07.47.AM'
  760. ::
  761. ::
  762. ::
  763. ::
  764. :: Try on-line GIFBuilder Form demo based on GIFDecoder.
  765. ::
  766. :: http://gifs.hu/phpclasses/demos/GifBuilder/
  767. ::
  768. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  769. */
  770. Class GIFDecoder
  771. {
  772. private $GIF_buffer = [];
  773. private $GIF_arrays = [];
  774. private $GIF_delays = [];
  775. private $GIF_stream = "";
  776. private $GIF_string = "";
  777. private $GIF_bfseek = 0;
  778. private $GIF_screen = [];
  779. private $GIF_global = [];
  780. private $GIF_sorted;
  781. private $GIF_colorS;
  782. private $GIF_colorC;
  783. private $GIF_colorF;
  784. /*
  785. :::::::::::::::::::::::::::::::::::::::::::::::::::
  786. ::
  787. :: GIFDecoder ( $GIF_pointer )
  788. ::
  789. */
  790. public function __construct($GIF_pointer)
  791. {
  792. $this->GIF_stream = $GIF_pointer;
  793. $this->GIFGetByte(6); // GIF89a
  794. $this->GIFGetByte(7); // Logical Screen Descriptor
  795. $this->GIF_screen = $this->GIF_buffer;
  796. $this->GIF_colorF = $this->GIF_buffer [4] & 0x80 ? 1 : 0;
  797. $this->GIF_sorted = $this->GIF_buffer [4] & 0x08 ? 1 : 0;
  798. $this->GIF_colorC = $this->GIF_buffer [4] & 0x07;
  799. $this->GIF_colorS = 2 << $this->GIF_colorC;
  800. if ($this->GIF_colorF == 1) {
  801. $this->GIFGetByte(3 * $this->GIF_colorS);
  802. $this->GIF_global = $this->GIF_buffer;
  803. }
  804. /*
  805. *
  806. * 05.06.2007.
  807. * Made a little modification
  808. *
  809. *
  810. - for ( $cycle = 1; $cycle; ) {
  811. + if ( GIFDecoder::GIFGetByte ( 1 ) ) {
  812. - switch ( $this->GIF_buffer [ 0 ] ) {
  813. - case 0x21:
  814. - GIFDecoder::GIFReadExtensions ( );
  815. - break;
  816. - case 0x2C:
  817. - GIFDecoder::GIFReadDescriptor ( );
  818. - break;
  819. - case 0x3B:
  820. - $cycle = 0;
  821. - break;
  822. - }
  823. - }
  824. + else {
  825. + $cycle = 0;
  826. + }
  827. - }
  828. */
  829. for ($cycle = 1; $cycle;) {
  830. if ($this->GIFGetByte(1)) {
  831. switch ($this->GIF_buffer [0]) {
  832. case 0x21:
  833. $this->GIFReadExtensions();
  834. break;
  835. case 0x2C:
  836. $this->GIFReadDescriptor();
  837. break;
  838. case 0x3B:
  839. $cycle = 0;
  840. break;
  841. }
  842. } else {
  843. $cycle = 0;
  844. }
  845. }
  846. }
  847. /*
  848. :::::::::::::::::::::::::::::::::::::::::::::::::::
  849. ::
  850. :: GIFReadExtension ( )
  851. ::
  852. */
  853. private function GIFReadExtensions()
  854. {
  855. $this->GIFGetByte(1);
  856. for (; ;) {
  857. $this->GIFGetByte(1);
  858. if (($u = $this->GIF_buffer [0]) == 0x00) {
  859. break;
  860. }
  861. $this->GIFGetByte($u);
  862. /*
  863. * 07.05.2007.
  864. * Implemented a new line for a new function
  865. * to determine the originaly delays between
  866. * frames.
  867. *
  868. */
  869. if ($u == 4) {
  870. $this->GIF_delays [] = ($this->GIF_buffer [1] | $this->GIF_buffer [2] << 8);
  871. }
  872. }
  873. }
  874. /*
  875. :::::::::::::::::::::::::::::::::::::::::::::::::::
  876. ::
  877. :: GIFReadExtension ( )
  878. ::
  879. */
  880. private function GIFReadDescriptor()
  881. {
  882. $this->GIFGetByte(9);
  883. $GIF_screen = $this->GIF_buffer;
  884. $GIF_colorF = $this->GIF_buffer [8] & 0x80 ? 1 : 0;
  885. if ($GIF_colorF) {
  886. $GIF_code = $this->GIF_buffer [8] & 0x07;
  887. $GIF_sort = $this->GIF_buffer [8] & 0x20 ? 1 : 0;
  888. } else {
  889. $GIF_code = $this->GIF_colorC;
  890. $GIF_sort = $this->GIF_sorted;
  891. }
  892. $GIF_size = 2 << $GIF_code;
  893. $this->GIF_screen [4] &= 0x70;
  894. $this->GIF_screen [4] |= 0x80;
  895. $this->GIF_screen [4] |= $GIF_code;
  896. if ($GIF_sort) {
  897. $this->GIF_screen [4] |= 0x08;
  898. }
  899. $this->GIF_string = "GIF87a";
  900. $this->GIFPutByte($this->GIF_screen);
  901. if ($GIF_colorF == 1) {
  902. $this->GIFGetByte(3 * $GIF_size);
  903. $this->GIFPutByte($this->GIF_buffer);
  904. } else {
  905. $this->GIFPutByte($this->GIF_global);
  906. }
  907. $this->GIF_string .= chr(0x2C);
  908. $GIF_screen [8] &= 0x40;
  909. $this->GIFPutByte($GIF_screen);
  910. $this->GIFGetByte(1);
  911. $this->GIFPutByte($this->GIF_buffer);
  912. for (; ;) {
  913. $this->GIFGetByte(1);
  914. $this->GIFPutByte($this->GIF_buffer);
  915. if (($u = $this->GIF_buffer [0]) == 0x00) {
  916. break;
  917. }
  918. $this->GIFGetByte($u);
  919. $this->GIFPutByte($this->GIF_buffer);
  920. }
  921. $this->GIF_string .= chr(0x3B);
  922. /*
  923. Add frames into $GIF_stream array...
  924. */
  925. $this->GIF_arrays [] = $this->GIF_string;
  926. }
  927. /*
  928. :::::::::::::::::::::::::::::::::::::::::::::::::::
  929. ::
  930. :: GIFGetByte ( $len )
  931. ::
  932. */
  933. /*
  934. *
  935. * 05.06.2007.
  936. * Made a little modification
  937. *
  938. *
  939. - function GIFGetByte ( $len ) {
  940. - $this->GIF_buffer = Array ( );
  941. -
  942. - for ( $i = 0; $i < $len; $i++ ) {
  943. + if ( $this->GIF_bfseek > strlen ( $this->GIF_stream ) ) {
  944. + return 0;
  945. + }
  946. - $this->GIF_buffer [ ] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
  947. - }
  948. + return 1;
  949. - }
  950. */
  951. private function GIFGetByte($len)
  952. {
  953. $this->GIF_buffer = [];
  954. for ($i = 0; $i < $len; $i++) {
  955. if ($this->GIF_bfseek > strlen($this->GIF_stream)) {
  956. return 0;
  957. }
  958. $this->GIF_buffer [] = ord($this->GIF_stream{$this->GIF_bfseek++});
  959. }
  960. return 1;
  961. }
  962. /*
  963. :::::::::::::::::::::::::::::::::::::::::::::::::::
  964. ::
  965. :: GIFPutByte ( $bytes )
  966. ::
  967. */
  968. private function GIFPutByte($bytes)
  969. {
  970. for ($i = 0; $i < count($bytes); $i++) {
  971. $this->GIF_string .= chr($bytes [$i]);
  972. }
  973. }
  974. /*
  975. :::::::::::::::::::::::::::::::::::::::::::::::::::
  976. ::
  977. :: PUBLIC FUNCTIONS
  978. ::
  979. ::
  980. :: GIFGetFrames ( )
  981. ::
  982. */
  983. public function GIFGetFrames()
  984. {
  985. return ($this->GIF_arrays);
  986. }
  987. /*
  988. :::::::::::::::::::::::::::::::::::::::::::::::::::
  989. ::
  990. :: GIFGetDelays ( )
  991. ::
  992. */
  993. public function GIFGetDelays()
  994. {
  995. return ($this->GIF_delays);
  996. }
  997. }