PageRenderTime 130ms CodeModel.GetById 77ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

/ThinkPHP/Library/Think/Image/Driver/GIF.class.php

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