1<?php
2///////////////////////////////////////////////////////////////////////////////////////////////////
3// GIF Util - (C) 2003 Yamasoft (S/C)
4// http://www.yamasoft.com
5// All Rights Reserved
6// This file can be freely copied, distributed, modified, updated by anyone under the only
7// condition to leave the original address (Yamasoft, http://www.yamasoft.com) and this header.
8///////////////////////////////////////////////////////////////////////////////////////////////////
9// <gif>  = gif_loadFile(filename, [index])
10// <bool> = gif_getSize(<gif> or filename, &width, &height)
11// <bool> = gif_outputAsPng(<gif>, filename, [bgColor])
12// <bool> = gif_outputAsBmp(<gif>, filename, [bgcolor])
13// <bool> = gif_outputAsJpeg(<gif>, filename, [bgcolor]) - use cjpeg if available otherwise uses GD
14///////////////////////////////////////////////////////////////////////////////////////////////////
15// Original code by Fabien Ezber
16// Modified by James Heinrich <info@silisoftware.com> for use in phpThumb() - December 10, 2003
17// * Added function gif_loadFileToGDimageResource() - this returns a GD image resource
18// * Modified gif_outputAsJpeg() to check if it's running under Windows, or if cjpeg is not
19//   available, in which case it will attempt to output JPEG using GD functions
20// * added @ error-suppression to two lines where it checks: if ($this->m_img->m_bTrans)
21//   otherwise warnings are generated if error_reporting == E_ALL
22///////////////////////////////////////////////////////////////////////////////////////////////////
23
24function gif_loadFile($lpszFileName, $iIndex = 0)
25{
26	$gif = new CGIF();
27	if ($gif->loadFile($lpszFileName, $iIndex)) {
28		return $gif;
29	}
30	return false;
31}
32
33///////////////////////////////////////////////////////////////////////////////////////////////////
34
35// Added by James Heinrich <info@silisoftware.com> - December 10, 2003
36function gif_loadFileToGDimageResource($gifFilename, $bgColor = -1)
37{
38	if ($gif = gif_loadFile($gifFilename)) {
39
40		if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) {
41			// shouldn't take nearly this long
42			set_time_limit(120);
43		}
44		// general strategy: convert raw data to PNG then convert PNG data to GD image resource
45		$PNGdata = $gif->getPng($bgColor);
46		if ($img = @imagecreatefromstring($PNGdata)) {
47
48			// excellent - PNG image data successfully converted to GD image
49			return $img;
50
51		} elseif ($img = $gif->getGD_PixelPlotterVersion()) {
52
53			// problem: imagecreatefromstring() didn't like the PNG image data.
54			//   This has been known to happen in PHP v4.0.6
55			// solution: take the raw image data and create a new GD image and plot
56			//   pixel-by-pixel on the GD image. This is extremely slow, but it does
57			//   work and a slow solution is better than no solution, right? :)
58			return $img;
59
60		}
61	}
62	return false;
63}
64
65///////////////////////////////////////////////////////////////////////////////////////////////////
66
67function gif_outputAsBmp($gif, $lpszFileName, $bgColor = -1)
68{
69	if (!isset($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) {
70		return false;
71	}
72
73	$fd = $gif->getBmp($bgColor);
74	if (strlen($fd) <= 0) {
75		return false;
76	}
77
78	if (!($fh = @fopen($lpszFileName, 'wb'))) {
79		return false;
80	}
81	@fwrite($fh, $fd, strlen($fd));
82	@fflush($fh);
83	@fclose($fh);
84	return true;
85}
86
87///////////////////////////////////////////////////////////////////////////////////////////////////
88
89function gif_outputAsPng($gif, $lpszFileName, $bgColor = -1)
90{
91	if (!isSet($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) {
92		return false;
93	}
94
95	$fd = $gif->getPng($bgColor);
96	if (strlen($fd) <= 0) {
97		return false;
98	}
99
100	if (!($fh = @fopen($lpszFileName, 'wb'))) {
101		return false;
102	}
103	@fwrite($fh, $fd, strlen($fd));
104	@fflush($fh);
105	@fclose($fh);
106	return true;
107}
108
109///////////////////////////////////////////////////////////////////////////////////////////////////
110
111function gif_outputAsJpeg($gif, $lpszFileName, $bgColor = -1)
112{
113	// JPEG output that does not require cjpeg added by James Heinrich <info@silisoftware.com> - December 10, 2003
114	if ((strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') && (file_exists('/usr/local/bin/cjpeg') || shell_exec('which cjpeg'))) {
115
116		if (gif_outputAsBmp($gif, $lpszFileName.'.bmp', $bgColor)) {
117			exec('cjpeg '.$lpszFileName.'.bmp >'.$lpszFileName.' 2>/dev/null');
118			@unlink($lpszFileName.'.bmp');
119
120			if (@file_exists($lpszFileName)) {
121				if (@filesize($lpszFileName) > 0) {
122					return true;
123				}
124
125				@unlink($lpszFileName);
126			}
127		}
128
129	} else {
130
131		// either Windows, or cjpeg not found in path
132		if ($img = @imagecreatefromstring($gif->getPng($bgColor))) {
133			if (@imagejpeg($img, $lpszFileName)) {
134				return true;
135			}
136		}
137
138	}
139
140	return false;
141}
142
143///////////////////////////////////////////////////////////////////////////////////////////////////
144
145function gif_getSize($gif, &$width, &$height)
146{
147	if (isSet($gif) && (@get_class($gif) == 'cgif') && $gif->loaded()) {
148		$width  = $gif->width();
149		$height = $gif->height();
150	} elseif (@file_exists($gif)) {
151		$myGIF = new CGIF();
152		if (!$myGIF->getSize($gif, $width, $height)) {
153			return false;
154		}
155	} else {
156		return false;
157	}
158
159	return true;
160}
161
162///////////////////////////////////////////////////////////////////////////////////////////////////
163
164class CGIFLZW
165{
166	public $MAX_LZW_BITS;
167	public $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
168	public $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
169
170	///////////////////////////////////////////////////////////////////////////
171
172	// CONSTRUCTOR
173	public function __construct()
174	{
175		$this->MAX_LZW_BITS = 12;
176		unSet($this->Next);
177		unSet($this->Vals);
178		unSet($this->Stack);
179		unSet($this->Buf);
180
181		$this->Next  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
182		$this->Vals  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
183		$this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
184		$this->Buf   = range(0, 279);
185	}
186
187	///////////////////////////////////////////////////////////////////////////
188
189	public function deCompress($data, &$datLen)
190	{
191		$stLen  = strlen($data);
192		$datLen = 0;
193		$ret    = '';
194
195		// INITIALIZATION
196		$this->LZWCommand($data, true);
197
198		while (($iIndex = $this->LZWCommand($data, false)) >= 0) {
199			$ret .= chr($iIndex);
200		}
201
202		$datLen = $stLen - strlen($data);
203
204		if ($iIndex != -2) {
205			return false;
206		}
207
208		return $ret;
209	}
210
211	///////////////////////////////////////////////////////////////////////////
212
213	public function LZWCommand(&$data, $bInit)
214	{
215		if ($bInit) {
216			$this->SetCodeSize = ord($data[0]);
217			$data = substr($data, 1);
218
219			$this->CodeSize    = $this->SetCodeSize + 1;
220			$this->ClearCode   = 1 << $this->SetCodeSize;
221			$this->EndCode     = $this->ClearCode + 1;
222			$this->MaxCode     = $this->ClearCode + 2;
223			$this->MaxCodeSize = $this->ClearCode << 1;
224
225			$this->GetCode($data, $bInit);
226
227			$this->Fresh = 1;
228			for ($i = 0; $i < $this->ClearCode; $i++) {
229				$this->Next[$i] = 0;
230				$this->Vals[$i] = $i;
231			}
232
233			for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
234				$this->Next[$i] = 0;
235				$this->Vals[$i] = 0;
236			}
237
238			$this->sp = 0;
239			return 1;
240		}
241
242		if ($this->Fresh) {
243			$this->Fresh = 0;
244			do {
245				$this->FirstCode = $this->GetCode($data, $bInit);
246				$this->OldCode   = $this->FirstCode;
247			}
248			while ($this->FirstCode == $this->ClearCode);
249
250			return $this->FirstCode;
251		}
252
253		if ($this->sp > 0) {
254			$this->sp--;
255			return $this->Stack[$this->sp];
256		}
257
258		while (($Code = $this->GetCode($data, $bInit)) >= 0) {
259			if ($Code == $this->ClearCode) {
260				for ($i = 0; $i < $this->ClearCode; $i++) {
261					$this->Next[$i] = 0;
262					$this->Vals[$i] = $i;
263				}
264
265				for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
266					$this->Next[$i] = 0;
267					$this->Vals[$i] = 0;
268				}
269
270				$this->CodeSize    = $this->SetCodeSize + 1;
271				$this->MaxCodeSize = $this->ClearCode << 1;
272				$this->MaxCode     = $this->ClearCode + 2;
273				$this->sp          = 0;
274				$this->FirstCode   = $this->GetCode($data, $bInit);
275				$this->OldCode     = $this->FirstCode;
276
277				return $this->FirstCode;
278			}
279
280			if ($Code == $this->EndCode) {
281				return -2;
282			}
283
284			$InCode = $Code;
285			if ($Code >= $this->MaxCode) {
286				$this->Stack[$this->sp] = $this->FirstCode;
287				$this->sp++;
288				$Code = $this->OldCode;
289			}
290
291			while ($Code >= $this->ClearCode) {
292				$this->Stack[$this->sp] = $this->Vals[$Code];
293				$this->sp++;
294
295				if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
296					return -1;
297
298				$Code = $this->Next[$Code];
299			}
300
301			$this->FirstCode = $this->Vals[$Code];
302			$this->Stack[$this->sp] = $this->FirstCode;
303			$this->sp++;
304
305			if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
306				$this->Next[$Code] = $this->OldCode;
307				$this->Vals[$Code] = $this->FirstCode;
308				$this->MaxCode++;
309
310				if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
311					$this->MaxCodeSize *= 2;
312					$this->CodeSize++;
313				}
314			}
315
316			$this->OldCode = $InCode;
317			if ($this->sp > 0) {
318				$this->sp--;
319				return $this->Stack[$this->sp];
320			}
321		}
322
323		return $Code;
324	}
325
326	///////////////////////////////////////////////////////////////////////////
327
328	public function GetCode(&$data, $bInit)
329	{
330		if ($bInit) {
331			$this->CurBit   = 0;
332			$this->LastBit  = 0;
333			$this->Done     = 0;
334			$this->LastByte = 2;
335			return 1;
336		}
337
338		if (($this->CurBit + $this->CodeSize) >= $this->LastBit) {
339			if ($this->Done) {
340				if ($this->CurBit >= $this->LastBit) {
341					// Ran off the end of my bits
342					return 0;
343				}
344				return -1;
345			}
346
347			$this->Buf[0] = $this->Buf[$this->LastByte - 2];
348			$this->Buf[1] = $this->Buf[$this->LastByte - 1];
349
350			$Count = ord($data[0]);
351			$data  = substr($data, 1);
352
353			if ($Count) {
354				for ($i = 0; $i < $Count; $i++) {
355					$this->Buf[2 + $i] = ord($data[$i]);
356				}
357				$data = substr($data, $Count);
358			} else {
359				$this->Done = 1;
360			}
361
362			$this->LastByte = 2 + $Count;
363			$this->CurBit   = ($this->CurBit - $this->LastBit) + 16;
364			$this->LastBit  = (2 + $Count) << 3;
365		}
366
367		$iRet = 0;
368		for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
369			$iRet |= (($this->Buf[ (int) ($i / 8) ] & (1 << ($i % 8))) != 0) << $j;
370		}
371
372		$this->CurBit += $this->CodeSize;
373		return $iRet;
374	}
375}
376
377///////////////////////////////////////////////////////////////////////////////////////////////////
378
379class CGIFCOLORTABLE
380{
381	public $m_nColors;
382	public $m_arColors;
383
384	///////////////////////////////////////////////////////////////////////////
385
386	// CONSTRUCTOR
387	public function __construct()
388	{
389		unSet($this->m_nColors);
390		unSet($this->m_arColors);
391	}
392
393	///////////////////////////////////////////////////////////////////////////
394
395	public function load($lpData, $num)
396	{
397		$this->m_nColors  = 0;
398		$this->m_arColors = array();
399
400		for ($i = 0; $i < $num; $i++) {
401			$rgb = substr($lpData, $i * 3, 3);
402			if (strlen($rgb) < 3) {
403				return false;
404			}
405
406			$this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]);
407			$this->m_nColors++;
408		}
409
410		return true;
411	}
412
413	///////////////////////////////////////////////////////////////////////////
414
415	public function toString()
416	{
417		$ret = '';
418
419		for ($i = 0; $i < $this->m_nColors; $i++) {
420			$ret .=
421				chr($this->m_arColors[ $i] & 0x000000FF)       . // R
422				chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
423				chr(($this->m_arColors[$i] & 0x00FF0000) >> 16);  // B
424		}
425
426		return $ret;
427	}
428
429	///////////////////////////////////////////////////////////////////////////
430
431	public function toRGBQuad()
432	{
433		$ret = '';
434
435		for ($i = 0; $i < $this->m_nColors; $i++) {
436			$ret .=
437				chr(($this->m_arColors[$i] & 0x00FF0000) >> 16) . // B
438				chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
439				chr($this->m_arColors[ $i] & 0x000000FF)       . // R
440				"\x00";
441		}
442
443		return $ret;
444	}
445
446	///////////////////////////////////////////////////////////////////////////
447
448	public function colorIndex($rgb)
449	{
450		$rgb = (int) $rgb & 0xFFFFFF;
451		$r1  = ($rgb & 0x0000FF);
452		$g1  = ($rgb & 0x00FF00) >>  8;
453		$b1  = ($rgb & 0xFF0000) >> 16;
454		$idx = -1;
455		$dif = 0;
456
457		for ($i = 0; $i < $this->m_nColors; $i++) {
458			$r2 = ($this->m_arColors[$i] & 0x000000FF);
459			$g2 = ($this->m_arColors[$i] & 0x0000FF00) >>  8;
460			$b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
461			$d  = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
462
463			if (($idx == -1) || ($d < $dif)) {
464				$idx = $i;
465				$dif = $d;
466			}
467		}
468
469		return $idx;
470	}
471}
472
473///////////////////////////////////////////////////////////////////////////////////////////////////
474
475class CGIFFILEHEADER
476{
477	public $m_lpVer;
478	public $m_nWidth;
479	public $m_nHeight;
480	public $m_bGlobalClr;
481	public $m_nColorRes;
482	public $m_bSorted;
483	public $m_nTableSize;
484	public $m_nBgColor;
485	public $m_nPixelRatio;
486	public $m_colorTable;
487
488	///////////////////////////////////////////////////////////////////////////
489
490	// CONSTRUCTOR
491	public function __construct()
492	{
493		unSet($this->m_lpVer);
494		unSet($this->m_nWidth);
495		unSet($this->m_nHeight);
496		unSet($this->m_bGlobalClr);
497		unSet($this->m_nColorRes);
498		unSet($this->m_bSorted);
499		unSet($this->m_nTableSize);
500		unSet($this->m_nBgColor);
501		unSet($this->m_nPixelRatio);
502		unSet($this->m_colorTable);
503	}
504
505	///////////////////////////////////////////////////////////////////////////
506
507	public function load($lpData, &$hdrLen)
508	{
509		$hdrLen = 0;
510
511		$this->m_lpVer = substr($lpData, 0, 6);
512		if (($this->m_lpVer <> 'GIF87a') && ($this->m_lpVer <> 'GIF89a')) {
513			return false;
514		}
515
516		$this->m_nWidth  = $this->w2i(substr($lpData, 6, 2));
517		$this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
518		if (!$this->m_nWidth || !$this->m_nHeight) {
519			return false;
520		}
521
522		$b = ord($lpData[ 10 ]);
523		$this->m_bGlobalClr  = ($b & 0x80) ? true : false;
524		$this->m_nColorRes   = ($b & 0x70) >> 4;
525		$this->m_bSorted     = ($b & 0x08) ? true : false;
526		$this->m_nTableSize  = 2 << ($b & 0x07);
527		$this->m_nBgColor    = ord($lpData[ 11 ]);
528		$this->m_nPixelRatio = ord($lpData[ 12 ]);
529		$hdrLen = 13;
530
531		if ($this->m_bGlobalClr) {
532			$this->m_colorTable = new CGIFCOLORTABLE();
533			if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
534				return false;
535			}
536			$hdrLen += 3 * $this->m_nTableSize;
537		}
538
539		return true;
540	}
541
542	///////////////////////////////////////////////////////////////////////////
543
544	public function w2i($str)
545	{
546		return ord($str[ 0 ]) + (ord($str[ 1 ]) << 8);
547	}
548}
549
550///////////////////////////////////////////////////////////////////////////////////////////////////
551
552class CGIFIMAGEHEADER
553{
554	public $m_nLeft;
555	public $m_nTop;
556	public $m_nWidth;
557	public $m_nHeight;
558	public $m_bLocalClr;
559	public $m_bInterlace;
560	public $m_bSorted;
561	public $m_nTableSize;
562	public $m_colorTable;
563
564	///////////////////////////////////////////////////////////////////////////
565
566	// CONSTRUCTOR
567	public function __construct()
568	{
569		unSet($this->m_nLeft);
570		unSet($this->m_nTop);
571		unSet($this->m_nWidth);
572		unSet($this->m_nHeight);
573		unSet($this->m_bLocalClr);
574		unSet($this->m_bInterlace);
575		unSet($this->m_bSorted);
576		unSet($this->m_nTableSize);
577		unSet($this->m_colorTable);
578	}
579
580	///////////////////////////////////////////////////////////////////////////
581
582	public function load($lpData, &$hdrLen)
583	{
584		$hdrLen = 0;
585
586		$this->m_nLeft   = $this->w2i(substr($lpData, 0, 2));
587		$this->m_nTop    = $this->w2i(substr($lpData, 2, 2));
588		$this->m_nWidth  = $this->w2i(substr($lpData, 4, 2));
589		$this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
590
591		if (!$this->m_nWidth || !$this->m_nHeight) {
592			return false;
593		}
594
595		$b = ord($lpData[8]);
596		$this->m_bLocalClr  = ($b & 0x80) ? true : false;
597		$this->m_bInterlace = ($b & 0x40) ? true : false;
598		$this->m_bSorted    = ($b & 0x20) ? true : false;
599		$this->m_nTableSize = 2 << ($b & 0x07);
600		$hdrLen = 9;
601
602		if ($this->m_bLocalClr) {
603			$this->m_colorTable = new CGIFCOLORTABLE();
604			if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
605				return false;
606			}
607			$hdrLen += 3 * $this->m_nTableSize;
608		}
609
610		return true;
611	}
612
613	///////////////////////////////////////////////////////////////////////////
614
615	public function w2i($str)
616	{
617		return ord($str[ 0 ]) + (ord($str[ 1 ]) << 8);
618	}
619}
620
621///////////////////////////////////////////////////////////////////////////////////////////////////
622
623class CGIFIMAGE
624{
625	public $m_disp;
626	public $m_bUser;
627	public $m_bTrans;
628	public $m_nDelay;
629	public $m_nTrans;
630	public $m_lpComm;
631	public $m_gih;
632	public $m_data;
633	public $m_lzw;
634
635	///////////////////////////////////////////////////////////////////////////
636
637	public function __construct()
638	{
639		unSet($this->m_disp);
640		unSet($this->m_bUser);
641		unSet($this->m_bTrans);
642		unSet($this->m_nDelay);
643		unSet($this->m_nTrans);
644		unSet($this->m_lpComm);
645		unSet($this->m_data);
646		$this->m_gih = new CGIFIMAGEHEADER();
647		$this->m_lzw = new CGIFLZW();
648	}
649
650	///////////////////////////////////////////////////////////////////////////
651
652	public function load($data, &$datLen)
653	{
654		$datLen = 0;
655
656		while (true) {
657			$b = ord($data[0]);
658			$data = substr($data, 1);
659			$datLen++;
660
661			switch($b) {
662			case 0x21: // Extension
663				if (!$this->skipExt($data, $len = 0)) {
664					return false;
665				}
666				$datLen += $len;
667				break;
668
669			case 0x2C: // Image
670				// LOAD HEADER & COLOR TABLE
671				if (!$this->m_gih->load($data, $len = 0)) {
672					return false;
673				}
674				$data = substr($data, $len);
675				$datLen += $len;
676
677				// ALLOC BUFFER
678				if (!($this->m_data = $this->m_lzw->deCompress($data, $len = 0))) {
679					return false;
680				}
681				$data = substr($data, $len);
682				$datLen += $len;
683
684				if ($this->m_gih->m_bInterlace) {
685					$this->deInterlace();
686				}
687				return true;
688
689			case 0x3B: // EOF
690			default:
691				return false;
692			}
693		}
694		return false;
695	}
696
697	///////////////////////////////////////////////////////////////////////////
698
699	public function skipExt(&$data, &$extLen)
700	{
701		$extLen = 0;
702
703		$b = ord($data[0]);
704		$data = substr($data, 1);
705		$extLen++;
706
707		switch($b) {
708		case 0xF9: // Graphic Control
709			$b = ord($data[1]);
710			$this->m_disp   = ($b & 0x1C) >> 2;
711			$this->m_bUser  = ($b & 0x02) ? true : false;
712			$this->m_bTrans = ($b & 0x01) ? true : false;
713			$this->m_nDelay = $this->w2i(substr($data, 2, 2));
714			$this->m_nTrans = ord($data[4]);
715			break;
716
717		case 0xFE: // Comment
718			$this->m_lpComm = substr($data, 1, ord($data[0]));
719			break;
720
721		case 0x01: // Plain text
722			break;
723
724		case 0xFF: // Application
725			break;
726		}
727
728		// SKIP DEFAULT AS DEFS MAY CHANGE
729		$b = ord($data[0]);
730		$data = substr($data, 1);
731		$extLen++;
732		while ($b > 0) {
733			$data = substr($data, $b);
734			$extLen += $b;
735			$b    = ord($data[0]);
736			$data = substr($data, 1);
737			$extLen++;
738		}
739		return true;
740	}
741
742	///////////////////////////////////////////////////////////////////////////
743
744	public function w2i($str)
745	{
746		return ord($str[ 0 ]) + (ord($str[ 1 ]) << 8);
747	}
748
749	///////////////////////////////////////////////////////////////////////////
750
751	public function deInterlace()
752	{
753		$data = $this->m_data;
754		$s = 0;
755		$y = 0;
756
757		for ($i = 0; $i < 4; $i++) {
758			switch($i) {
759			case 0:
760				$s = 8;
761				$y = 0;
762				break;
763
764			case 1:
765				$s = 8;
766				$y = 4;
767				break;
768
769			case 2:
770				$s = 4;
771				$y = 2;
772				break;
773
774			case 3:
775				$s = 2;
776				$y = 1;
777				break;
778			}
779
780			for (; $y < $this->m_gih->m_nHeight; $y += $s) {
781				$lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
782				$this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
783
784				$data =
785					substr($data, 0, $y * $this->m_gih->m_nWidth) .
786					$lne .
787					substr($data, ($y + 1) * $this->m_gih->m_nWidth);
788			}
789		}
790
791		$this->m_data = $data;
792	}
793}
794
795///////////////////////////////////////////////////////////////////////////////////////////////////
796
797class CGIF
798{
799	public $m_gfh;
800	public $m_lpData;
801	public $m_img;
802	public $m_bLoaded;
803
804	///////////////////////////////////////////////////////////////////////////
805
806	// CONSTRUCTOR
807	public function __construct()
808	{
809		$this->m_gfh     = new CGIFFILEHEADER();
810		$this->m_img     = new CGIFIMAGE();
811		$this->m_lpData  = '';
812		$this->m_bLoaded = false;
813	}
814
815	///////////////////////////////////////////////////////////////////////////
816
817	public function loadFile($lpszFileName, $iIndex)
818	{
819		if ($iIndex < 0) {
820			return false;
821		}
822
823		// READ FILE
824		if (!($fh = @fopen($lpszFileName, 'rb'))) {
825			return false;
826		}
827		$this->m_lpData = @fread($fh, @filesize($lpszFileName));
828		fclose($fh);
829
830		// GET FILE HEADER
831		if (!$this->m_gfh->load($this->m_lpData, $len = 0)) {
832			return false;
833		}
834		$this->m_lpData = substr($this->m_lpData, $len);
835
836		do {
837			if (!$this->m_img->load($this->m_lpData, $imgLen = 0)) {
838				return false;
839			}
840			$this->m_lpData = substr($this->m_lpData, $imgLen);
841		}
842		while ($iIndex-- > 0);
843
844		$this->m_bLoaded = true;
845		return true;
846	}
847
848	///////////////////////////////////////////////////////////////////////////
849
850	public function getSize($lpszFileName, &$width, &$height)
851	{
852		if (!($fh = @fopen($lpszFileName, 'rb'))) {
853			return false;
854		}
855		$data = @fread($fh, @filesize($lpszFileName));
856		@fclose($fh);
857
858		$gfh = new CGIFFILEHEADER();
859		if (!$gfh->load($data, $len = 0)) {
860			return false;
861		}
862
863		$width  = $gfh->m_nWidth;
864		$height = $gfh->m_nHeight;
865		return true;
866	}
867
868	///////////////////////////////////////////////////////////////////////////
869
870	public function getBmp($bgColor)
871	{
872		$out = '';
873
874		if (!$this->m_bLoaded) {
875			return false;
876		}
877
878		// PREPARE COLOR TABLE (RGBQUADs)
879		if ($this->m_img->m_gih->m_bLocalClr) {
880			$nColors = $this->m_img->m_gih->m_nTableSize;
881			$rgbq    = $this->m_img->m_gih->m_colorTable->toRGBQuad();
882			if ($bgColor != -1) {
883				$bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
884			}
885		} elseif ($this->m_gfh->m_bGlobalClr) {
886			$nColors = $this->m_gfh->m_nTableSize;
887			$rgbq    = $this->m_gfh->m_colorTable->toRGBQuad();
888			if ($bgColor != -1) {
889				$bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
890			}
891		} else {
892			$nColors =  0;
893			$rgbq    = '';
894			$bgColor = -1;
895		}
896
897		// PREPARE BITMAP BITS
898		$data = $this->m_img->m_data;
899		$nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
900		$bmp  = '';
901		$nPad = ($this->m_gfh->m_nWidth % 4) ? 4 - ($this->m_gfh->m_nWidth % 4) : 0;
902		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
903			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
904				if (
905					($x >= $this->m_img->m_gih->m_nLeft) &&
906					($y >= $this->m_img->m_gih->m_nTop) &&
907					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
908					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
909					// PART OF IMAGE
910					if (@$this->m_img->m_bTrans && (ord($data[$nPxl]) == $this->m_img->m_nTrans)) {
911						// TRANSPARENT -> BACKGROUND
912						if ($bgColor == -1) {
913							$bmp .= chr($this->m_gfh->m_nBgColor);
914						} else {
915							$bmp .= chr($bgColor);
916						}
917					} else {
918						$bmp .= $data[$nPxl];
919					}
920				} else {
921					// BACKGROUND
922					if ($bgColor == -1) {
923						$bmp .= chr($this->m_gfh->m_nBgColor);
924					} else {
925						$bmp .= chr($bgColor);
926					}
927				}
928			}
929			$nPxl -= $this->m_gfh->m_nWidth << 1;
930
931			// ADD PADDING
932			for ($x = 0; $x < $nPad; $x++) {
933				$bmp .= "\x00";
934			}
935		}
936
937		// BITMAPFILEHEADER
938		$out .= 'BM';
939		$out .= $this->dword(14 + 40 + ($nColors << 2) + strlen($bmp));
940		$out .= "\x00\x00";
941		$out .= "\x00\x00";
942		$out .= $this->dword(14 + 40 + ($nColors << 2));
943
944		// BITMAPINFOHEADER
945		$out .= $this->dword(40);
946		$out .= $this->dword($this->m_gfh->m_nWidth);
947		$out .= $this->dword($this->m_gfh->m_nHeight);
948		$out .= "\x01\x00";
949		$out .= "\x08\x00";
950		$out .= "\x00\x00\x00\x00";
951		$out .= "\x00\x00\x00\x00";
952		$out .= "\x12\x0B\x00\x00";
953		$out .= "\x12\x0B\x00\x00";
954		$out .= $this->dword($nColors % 256);
955		$out .= "\x00\x00\x00\x00";
956
957		// COLOR TABLE
958		if ($nColors > 0) {
959			$out .= $rgbq;
960		}
961
962		// DATA
963		$out .= $bmp;
964
965		return $out;
966	}
967
968	///////////////////////////////////////////////////////////////////////////
969
970	public function getPng($bgColor)
971	{
972		$out = '';
973
974		if (!$this->m_bLoaded) {
975			return false;
976		}
977
978		// PREPARE COLOR TABLE (RGBQUADs)
979		if ($this->m_img->m_gih->m_bLocalClr) {
980			$nColors = $this->m_img->m_gih->m_nTableSize;
981			$pal     = $this->m_img->m_gih->m_colorTable->toString();
982			if ($bgColor != -1) {
983				$bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
984			}
985		} elseif ($this->m_gfh->m_bGlobalClr) {
986			$nColors = $this->m_gfh->m_nTableSize;
987			$pal     = $this->m_gfh->m_colorTable->toString();
988			if ($bgColor != -1) {
989				$bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
990			}
991		} else {
992			$nColors =  0;
993			$pal     = '';
994			$bgColor = -1;
995		}
996
997		// PREPARE BITMAP BITS
998		$data = $this->m_img->m_data;
999		$nPxl = 0;
1000		$bmp  = '';
1001		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
1002			$bmp .= "\x00";
1003			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
1004				if (
1005					($x >= $this->m_img->m_gih->m_nLeft) &&
1006					($y >= $this->m_img->m_gih->m_nTop) &&
1007					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
1008					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
1009					// PART OF IMAGE
1010					$bmp .= $data[$nPxl];
1011				} else {
1012					// BACKGROUND
1013					if ($bgColor == -1) {
1014						$bmp .= chr($this->m_gfh->m_nBgColor);
1015					} else {
1016						$bmp .= chr($bgColor);
1017					}
1018				}
1019			}
1020		}
1021		$bmp = gzcompress($bmp, 9);
1022
1023		///////////////////////////////////////////////////////////////////////
1024		// SIGNATURE
1025		$out .= "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
1026		///////////////////////////////////////////////////////////////////////
1027		// HEADER
1028		$out .= "\x00\x00\x00\x0D";
1029		$tmp  = 'IHDR';
1030		$tmp .= $this->ndword($this->m_gfh->m_nWidth);
1031		$tmp .= $this->ndword($this->m_gfh->m_nHeight);
1032		$tmp .= "\x08\x03\x00\x00\x00";
1033		$out .= $tmp;
1034		$out .= $this->ndword(crc32($tmp));
1035		///////////////////////////////////////////////////////////////////////
1036		// PALETTE
1037		if ($nColors > 0) {
1038			$out .= $this->ndword($nColors * 3);
1039			$tmp  = 'PLTE';
1040			$tmp .= $pal;
1041			$out .= $tmp;
1042			$out .= $this->ndword(crc32($tmp));
1043		}
1044		///////////////////////////////////////////////////////////////////////
1045		// TRANSPARENCY
1046		if (@$this->m_img->m_bTrans && ($nColors > 0)) {
1047			$out .= $this->ndword($nColors);
1048			$tmp  = 'tRNS';
1049			for ($i = 0; $i < $nColors; $i++) {
1050				$tmp .= ($i == $this->m_img->m_nTrans) ? "\x00" : "\xFF";
1051			}
1052			$out .= $tmp;
1053			$out .= $this->ndword(crc32($tmp));
1054		}
1055		///////////////////////////////////////////////////////////////////////
1056		// DATA BITS
1057		$out .= $this->ndword(strlen($bmp));
1058		$tmp  = 'IDAT';
1059		$tmp .= $bmp;
1060		$out .= $tmp;
1061		$out .= $this->ndword(crc32($tmp));
1062		///////////////////////////////////////////////////////////////////////
1063		// END OF FILE
1064		$out .= "\x00\x00\x00\x00IEND\xAE\x42\x60\x82";
1065
1066		return $out;
1067	}
1068
1069	///////////////////////////////////////////////////////////////////////////
1070
1071	// Added by James Heinrich <info@silisoftware.com> - January 5, 2003
1072
1073	// Takes raw image data and plots it pixel-by-pixel on a new GD image and returns that
1074	// It's extremely slow, but the only solution when imagecreatefromstring() fails
1075	public function getGD_PixelPlotterVersion()
1076	{
1077		if (!$this->m_bLoaded) {
1078			return false;
1079		}
1080
1081		// PREPARE COLOR TABLE (RGBQUADs)
1082		if ($this->m_img->m_gih->m_bLocalClr) {
1083			$pal = $this->m_img->m_gih->m_colorTable->toString();
1084		} elseif ($this->m_gfh->m_bGlobalClr) {
1085			$pal = $this->m_gfh->m_colorTable->toString();
1086		} else {
1087			die('No color table available in getGD_PixelPlotterVersion()');
1088		}
1089
1090		$PlottingIMG = imagecreate($this->m_gfh->m_nWidth, $this->m_gfh->m_nHeight);
1091		$NumColorsInPal = floor(strlen($pal) / 3);
1092		$ThisImageColor = array();
1093		for ($i = 0; $i < $NumColorsInPal; $i++) {
1094			$ThisImageColor[$i] = imagecolorallocate(
1095									$PlottingIMG,
1096									ord($pal[($i * 3) + 0]),
1097									ord($pal[($i * 3) + 1]),
1098									ord($pal[($i * 3) + 2]));
1099		}
1100
1101		// PREPARE BITMAP BITS
1102		$data = $this->m_img->m_data;
1103		$nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
1104		for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
1105			if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) {
1106				set_time_limit(30);
1107			}
1108			for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
1109				if (
1110					($x >= $this->m_img->m_gih->m_nLeft) &&
1111					($y >= $this->m_img->m_gih->m_nTop) &&
1112					($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
1113					($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
1114					// PART OF IMAGE
1115					if (@$this->m_img->m_bTrans && (ord($data[$nPxl]) == $this->m_img->m_nTrans)) {
1116						imagesetpixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]);
1117					} else {
1118						imagesetpixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[ord($data[$nPxl])]);
1119					}
1120				} else {
1121					// BACKGROUND
1122					imagesetpixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]);
1123				}
1124			}
1125			$nPxl -= $this->m_gfh->m_nWidth << 1;
1126
1127		}
1128
1129		return $PlottingIMG;
1130	}
1131
1132	///////////////////////////////////////////////////////////////////////////
1133
1134	public function dword($val)
1135	{
1136		$val = (int) $val;
1137		return chr($val & 0xFF).chr(($val & 0xFF00) >> 8).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF000000) >> 24);
1138	}
1139
1140	///////////////////////////////////////////////////////////////////////////
1141
1142	public function ndword($val)
1143	{
1144		$val = (int) $val;
1145		return chr(($val & 0xFF000000) >> 24).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF00) >> 8).chr($val & 0xFF);
1146	}
1147
1148	///////////////////////////////////////////////////////////////////////////
1149
1150	public function width()
1151	{
1152		return $this->m_gfh->m_nWidth;
1153	}
1154
1155	///////////////////////////////////////////////////////////////////////////
1156
1157	public function height()
1158	{
1159		return $this->m_gfh->m_nHeight;
1160	}
1161
1162	///////////////////////////////////////////////////////////////////////////
1163
1164	public function comment()
1165	{
1166		return $this->m_img->m_lpComm;
1167	}
1168
1169	///////////////////////////////////////////////////////////////////////////
1170
1171	public function loaded()
1172	{
1173		return $this->m_bLoaded;
1174	}
1175}
1176