GIF.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | TOPThink [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2010 http://topthink.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: 小梦科技资源 <zuojiazi@vip.qq.com> <https://www.nanodreamtech.com>
  10. // +----------------------------------------------------------------------
  11. // | GIF.class.php 2013-03-09
  12. // +----------------------------------------------------------------------
  13. namespace Think\Image\Driver;
  14. class GIF{
  15. /**
  16. * GIF帧列表
  17. * @var array
  18. */
  19. private $frames = array();
  20. /**
  21. * 每帧等待时间列表
  22. * @var array
  23. */
  24. private $delays = array();
  25. /**
  26. * 构造方法,用于解码GIF图片
  27. * @param string $src GIF图片数据
  28. * @param string $mod 图片数据类型
  29. */
  30. public function __construct($src = null, $mod = 'url') {
  31. if(!is_null($src)){
  32. if('url' == $mod && is_file($src)){
  33. $src = file_get_contents($src);
  34. }
  35. /* 解码GIF图片 */
  36. try{
  37. $de = new GIFDecoder($src);
  38. $this->frames = $de->GIFGetFrames();
  39. $this->delays = $de->GIFGetDelays();
  40. } catch(\Exception $e){
  41. E("解码GIF图片出错");
  42. }
  43. }
  44. }
  45. /**
  46. * 设置或获取当前帧的数据
  47. * @param string $stream 二进制数据流
  48. * @return boolean 获取到的数据
  49. */
  50. public function image($stream = null){
  51. if(is_null($stream)){
  52. $current = current($this->frames);
  53. return false === $current ? reset($this->frames) : $current;
  54. } else {
  55. $this->frames[key($this->frames)] = $stream;
  56. }
  57. }
  58. /**
  59. * 将当前帧移动到下一帧
  60. * @return string 当前帧数据
  61. */
  62. public function nextImage(){
  63. return next($this->frames);
  64. }
  65. /**
  66. * 编码并保存当前GIF图片
  67. * @param string $gifname 图片名称
  68. */
  69. public function save($gifname){
  70. $gif = new GIFEncoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin');
  71. file_put_contents($gifname, $gif->GetAnimation());
  72. }
  73. }
  74. /*
  75. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  76. ::
  77. :: GIFEncoder Version 2.0 by László Zsidi, http://gifs.hu
  78. ::
  79. :: This class is a rewritten 'GifMerge.class.php' version.
  80. ::
  81. :: Modification:
  82. :: - Simplified and easy code,
  83. :: - Ultra fast encoding,
  84. :: - Built-in errors,
  85. :: - Stable working
  86. ::
  87. ::
  88. :: Updated at 2007. 02. 13. '00.05.AM'
  89. ::
  90. ::
  91. ::
  92. :: Try on-line GIFBuilder Form demo based on GIFEncoder.
  93. ::
  94. :: http://gifs.hu/phpclasses/demos/GifBuilder/
  95. ::
  96. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  97. */
  98. Class GIFEncoder {
  99. private $GIF = "GIF89a"; /* GIF header 6 bytes */
  100. private $VER = "GIFEncoder V2.05"; /* Encoder version */
  101. private $BUF = Array ( );
  102. private $LOP = 0;
  103. private $DIS = 2;
  104. private $COL = -1;
  105. private $IMG = -1;
  106. private $ERR = Array (
  107. 'ERR00' => "Does not supported function for only one image!",
  108. 'ERR01' => "Source is not a GIF image!",
  109. 'ERR02' => "Unintelligible flag ",
  110. 'ERR03' => "Does not make animation from animated GIF source",
  111. );
  112. /*
  113. :::::::::::::::::::::::::::::::::::::::::::::::::::
  114. ::
  115. :: GIFEncoder...
  116. ::
  117. */
  118. public function __construct($GIF_src, $GIF_dly, $GIF_lop, $GIF_dis,$GIF_red, $GIF_grn, $GIF_blu, $GIF_mod) {
  119. if ( ! is_array ( $GIF_src ) && ! is_array ( $GIF_dly ) ) {
  120. printf ( "%s: %s", $this->VER, $this->ERR [ 'ERR00' ] );
  121. exit ( 0 );
  122. }
  123. $this->LOP = ( $GIF_lop > -1 ) ? $GIF_lop : 0;
  124. $this->DIS = ( $GIF_dis > -1 ) ? ( ( $GIF_dis < 3 ) ? $GIF_dis : 3 ) : 2;
  125. $this->COL = ( $GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1 ) ?
  126. ( $GIF_red | ( $GIF_grn << 8 ) | ( $GIF_blu << 16 ) ) : -1;
  127. for ( $i = 0; $i < count ( $GIF_src ); $i++ ) {
  128. if ( strToLower ( $GIF_mod ) == "url" ) {
  129. $this->BUF [ ] = fread ( fopen ( $GIF_src [ $i ], "rb" ), filesize ( $GIF_src [ $i ] ) );
  130. }
  131. else if ( strToLower ( $GIF_mod ) == "bin" ) {
  132. $this->BUF [ ] = $GIF_src [ $i ];
  133. }
  134. else {
  135. printf ( "%s: %s ( %s )!", $this->VER, $this->ERR [ 'ERR02' ], $GIF_mod );
  136. exit ( 0 );
  137. }
  138. if ( substr ( $this->BUF [ $i ], 0, 6 ) != "GIF87a" && substr ( $this->BUF [ $i ], 0, 6 ) != "GIF89a" ) {
  139. printf ( "%s: %d %s", $this->VER, $i, $this->ERR [ 'ERR01' ] );
  140. exit ( 0 );
  141. }
  142. for ( $j = ( 13 + 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ) ), $k = TRUE; $k; $j++ ) {
  143. switch ( $this->BUF [ $i ] { $j } ) {
  144. case "!":
  145. if ( ( substr ( $this->BUF [ $i ], ( $j + 3 ), 8 ) ) == "NETSCAPE" ) {
  146. printf ( "%s: %s ( %s source )!", $this->VER, $this->ERR [ 'ERR03' ], ( $i + 1 ) );
  147. exit ( 0 );
  148. }
  149. break;
  150. case ";":
  151. $k = FALSE;
  152. break;
  153. }
  154. }
  155. }
  156. $this->GIFAddHeader ( );
  157. for ( $i = 0; $i < count ( $this->BUF ); $i++ ) {
  158. $this->GIFAddFrames ( $i, $GIF_dly [ $i ] );
  159. }
  160. $this->GIFAddFooter ( );
  161. }
  162. /*
  163. :::::::::::::::::::::::::::::::::::::::::::::::::::
  164. ::
  165. :: GIFAddHeader...
  166. ::
  167. */
  168. private function GIFAddHeader ( ) {
  169. $cmap = 0;
  170. if ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x80 ) {
  171. $cmap = 3 * ( 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 ) );
  172. $this->GIF .= substr ( $this->BUF [ 0 ], 6, 7 );
  173. $this->GIF .= substr ( $this->BUF [ 0 ], 13, $cmap );
  174. $this->GIF .= "!\377\13NETSCAPE2.0\3\1" . $this->GIFWord ( $this->LOP ) . "\0";
  175. }
  176. }
  177. /*
  178. :::::::::::::::::::::::::::::::::::::::::::::::::::
  179. ::
  180. :: GIFAddFrames...
  181. ::
  182. */
  183. private function GIFAddFrames ( $i, $d ) {
  184. $Locals_str = 13 + 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) );
  185. $Locals_end = strlen ( $this->BUF [ $i ] ) - $Locals_str - 1;
  186. $Locals_tmp = substr ( $this->BUF [ $i ], $Locals_str, $Locals_end );
  187. $Global_len = 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 );
  188. $Locals_len = 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 );
  189. $Global_rgb = substr ( $this->BUF [ 0 ], 13,
  190. 3 * ( 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 ) ) );
  191. $Locals_rgb = substr ( $this->BUF [ $i ], 13,
  192. 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ) );
  193. $Locals_ext = "!\xF9\x04" . chr ( ( $this->DIS << 2 ) + 0 ) .
  194. chr ( ( $d >> 0 ) & 0xFF ) . chr ( ( $d >> 8 ) & 0xFF ) . "\x0\x0";
  195. if ( $this->COL > -1 && ord ( $this->BUF [ $i ] { 10 } ) & 0x80 ) {
  196. for ( $j = 0; $j < ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ); $j++ ) {
  197. if (
  198. ord ( $Locals_rgb { 3 * $j + 0 } ) == ( ( $this->COL >> 16 ) & 0xFF ) &&
  199. ord ( $Locals_rgb { 3 * $j + 1 } ) == ( ( $this->COL >> 8 ) & 0xFF ) &&
  200. ord ( $Locals_rgb { 3 * $j + 2 } ) == ( ( $this->COL >> 0 ) & 0xFF )
  201. ) {
  202. $Locals_ext = "!\xF9\x04" . chr ( ( $this->DIS << 2 ) + 1 ) .
  203. chr ( ( $d >> 0 ) & 0xFF ) . chr ( ( $d >> 8 ) & 0xFF ) . chr ( $j ) . "\x0";
  204. break;
  205. }
  206. }
  207. }
  208. switch ( $Locals_tmp { 0 } ) {
  209. case "!":
  210. $Locals_img = substr ( $Locals_tmp, 8, 10 );
  211. $Locals_tmp = substr ( $Locals_tmp, 18, strlen ( $Locals_tmp ) - 18 );
  212. break;
  213. case ",":
  214. $Locals_img = substr ( $Locals_tmp, 0, 10 );
  215. $Locals_tmp = substr ( $Locals_tmp, 10, strlen ( $Locals_tmp ) - 10 );
  216. break;
  217. }
  218. if ( ord ( $this->BUF [ $i ] { 10 } ) & 0x80 && $this->IMG > -1 ) {
  219. if ( $Global_len == $Locals_len ) {
  220. if ( $this->GIFBlockCompare ( $Global_rgb, $Locals_rgb, $Global_len ) ) {
  221. $this->GIF .= ( $Locals_ext . $Locals_img . $Locals_tmp );
  222. }
  223. else {
  224. $byte = ord ( $Locals_img { 9 } );
  225. $byte |= 0x80;
  226. $byte &= 0xF8;
  227. $byte |= ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 );
  228. $Locals_img { 9 } = chr ( $byte );
  229. $this->GIF .= ( $Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp );
  230. }
  231. }
  232. else {
  233. $byte = ord ( $Locals_img { 9 } );
  234. $byte |= 0x80;
  235. $byte &= 0xF8;
  236. $byte |= ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 );
  237. $Locals_img { 9 } = chr ( $byte );
  238. $this->GIF .= ( $Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp );
  239. }
  240. }
  241. else {
  242. $this->GIF .= ( $Locals_ext . $Locals_img . $Locals_tmp );
  243. }
  244. $this->IMG = 1;
  245. }
  246. /*
  247. :::::::::::::::::::::::::::::::::::::::::::::::::::
  248. ::
  249. :: GIFAddFooter...
  250. ::
  251. */
  252. private function GIFAddFooter ( ) {
  253. $this->GIF .= ";";
  254. }
  255. /*
  256. :::::::::::::::::::::::::::::::::::::::::::::::::::
  257. ::
  258. :: GIFBlockCompare...
  259. ::
  260. */
  261. private function GIFBlockCompare ( $GlobalBlock, $LocalBlock, $Len ) {
  262. for ( $i = 0; $i < $Len; $i++ ) {
  263. if (
  264. $GlobalBlock { 3 * $i + 0 } != $LocalBlock { 3 * $i + 0 } ||
  265. $GlobalBlock { 3 * $i + 1 } != $LocalBlock { 3 * $i + 1 } ||
  266. $GlobalBlock { 3 * $i + 2 } != $LocalBlock { 3 * $i + 2 }
  267. ) {
  268. return ( 0 );
  269. }
  270. }
  271. return ( 1 );
  272. }
  273. /*
  274. :::::::::::::::::::::::::::::::::::::::::::::::::::
  275. ::
  276. :: GIFWord...
  277. ::
  278. */
  279. private function GIFWord ( $int ) {
  280. return ( chr ( $int & 0xFF ) . chr ( ( $int >> 8 ) & 0xFF ) );
  281. }
  282. /*
  283. :::::::::::::::::::::::::::::::::::::::::::::::::::
  284. ::
  285. :: GetAnimation...
  286. ::
  287. */
  288. public function GetAnimation ( ) {
  289. return ( $this->GIF );
  290. }
  291. }
  292. /*
  293. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  294. ::
  295. :: GIFDecoder Version 2.0 by László Zsidi, http://gifs.hu
  296. ::
  297. :: Created at 2007. 02. 01. '07.47.AM'
  298. ::
  299. ::
  300. ::
  301. ::
  302. :: Try on-line GIFBuilder Form demo based on GIFDecoder.
  303. ::
  304. :: http://gifs.hu/phpclasses/demos/GifBuilder/
  305. ::
  306. :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  307. */
  308. Class GIFDecoder {
  309. private $GIF_buffer = Array ( );
  310. private $GIF_arrays = Array ( );
  311. private $GIF_delays = Array ( );
  312. private $GIF_stream = "";
  313. private $GIF_string = "";
  314. private $GIF_bfseek = 0;
  315. private $GIF_screen = Array ( );
  316. private $GIF_global = Array ( );
  317. private $GIF_sorted;
  318. private $GIF_colorS;
  319. private $GIF_colorC;
  320. private $GIF_colorF;
  321. /*
  322. :::::::::::::::::::::::::::::::::::::::::::::::::::
  323. ::
  324. :: GIFDecoder ( $GIF_pointer )
  325. ::
  326. */
  327. public function __construct ( $GIF_pointer ) {
  328. $this->GIF_stream = $GIF_pointer;
  329. $this->GIFGetByte ( 6 ); // GIF89a
  330. $this->GIFGetByte ( 7 ); // Logical Screen Descriptor
  331. $this->GIF_screen = $this->GIF_buffer;
  332. $this->GIF_colorF = $this->GIF_buffer [ 4 ] & 0x80 ? 1 : 0;
  333. $this->GIF_sorted = $this->GIF_buffer [ 4 ] & 0x08 ? 1 : 0;
  334. $this->GIF_colorC = $this->GIF_buffer [ 4 ] & 0x07;
  335. $this->GIF_colorS = 2 << $this->GIF_colorC;
  336. if ( $this->GIF_colorF == 1 ) {
  337. $this->GIFGetByte ( 3 * $this->GIF_colorS );
  338. $this->GIF_global = $this->GIF_buffer;
  339. }
  340. /*
  341. *
  342. * 05.06.2007.
  343. * Made a little modification
  344. *
  345. *
  346. - for ( $cycle = 1; $cycle; ) {
  347. + if ( GIFDecoder::GIFGetByte ( 1 ) ) {
  348. - switch ( $this->GIF_buffer [ 0 ] ) {
  349. - case 0x21:
  350. - GIFDecoder::GIFReadExtensions ( );
  351. - break;
  352. - case 0x2C:
  353. - GIFDecoder::GIFReadDescriptor ( );
  354. - break;
  355. - case 0x3B:
  356. - $cycle = 0;
  357. - break;
  358. - }
  359. - }
  360. + else {
  361. + $cycle = 0;
  362. + }
  363. - }
  364. */
  365. for ( $cycle = 1; $cycle; ) {
  366. if ( $this->GIFGetByte ( 1 ) ) {
  367. switch ( $this->GIF_buffer [ 0 ] ) {
  368. case 0x21:
  369. $this->GIFReadExtensions ( );
  370. break;
  371. case 0x2C:
  372. $this->GIFReadDescriptor ( );
  373. break;
  374. case 0x3B:
  375. $cycle = 0;
  376. break;
  377. }
  378. }
  379. else {
  380. $cycle = 0;
  381. }
  382. }
  383. }
  384. /*
  385. :::::::::::::::::::::::::::::::::::::::::::::::::::
  386. ::
  387. :: GIFReadExtension ( )
  388. ::
  389. */
  390. private function GIFReadExtensions ( ) {
  391. $this->GIFGetByte ( 1 );
  392. for ( ; ; ) {
  393. $this->GIFGetByte ( 1 );
  394. if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
  395. break;
  396. }
  397. $this->GIFGetByte ( $u );
  398. /*
  399. * 07.05.2007.
  400. * Implemented a new line for a new function
  401. * to determine the originaly delays between
  402. * frames.
  403. *
  404. */
  405. if ( $u == 4 ) {
  406. $this->GIF_delays [ ] = ( $this->GIF_buffer [ 1 ] | $this->GIF_buffer [ 2 ] << 8 );
  407. }
  408. }
  409. }
  410. /*
  411. :::::::::::::::::::::::::::::::::::::::::::::::::::
  412. ::
  413. :: GIFReadExtension ( )
  414. ::
  415. */
  416. private function GIFReadDescriptor ( ) {
  417. $GIF_screen = Array ( );
  418. $this->GIFGetByte ( 9 );
  419. $GIF_screen = $this->GIF_buffer;
  420. $GIF_colorF = $this->GIF_buffer [ 8 ] & 0x80 ? 1 : 0;
  421. if ( $GIF_colorF ) {
  422. $GIF_code = $this->GIF_buffer [ 8 ] & 0x07;
  423. $GIF_sort = $this->GIF_buffer [ 8 ] & 0x20 ? 1 : 0;
  424. }
  425. else {
  426. $GIF_code = $this->GIF_colorC;
  427. $GIF_sort = $this->GIF_sorted;
  428. }
  429. $GIF_size = 2 << $GIF_code;
  430. $this->GIF_screen [ 4 ] &= 0x70;
  431. $this->GIF_screen [ 4 ] |= 0x80;
  432. $this->GIF_screen [ 4 ] |= $GIF_code;
  433. if ( $GIF_sort ) {
  434. $this->GIF_screen [ 4 ] |= 0x08;
  435. }
  436. $this->GIF_string = "GIF87a";
  437. $this->GIFPutByte ( $this->GIF_screen );
  438. if ( $GIF_colorF == 1 ) {
  439. $this->GIFGetByte ( 3 * $GIF_size );
  440. $this->GIFPutByte ( $this->GIF_buffer );
  441. }
  442. else {
  443. $this->GIFPutByte ( $this->GIF_global );
  444. }
  445. $this->GIF_string .= chr ( 0x2C );
  446. $GIF_screen [ 8 ] &= 0x40;
  447. $this->GIFPutByte ( $GIF_screen );
  448. $this->GIFGetByte ( 1 );
  449. $this->GIFPutByte ( $this->GIF_buffer );
  450. for ( ; ; ) {
  451. $this->GIFGetByte ( 1 );
  452. $this->GIFPutByte ( $this->GIF_buffer );
  453. if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
  454. break;
  455. }
  456. $this->GIFGetByte ( $u );
  457. $this->GIFPutByte ( $this->GIF_buffer );
  458. }
  459. $this->GIF_string .= chr ( 0x3B );
  460. /*
  461. Add frames into $GIF_stream array...
  462. */
  463. $this->GIF_arrays [ ] = $this->GIF_string;
  464. }
  465. /*
  466. :::::::::::::::::::::::::::::::::::::::::::::::::::
  467. ::
  468. :: GIFGetByte ( $len )
  469. ::
  470. */
  471. /*
  472. *
  473. * 05.06.2007.
  474. * Made a little modification
  475. *
  476. *
  477. - function GIFGetByte ( $len ) {
  478. - $this->GIF_buffer = Array ( );
  479. -
  480. - for ( $i = 0; $i < $len; $i++ ) {
  481. + if ( $this->GIF_bfseek > strlen ( $this->GIF_stream ) ) {
  482. + return 0;
  483. + }
  484. - $this->GIF_buffer [ ] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
  485. - }
  486. + return 1;
  487. - }
  488. */
  489. private function GIFGetByte ( $len ) {
  490. $this->GIF_buffer = Array ( );
  491. for ( $i = 0; $i < $len; $i++ ) {
  492. if ( $this->GIF_bfseek > strlen ( $this->GIF_stream ) ) {
  493. return 0;
  494. }
  495. $this->GIF_buffer [ ] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
  496. }
  497. return 1;
  498. }
  499. /*
  500. :::::::::::::::::::::::::::::::::::::::::::::::::::
  501. ::
  502. :: GIFPutByte ( $bytes )
  503. ::
  504. */
  505. private function GIFPutByte ( $bytes ) {
  506. for ( $i = 0; $i < count ( $bytes ); $i++ ) {
  507. $this->GIF_string .= chr ( $bytes [ $i ] );
  508. }
  509. }
  510. /*
  511. :::::::::::::::::::::::::::::::::::::::::::::::::::
  512. ::
  513. :: PUBLIC FUNCTIONS
  514. ::
  515. ::
  516. :: GIFGetFrames ( )
  517. ::
  518. */
  519. public function GIFGetFrames ( ) {
  520. return ( $this->GIF_arrays );
  521. }
  522. /*
  523. :::::::::::::::::::::::::::::::::::::::::::::::::::
  524. ::
  525. :: GIFGetDelays ( )
  526. ::
  527. */
  528. public function GIFGetDelays ( ) {
  529. return ( $this->GIF_delays );
  530. }
  531. }