3 // GIF Util - (C) 2003 Yamasoft (S/C)
4 // http://www.yamasoft.com
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.
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]) - Requires cjpeg
15 function gif_loadFile($lpszFileName, $iIndex = 0)
19 if (!$gif->loadFile($lpszFileName, $iIndex)) {
26 function gif_outputAsBmp($gif, $lpszFileName, $bgColor = -1)
28 if (!isSet($gif) || (@get_class($gif) <> "cgif") || !$gif->loaded() || ($lpszFileName == "")) {
32 $fd = $gif->getBmp($bgColor);
33 if (strlen($fd) <= 0) {
37 if (!($fh = @fOpen($lpszFileName, "wb"))) {
40 @fWrite($fh, $fd, strlen($fd));
46 function gif_outputAsPng($gif, $lpszFileName, $bgColor = -1)
48 if (!isSet($gif) || (@get_class($gif) <> "cgif") || !$gif->loaded() || ($lpszFileName == "")) {
52 $fd = $gif->getPng($bgColor);
53 if (strlen($fd) <= 0) {
57 if (!($fh = @fOpen($lpszFileName, "wb"))) {
60 @fWrite($fh, $fd, strlen($fd));
66 function gif_outputAsJpeg($gif, $lpszFileName, $bgColor = -1)
68 if (gif_outputAsBmp($gif, "$lpszFileName.bmp", $gbColor)) {
69 exec("cjpeg $lpszFileName.bmp >$lpszFileName 2>/dev/null");
70 @unLink("$lpszFileName.bmp");
72 if (@file_exists($lpszFileName)) {
73 if (@fileSize($lpszFileName) > 0) {
77 @unLink($lpszFileName);
84 function gif_getSize($gif, &$width, &$height)
86 if (isSet($gif) && (@get_class($gif) == "cgif") && $gif->loaded()) {
87 $width = $gif->width();
88 $height = $gif->height();
89 } elseif (@file_exists($gif)) {
91 if (!$myGIF->getSize($gif, $width, $height)) {
104 var $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
105 var $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
110 $this->MAX_LZW_BITS = 12;
116 $this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1);
117 $this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1);
118 $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
119 $this->Buf = range(0, 279);
122 function deCompress($data, &$datLen)
124 $stLen = strlen($data);
129 $this->LZWCommand($data, true);
131 while (($iIndex = $this->LZWCommand($data, false)) >= 0) {
132 $ret .= chr($iIndex);
135 $datLen = $stLen - strlen($data);
144 function LZWCommand(&$data, $bInit)
147 $this->SetCodeSize = ord($data{0});
148 $data = substr($data, 1);
150 $this->CodeSize = $this->SetCodeSize + 1;
151 $this->ClearCode = 1 << $this->SetCodeSize;
152 $this->EndCode = $this->ClearCode + 1;
153 $this->MaxCode = $this->ClearCode + 2;
154 $this->MaxCodeSize = $this->ClearCode << 1;
156 $this->GetCode($data, $bInit);
159 for ($i = 0; $i < $this->ClearCode; $i++) {
161 $this->Vals[$i] = $i;
164 for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
176 $this->FirstCode = $this->GetCode($data, $bInit);
177 $this->OldCode = $this->FirstCode;
178 } while ($this->FirstCode == $this->ClearCode);
180 return $this->FirstCode;
185 return $this->Stack[$this->sp];
188 while (($Code = $this->GetCode($data, $bInit)) >= 0) {
189 if ($Code == $this->ClearCode) {
190 for ($i = 0; $i < $this->ClearCode; $i++) {
192 $this->Vals[$i] = $i;
195 for (; $i < (1 << $this->MAX_LZW_BITS); $i++) {
200 $this->CodeSize = $this->SetCodeSize + 1;
201 $this->MaxCodeSize = $this->ClearCode << 1;
202 $this->MaxCode = $this->ClearCode + 2;
204 $this->FirstCode = $this->GetCode($data, $bInit);
205 $this->OldCode = $this->FirstCode;
207 return $this->FirstCode;
210 if ($Code == $this->EndCode) {
215 if ($Code >= $this->MaxCode) {
216 $this->Stack[$this->sp] = $this->FirstCode;
218 $Code = $this->OldCode;
221 while ($Code >= $this->ClearCode) {
222 $this->Stack[$this->sp] = $this->Vals[$Code];
225 if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
228 $Code = $this->Next[$Code];
231 $this->FirstCode = $this->Vals[$Code];
232 $this->Stack[$this->sp] = $this->FirstCode;
235 if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
236 $this->Next[$Code] = $this->OldCode;
237 $this->Vals[$Code] = $this->FirstCode;
240 if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
241 $this->MaxCodeSize *= 2;
246 $this->OldCode = $InCode;
249 return $this->Stack[$this->sp];
256 function GetCode(&$data, $bInit)
266 if (($this->CurBit + $this->CodeSize) >= $this->LastBit) {
268 if ($this->CurBit >= $this->LastBit) {
269 // Ran off the end of my bits
275 $this->Buf[0] = $this->Buf[$this->LastByte - 2];
276 $this->Buf[1] = $this->Buf[$this->LastByte - 1];
278 $Count = ord($data{0});
279 $data = substr($data, 1);
282 for ($i = 0; $i < $Count; $i++) {
283 $this->Buf[2 + $i] = ord($data{$i});
285 $data = substr($data, $Count);
290 $this->LastByte = 2 + $Count;
291 $this->CurBit = ($this->CurBit - $this->LastBit) + 16;
292 $this->LastBit = (2 + $Count) << 3;
296 for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
297 $iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
300 $this->CurBit += $this->CodeSize;
311 function CGIFCOLORTABLE()
313 unSet($this->m_nColors);
314 unSet($this->m_arColors);
317 function load($lpData, $num)
319 $this->m_nColors = 0;
320 $this->m_arColors = array();
322 for ($i = 0; $i < $num; $i++) {
323 $rgb = substr($lpData, $i * 3, 3);
324 if (strlen($rgb) < 3) {
328 $this->m_arColors[] = (ord($rgb{2}) << 16) + (ord($rgb{1}) << 8) + ord($rgb{0});
339 for ($i = 0; $i < $this->m_nColors; $i++) {
341 chr(($this->m_arColors[$i] & 0x000000FF)) . // R
342 chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G
343 chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B
353 for ($i = 0; $i < $this->m_nColors; $i++) {
355 chr(($this->m_arColors[$i] & 0x00FF0000) >> 16) . // B
356 chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G
357 chr(($this->m_arColors[$i] & 0x000000FF)) . // R
364 function colorIndex($rgb)
366 $rgb = intval($rgb) & 0xFFFFFF;
367 $r1 = ($rgb & 0x0000FF);
368 $g1 = ($rgb & 0x00FF00) >> 8;
369 $b1 = ($rgb & 0xFF0000) >> 16;
372 for ($i = 0; $i < $this->m_nColors; $i++) {
373 $r2 = ($this->m_arColors[$i] & 0x000000FF);
374 $g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8;
375 $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
376 $d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
378 if (($idx == -1) || ($d < $dif)) {
402 function CGIFFILEHEADER()
404 unSet($this->m_lpVer);
405 unSet($this->m_nWidth);
406 unSet($this->m_nHeight);
407 unSet($this->m_bGlobalClr);
408 unSet($this->m_nColorRes);
409 unSet($this->m_bSorted);
410 unSet($this->m_nTableSize);
411 unSet($this->m_nBgColor);
412 unSet($this->m_nPixelRatio);
413 unSet($this->m_colorTable);
416 function load($lpData, &$hdrLen)
420 $this->m_lpVer = substr($lpData, 0, 6);
421 if (($this->m_lpVer <> "GIF87a") && ($this->m_lpVer <> "GIF89a")) {
425 $this->m_nWidth = $this->w2i(substr($lpData, 6, 2));
426 $this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
427 if (!$this->m_nWidth || !$this->m_nHeight) {
431 $b = ord(substr($lpData, 10, 1));
432 $this->m_bGlobalClr = ($b & 0x80) ? true : false;
433 $this->m_nColorRes = ($b & 0x70) >> 4;
434 $this->m_bSorted = ($b & 0x08) ? true : false;
435 $this->m_nTableSize = 2 << ($b & 0x07);
436 $this->m_nBgColor = ord(substr($lpData, 11, 1));
437 $this->m_nPixelRatio = ord(substr($lpData, 12, 1));
440 if ($this->m_bGlobalClr) {
441 $this->m_colorTable = new CGIFCOLORTABLE();
442 if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
445 $hdrLen += 3 * $this->m_nTableSize;
453 return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
457 class CGIFIMAGEHEADER
470 function CGIFIMAGEHEADER()
472 unSet($this->m_nLeft);
473 unSet($this->m_nTop);
474 unSet($this->m_nWidth);
475 unSet($this->m_nHeight);
476 unSet($this->m_bLocalClr);
477 unSet($this->m_bInterlace);
478 unSet($this->m_bSorted);
479 unSet($this->m_nTableSize);
480 unSet($this->m_colorTable);
483 function load($lpData, &$hdrLen)
487 $this->m_nLeft = $this->w2i(substr($lpData, 0, 2));
488 $this->m_nTop = $this->w2i(substr($lpData, 2, 2));
489 $this->m_nWidth = $this->w2i(substr($lpData, 4, 2));
490 $this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
492 if (!$this->m_nWidth || !$this->m_nHeight) {
496 $b = ord($lpData{8});
497 $this->m_bLocalClr = ($b & 0x80) ? true : false;
498 $this->m_bInterlace = ($b & 0x40) ? true : false;
499 $this->m_bSorted = ($b & 0x20) ? true : false;
500 $this->m_nTableSize = 2 << ($b & 0x07);
503 if ($this->m_bLocalClr) {
504 $this->m_colorTable = new CGIFCOLORTABLE();
505 if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
508 $hdrLen += 3 * $this->m_nTableSize;
516 return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
534 unSet($this->m_disp);
535 unSet($this->m_bUser);
536 unSet($this->m_bTrans);
537 unSet($this->m_nDelay);
538 unSet($this->m_nTrans);
539 unSet($this->m_lpComm);
540 unSet($this->m_data);
541 $this->m_gih = new CGIFIMAGEHEADER();
542 $this->m_lzw = new CGIFLZW();
545 function load($data, &$datLen)
551 $data = substr($data, 1);
555 case 0x21: // Extension
556 if (!$this->skipExt($data, $len = 0)) {
563 // LOAD HEADER & COLOR TABLE
564 if (!$this->m_gih->load($data, $len = 0)) {
567 $data = substr($data, $len);
571 if (!($this->m_data = $this->m_lzw->deCompress($data, $len = 0))) {
574 $data = substr($data, $len);
577 if ($this->m_gih->m_bInterlace) {
578 $this->deInterlace();
590 function skipExt(&$data, &$extLen)
595 $data = substr($data, 1);
599 case 0xF9: // Graphic Control
601 $this->m_disp = ($b & 0x1C) >> 2;
602 $this->m_bUser = ($b & 0x02) ? true : false;
603 $this->m_bTrans = ($b & 0x01) ? true : false;
604 $this->m_nDelay = $this->w2i(substr($data, 2, 2));
605 $this->m_nTrans = ord($data{4});
608 case 0xFE: // Comment
609 $this->m_lpComm = substr($data, 1, ord($data{0}));
612 case 0x01: // Plain text
615 case 0xFF: // Application
619 // SKIP DEFAULT AS DEFS MAY CHANGE
621 $data = substr($data, 1);
624 $data = substr($data, $b);
627 $data = substr($data, 1);
635 return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
638 function deInterlace()
640 $data = $this->m_data;
642 for ($i = 0; $i < 4; $i++) {
665 for (; $y < $this->m_gih->m_nHeight; $y += $s) {
666 $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
667 $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
670 substr($data, 0, $y * $this->m_gih->m_nWidth) .
672 substr($data, ($y + 1) * $this->m_gih->m_nWidth);
676 $this->m_data = $data;
690 $this->m_gfh = new CGIFFILEHEADER();
691 $this->m_img = new CGIFIMAGE();
692 $this->m_lpData = "";
693 $this->m_bLoaded = false;
696 function loadFile($lpszFileName, $iIndex)
703 if (!($fh = @fOpen($lpszFileName, "rb"))) {
706 $data = @fRead($fh, @fileSize($lpszFileName));
708 // $data=fread($fh,filesize($lpszFileName));
710 $data = $data . @fread($fh, 1024);
712 $this->m_lpData = @fRead($fh, @fileSize($lpszFileName));
716 if (!$this->m_gfh->load($this->m_lpData, $len = 0)) {
719 $this->m_lpData = substr($this->m_lpData, $len);
722 if (!$this->m_img->load($this->m_lpData, $imgLen = 0)) {
725 $this->m_lpData = substr($this->m_lpData, $imgLen);
726 } while ($iIndex-- > 0);
728 $this->m_bLoaded = true;
732 function getSize($lpszFileName, &$width, &$height)
734 if (!($fh = @fOpen($lpszFileName, "rb"))) {
737 $data = @fRead($fh, @fileSize($lpszFileName));
741 $gfh = new CGIFFILEHEADER();
742 if (!$gfh->load($data, $len = 0)) {
746 $width = $gfh->m_nWidth;
747 $height = $gfh->m_nHeight;
751 function getBmp($bgColor)
755 if (!$this->m_bLoaded) {
759 // PREPARE COLOR TABLE (RGBQUADs)
760 if ($this->m_img->m_gih->m_bLocalClr) {
761 $nColors = $this->m_img->m_gih->m_nTableSize;
762 $rgbq = $this->m_img->m_gih->m_colorTable->toRGBQuad();
763 if ($bgColor != -1) {
764 $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
766 } elseif ($this->m_gfh->m_bGlobalClr) {
767 $nColors = $this->m_gfh->m_nTableSize;
768 $rgbq = $this->m_gfh->m_colorTable->toRGBQuad();
769 if ($bgColor != -1) {
770 $bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
777 // PREPARE BITMAP BITS
778 $data = $this->m_img->m_data;
779 $nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
782 $nPad = ($this->m_gfh->m_nWidth % 4) ? 4 - ($this->m_gfh->m_nWidth % 4) : 0;
783 for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
784 for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
786 ($x >= $this->m_img->m_gih->m_nLeft) &&
787 ($y >= $this->m_img->m_gih->m_nTop) &&
788 ($x < ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
789 ($y < ($this->m_img->m_gih->m_nTop + $this->m_img->m_gih->m_nHeight))
792 if ($this->m_img->m_bTrans && (ord($data{$nPxl}) == $this->m_img->m_nTrans)) {
793 // TRANSPARENT -> BACKGROUND
794 if ($bgColor == -1) {
795 $bmp .= chr($this->m_gfh->m_nBgColor);
797 $bmp .= chr($bgColor);
800 $bmp .= $data{$nPxl};
804 if ($bgColor == -1) {
805 $bmp .= chr($this->m_gfh->m_nBgColor);
807 $bmp .= chr($bgColor);
811 $nPxl -= $this->m_gfh->m_nWidth << 1;
814 for ($x = 0; $x < $nPad; $x++) {
821 $out .= $this->dword(14 + 40 + ($nColors << 2) + strlen($bmp));
824 $out .= $this->dword(14 + 40 + ($nColors << 2));
827 $out .= $this->dword(40);
828 $out .= $this->dword($this->m_gfh->m_nWidth);
829 $out .= $this->dword($this->m_gfh->m_nHeight);
832 $out .= "\x00\x00\x00\x00";
833 $out .= "\x00\x00\x00\x00";
834 $out .= "\x12\x0B\x00\x00";
835 $out .= "\x12\x0B\x00\x00";
836 $out .= $this->dword($nColors % 256);
837 $out .= "\x00\x00\x00\x00";
850 function getPng($bgColor)
854 if (!$this->m_bLoaded) {
858 // PREPARE COLOR TABLE (RGBQUADs)
859 if ($this->m_img->m_gih->m_bLocalClr) {
860 $nColors = $this->m_img->m_gih->m_nTableSize;
861 $pal = $this->m_img->m_gih->m_colorTable->toString();
862 if ($bgColor != -1) {
863 $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
865 } elseif ($this->m_gfh->m_bGlobalClr) {
866 $nColors = $this->m_gfh->m_nTableSize;
867 $pal = $this->m_gfh->m_colorTable->toString();
868 if ($bgColor != -1) {
869 $bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
876 // PREPARE BITMAP BITS
877 $data = $this->m_img->m_data;
880 for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
882 for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
884 ($x >= $this->m_img->m_gih->m_nLeft) &&
885 ($y >= $this->m_img->m_gih->m_nTop) &&
886 ($x < ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
887 ($y < ($this->m_img->m_gih->m_nTop + $this->m_img->m_gih->m_nHeight))
890 $bmp .= $data{$nPxl};
893 if ($bgColor == -1) {
894 $bmp .= chr($this->m_gfh->m_nBgColor);
896 $bmp .= chr($bgColor);
901 $bmp = gzcompress($bmp, 9);
904 $out .= "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
907 $out .= "\x00\x00\x00\x0D";
909 $tmp .= $this->ndword($this->m_gfh->m_nWidth);
910 $tmp .= $this->ndword($this->m_gfh->m_nHeight);
911 $tmp .= "\x08\x03\x00\x00\x00";
913 $out .= $this->ndword(crc32($tmp));
917 $out .= $this->ndword($nColors * 3);
921 $out .= $this->ndword(crc32($tmp));
925 if ($this->m_img->m_bTrans && ($nColors > 0)) {
926 $out .= $this->ndword($nColors);
928 for ($i = 0; $i < $nColors; $i++) {
929 $tmp .= ($i == $this->m_img->m_nTrans) ? "\x00" : "\xFF";
932 $out .= $this->ndword(crc32($tmp));
936 $out .= $this->ndword(strlen($bmp));
940 $out .= $this->ndword(crc32($tmp));
943 $out .= "\x00\x00\x00\x00IEND\xAE\x42\x60\x82";
951 return chr($val & 0xFF) . chr(($val & 0xFF00) >> 8) . chr(($val & 0xFF0000) >> 16) . chr(($val & 0xFF000000) >> 24);
954 function ndword($val)
957 return chr(($val & 0xFF000000) >> 24) . chr(($val & 0xFF0000) >> 16) . chr(($val & 0xFF00) >> 8) . chr($val & 0xFF);
962 return $this->m_gfh->m_nWidth;
967 return $this->m_gfh->m_nHeight;
972 return $this->m_img->m_lpComm;
977 return $this->m_bLoaded;
985 // c-hanging-comment-ender-p: nil
986 // indent-tabs-mode: nil