PageRenderTime 2ms CodeModel.GetById 14ms app.highlight 19ms RepoModel.GetById 2ms app.codeStats 0ms

/ThinkPHP/Extend/Library/ORG/Util/Image/Driver/GIF.class.php

https://github.com/huangshan/yuqing
PHP | 570 lines | 315 code | 37 blank | 218 comment | 70 complexity | f0eeb0195700651416b2e33be8da434d 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.cn@gmail.com> <http://www.zjzit.cn>
 10// +----------------------------------------------------------------------
 11// | GIF.class.php 2013-03-09
 12// +----------------------------------------------------------------------
 13
 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				throw new Exception("解码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	var $GIF = "GIF89a";		/* GIF header 6 bytes	*/
110	var $VER = "GIFEncoder V2.05";	/* Encoder version		*/
111
112	var $BUF = Array ( );
113	var $LOP =  0;
114	var $DIS =  2;
115	var $COL = -1;
116	var $IMG = -1;
117
118	var $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	function GIFEncoder	(
132							$GIF_src, $GIF_dly, $GIF_lop, $GIF_dis,
133							$GIF_red, $GIF_grn, $GIF_blu, $GIF_mod
134						) {
135		if ( ! is_array ( $GIF_src ) && ! is_array ( $GIF_tim ) ) {
136			printf	( "%s: %s", $this->VER, $this->ERR [ 'ERR00' ] );
137			exit	( 0 );
138		}
139		$this->LOP = ( $GIF_lop > -1 ) ? $GIF_lop : 0;
140		$this->DIS = ( $GIF_dis > -1 ) ? ( ( $GIF_dis < 3 ) ? $GIF_dis : 3 ) : 2;
141		$this->COL = ( $GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1 ) ?
142						( $GIF_red | ( $GIF_grn << 8 ) | ( $GIF_blu << 16 ) ) : -1;
143
144		for ( $i = 0; $i < count ( $GIF_src ); $i++ ) {
145			if ( strToLower ( $GIF_mod ) == "url" ) {
146				$this->BUF [ ] = fread ( fopen ( $GIF_src [ $i ], "rb" ), filesize ( $GIF_src [ $i ] ) );
147			}
148			else if ( strToLower ( $GIF_mod ) == "bin" ) {
149				$this->BUF [ ] = $GIF_src [ $i ];
150			}
151			else {
152				printf	( "%s: %s ( %s )!", $this->VER, $this->ERR [ 'ERR02' ], $GIF_mod );
153				exit	( 0 );
154			}
155			if ( substr ( $this->BUF [ $i ], 0, 6 ) != "GIF87a" && substr ( $this->BUF [ $i ], 0, 6 ) != "GIF89a" ) {
156				printf	( "%s: %d %s", $this->VER, $i, $this->ERR [ 'ERR01' ] );
157				exit	( 0 );
158			}
159			for ( $j = ( 13 + 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ) ), $k = TRUE; $k; $j++ ) {
160				switch ( $this->BUF [ $i ] { $j } ) {
161					case "!":
162						if ( ( substr ( $this->BUF [ $i ], ( $j + 3 ), 8 ) ) == "NETSCAPE" ) {
163							printf	( "%s: %s ( %s source )!", $this->VER, $this->ERR [ 'ERR03' ], ( $i + 1 ) );
164							exit	( 0 );
165						}
166						break;
167					case ";":
168						$k = FALSE;
169						break;
170				}
171			}
172		}
173		GIFEncoder::GIFAddHeader ( );
174		for ( $i = 0; $i < count ( $this->BUF ); $i++ ) {
175			GIFEncoder::GIFAddFrames ( $i, $GIF_dly [ $i ] );
176		}
177		GIFEncoder::GIFAddFooter ( );
178	}
179	/*
180	:::::::::::::::::::::::::::::::::::::::::::::::::::
181	::
182	::	GIFAddHeader...
183	::
184	*/
185	function GIFAddHeader ( ) {
186		$cmap = 0;
187
188		if ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x80 ) {
189			$cmap = 3 * ( 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 ) );
190
191			$this->GIF .= substr ( $this->BUF [ 0 ], 6, 7		);
192			$this->GIF .= substr ( $this->BUF [ 0 ], 13, $cmap	);
193			$this->GIF .= "!\377\13NETSCAPE2.0\3\1" . GIFEncoder::GIFWord ( $this->LOP ) . "\0";
194		}
195	}
196	/*
197	:::::::::::::::::::::::::::::::::::::::::::::::::::
198	::
199	::	GIFAddFrames...
200	::
201	*/
202	function GIFAddFrames ( $i, $d ) {
203
204		$Locals_str = 13 + 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) );
205
206		$Locals_end = strlen ( $this->BUF [ $i ] ) - $Locals_str - 1;
207		$Locals_tmp = substr ( $this->BUF [ $i ], $Locals_str, $Locals_end );
208
209		$Global_len = 2 << ( ord ( $this->BUF [ 0  ] { 10 } ) & 0x07 );
210		$Locals_len = 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 );
211
212		$Global_rgb = substr ( $this->BUF [ 0  ], 13,
213							3 * ( 2 << ( ord ( $this->BUF [ 0  ] { 10 } ) & 0x07 ) ) );
214		$Locals_rgb = substr ( $this->BUF [ $i ], 13,
215							3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ) );
216
217		$Locals_ext = "!\xF9\x04" . chr ( ( $this->DIS << 2 ) + 0 ) .
218						chr ( ( $d >> 0 ) & 0xFF ) . chr ( ( $d >> 8 ) & 0xFF ) . "\x0\x0";
219
220		if ( $this->COL > -1 && ord ( $this->BUF [ $i ] { 10 } ) & 0x80 ) {
221			for ( $j = 0; $j < ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ); $j++ ) {
222				if	(
223						ord ( $Locals_rgb { 3 * $j + 0 } ) == ( ( $this->COL >> 16 ) & 0xFF ) &&
224						ord ( $Locals_rgb { 3 * $j + 1 } ) == ( ( $this->COL >>  8 ) & 0xFF ) &&
225						ord ( $Locals_rgb { 3 * $j + 2 } ) == ( ( $this->COL >>  0 ) & 0xFF )
226					) {
227					$Locals_ext = "!\xF9\x04" . chr ( ( $this->DIS << 2 ) + 1 ) .
228									chr ( ( $d >> 0 ) & 0xFF ) . chr ( ( $d >> 8 ) & 0xFF ) . chr ( $j ) . "\x0";
229					break;
230				}
231			}
232		}
233		switch ( $Locals_tmp { 0 } ) {
234			case "!":
235				$Locals_img = substr ( $Locals_tmp, 8, 10 );
236				$Locals_tmp = substr ( $Locals_tmp, 18, strlen ( $Locals_tmp ) - 18 );
237				break;
238			case ",":
239				$Locals_img = substr ( $Locals_tmp, 0, 10 );
240				$Locals_tmp = substr ( $Locals_tmp, 10, strlen ( $Locals_tmp ) - 10 );
241				break;
242		}
243		if ( ord ( $this->BUF [ $i ] { 10 } ) & 0x80 && $this->IMG > -1 ) {
244			if ( $Global_len == $Locals_len ) {
245				if ( GIFEncoder::GIFBlockCompare ( $Global_rgb, $Locals_rgb, $Global_len ) ) {
246					$this->GIF .= ( $Locals_ext . $Locals_img . $Locals_tmp );
247				}
248				else {
249					$byte  = ord ( $Locals_img { 9 } );
250					$byte |= 0x80;
251					$byte &= 0xF8;
252					$byte |= ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 );
253					$Locals_img { 9 } = chr ( $byte );
254					$this->GIF .= ( $Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp );
255				}
256			}
257			else {
258				$byte  = ord ( $Locals_img { 9 } );
259				$byte |= 0x80;
260				$byte &= 0xF8;
261				$byte |= ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 );
262				$Locals_img { 9 } = chr ( $byte );
263				$this->GIF .= ( $Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp );
264			}
265		}
266		else {
267			$this->GIF .= ( $Locals_ext . $Locals_img . $Locals_tmp );
268		}
269		$this->IMG  = 1;
270	}
271	/*
272	:::::::::::::::::::::::::::::::::::::::::::::::::::
273	::
274	::	GIFAddFooter...
275	::
276	*/
277	function GIFAddFooter ( ) {
278		$this->GIF .= ";";
279	}
280	/*
281	:::::::::::::::::::::::::::::::::::::::::::::::::::
282	::
283	::	GIFBlockCompare...
284	::
285	*/
286	function GIFBlockCompare ( $GlobalBlock, $LocalBlock, $Len ) {
287
288		for ( $i = 0; $i < $Len; $i++ ) {
289			if	(
290					$GlobalBlock { 3 * $i + 0 } != $LocalBlock { 3 * $i + 0 } ||
291					$GlobalBlock { 3 * $i + 1 } != $LocalBlock { 3 * $i + 1 } ||
292					$GlobalBlock { 3 * $i + 2 } != $LocalBlock { 3 * $i + 2 }
293				) {
294					return ( 0 );
295			}
296		}
297
298		return ( 1 );
299	}
300	/*
301	:::::::::::::::::::::::::::::::::::::::::::::::::::
302	::
303	::	GIFWord...
304	::
305	*/
306	function GIFWord ( $int ) {
307
308		return ( chr ( $int & 0xFF ) . chr ( ( $int >> 8 ) & 0xFF ) );
309	}
310	/*
311	:::::::::::::::::::::::::::::::::::::::::::::::::::
312	::
313	::	GetAnimation...
314	::
315	*/
316	function GetAnimation ( ) {
317		return ( $this->GIF );
318	}
319}
320
321
322/*
323:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
324::
325::	GIFDecoder Version 2.0 by László Zsidi, http://gifs.hu
326::
327::	Created at 2007. 02. 01. '07.47.AM'
328::
329::
330::
331::
332::  Try on-line GIFBuilder Form demo based on GIFDecoder.
333::
334::  http://gifs.hu/phpclasses/demos/GifBuilder/
335::
336:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
337*/
338
339Class GIFDecoder {
340	var $GIF_buffer = Array ( );
341	var $GIF_arrays = Array ( );
342	var $GIF_delays = Array ( );
343	var $GIF_stream = "";
344	var $GIF_string = "";
345	var $GIF_bfseek =  0;
346
347	var $GIF_screen = Array ( );
348	var $GIF_global = Array ( );
349	var $GIF_sorted;
350	var $GIF_colorS;
351	var $GIF_colorC;
352	var $GIF_colorF;
353
354	/*
355	:::::::::::::::::::::::::::::::::::::::::::::::::::
356	::
357	::	GIFDecoder ( $GIF_pointer )
358	::
359	*/
360	function GIFDecoder ( $GIF_pointer ) {
361		$this->GIF_stream = $GIF_pointer;
362
363		GIFDecoder::GIFGetByte ( 6 );	// GIF89a
364		GIFDecoder::GIFGetByte ( 7 );	// Logical Screen Descriptor
365
366		$this->GIF_screen = $this->GIF_buffer;
367		$this->GIF_colorF = $this->GIF_buffer [ 4 ] & 0x80 ? 1 : 0;
368		$this->GIF_sorted = $this->GIF_buffer [ 4 ] & 0x08 ? 1 : 0;
369		$this->GIF_colorC = $this->GIF_buffer [ 4 ] & 0x07;
370		$this->GIF_colorS = 2 << $this->GIF_colorC;
371
372		if ( $this->GIF_colorF == 1 ) {
373			GIFDecoder::GIFGetByte ( 3 * $this->GIF_colorS );
374			$this->GIF_global = $this->GIF_buffer;
375		}
376		/*
377		 *
378		 *  05.06.2007.
379		 *  Made a little modification
380		 *
381		 *
382		 -	for ( $cycle = 1; $cycle; ) {
383		 +		if ( GIFDecoder::GIFGetByte ( 1 ) ) {
384		 -			switch ( $this->GIF_buffer [ 0 ] ) {
385		 -				case 0x21:
386		 -					GIFDecoder::GIFReadExtensions ( );
387		 -					break;
388		 -				case 0x2C:
389		 -					GIFDecoder::GIFReadDescriptor ( );
390		 -					break;
391		 -				case 0x3B:
392		 -					$cycle = 0;
393		 -					break;
394		 -		  	}
395		 -		}
396		 +		else {
397		 +			$cycle = 0;
398		 +		}
399		 -	}
400		*/
401		for ( $cycle = 1; $cycle; ) {
402			if ( GIFDecoder::GIFGetByte ( 1 ) ) {
403				switch ( $this->GIF_buffer [ 0 ] ) {
404					case 0x21:
405						GIFDecoder::GIFReadExtensions ( );
406						break;
407					case 0x2C:
408						GIFDecoder::GIFReadDescriptor ( );
409						break;
410					case 0x3B:
411						$cycle = 0;
412						break;
413				}
414			}
415			else {
416				$cycle = 0;
417			}
418		}
419	}
420	/*
421	:::::::::::::::::::::::::::::::::::::::::::::::::::
422	::
423	::	GIFReadExtension ( )
424	::
425	*/
426	function GIFReadExtensions ( ) {
427		GIFDecoder::GIFGetByte ( 1 );
428		for ( ; ; ) {
429			GIFDecoder::GIFGetByte ( 1 );
430			if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
431				break;
432			}
433			GIFDecoder::GIFGetByte ( $u );
434			/*
435			 * 07.05.2007.
436			 * Implemented a new line for a new function
437			 * to determine the originaly delays between
438			 * frames.
439			 *
440			 */
441			if ( $u == 4 ) {
442				$this->GIF_delays [ ] = ( $this->GIF_buffer [ 1 ] | $this->GIF_buffer [ 2 ] << 8 );
443			}
444		}
445	}
446	/*
447	:::::::::::::::::::::::::::::::::::::::::::::::::::
448	::
449	::	GIFReadExtension ( )
450	::
451	*/
452	function GIFReadDescriptor ( ) {
453		$GIF_screen	= Array ( );
454
455		GIFDecoder::GIFGetByte ( 9 );
456		$GIF_screen = $this->GIF_buffer;
457		$GIF_colorF = $this->GIF_buffer [ 8 ] & 0x80 ? 1 : 0;
458		if ( $GIF_colorF ) {
459			$GIF_code = $this->GIF_buffer [ 8 ] & 0x07;
460			$GIF_sort = $this->GIF_buffer [ 8 ] & 0x20 ? 1 : 0;
461		}
462		else {
463			$GIF_code = $this->GIF_colorC;
464			$GIF_sort = $this->GIF_sorted;
465		}
466		$GIF_size = 2 << $GIF_code;
467		$this->GIF_screen [ 4 ] &= 0x70;
468		$this->GIF_screen [ 4 ] |= 0x80;
469		$this->GIF_screen [ 4 ] |= $GIF_code;
470		if ( $GIF_sort ) {
471			$this->GIF_screen [ 4 ] |= 0x08;
472		}
473		$this->GIF_string = "GIF87a";
474		GIFDecoder::GIFPutByte ( $this->GIF_screen );
475		if ( $GIF_colorF == 1 ) {
476			GIFDecoder::GIFGetByte ( 3 * $GIF_size );
477			GIFDecoder::GIFPutByte ( $this->GIF_buffer );
478		}
479		else {
480			GIFDecoder::GIFPutByte ( $this->GIF_global );
481		}
482		$this->GIF_string .= chr ( 0x2C );
483		$GIF_screen [ 8 ] &= 0x40;
484		GIFDecoder::GIFPutByte ( $GIF_screen );
485		GIFDecoder::GIFGetByte ( 1 );
486		GIFDecoder::GIFPutByte ( $this->GIF_buffer );
487		for ( ; ; ) {
488			GIFDecoder::GIFGetByte ( 1 );
489			GIFDecoder::GIFPutByte ( $this->GIF_buffer );
490			if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
491				break;
492			}
493			GIFDecoder::GIFGetByte ( $u );
494			GIFDecoder::GIFPutByte ( $this->GIF_buffer );
495		}
496		$this->GIF_string .= chr ( 0x3B );
497		/*
498		   Add frames into $GIF_stream array...
499		*/
500		$this->GIF_arrays [ ] = $this->GIF_string;
501	}
502	/*
503	:::::::::::::::::::::::::::::::::::::::::::::::::::
504	::
505	::	GIFGetByte ( $len )
506	::
507	*/
508
509	/*
510	 *
511	 *  05.06.2007.
512	 *  Made a little modification
513	 *
514	 *
515	 -	function GIFGetByte ( $len ) {
516	 -		$this->GIF_buffer = Array ( );
517	 -
518	 -		for ( $i = 0; $i < $len; $i++ ) {
519	 +			if ( $this->GIF_bfseek > strlen ( $this->GIF_stream ) ) {
520	 +				return 0;
521	 +			}
522	 -			$this->GIF_buffer [ ] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
523	 -		}
524	 +		return 1;
525	 -	}
526	 */
527	function GIFGetByte ( $len ) {
528		$this->GIF_buffer = Array ( );
529
530		for ( $i = 0; $i < $len; $i++ ) {
531			if ( $this->GIF_bfseek > strlen ( $this->GIF_stream ) ) {
532				return 0;
533			}
534			$this->GIF_buffer [ ] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
535		}
536		return 1;
537	}
538	/*
539	:::::::::::::::::::::::::::::::::::::::::::::::::::
540	::
541	::	GIFPutByte ( $bytes )
542	::
543	*/
544	function GIFPutByte ( $bytes ) {
545		for ( $i = 0; $i < count ( $bytes ); $i++ ) {
546			$this->GIF_string .= chr ( $bytes [ $i ] );
547		}
548	}
549	/*
550	:::::::::::::::::::::::::::::::::::::::::::::::::::
551	::
552	::	PUBLIC FUNCTIONS
553	::
554	::
555	::	GIFGetFrames ( )
556	::
557	*/
558	function GIFGetFrames ( ) {
559		return ( $this->GIF_arrays );
560	}
561	/*
562	:::::::::::::::::::::::::::::::::::::::::::::::::::
563	::
564	::	GIFGetDelays ( )
565	::
566	*/
567	function GIFGetDelays ( ) {
568		return ( $this->GIF_delays );
569	}
570}