]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/gif.php
No tabs
[SourceForge/phpwiki.git] / lib / gif.php
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]) - Requires cjpeg
14
15
16
17 function gif_loadFile($lpszFileName, $iIndex = 0)
18 {
19     $gif = new CGIF();
20
21     if(!$gif->loadFile($lpszFileName, $iIndex)) {
22         return false;
23     }
24
25     return $gif;
26 }
27
28
29
30 function gif_outputAsBmp($gif, $lpszFileName, $bgColor = -1)
31 {
32     if(!isSet($gif) || (@get_class($gif) <> "cgif") || !$gif->loaded() || ($lpszFileName == "")) {
33         return false;
34     }
35
36     $fd = $gif->getBmp($bgColor);
37     if(strlen($fd) <= 0) {
38         return false;
39     }
40
41     if(!($fh = @fOpen($lpszFileName, "wb"))) {
42         return false;
43     }
44     @fWrite($fh, $fd, strlen($fd));
45     @fFlush($fh);
46     @fClose($fh);
47     return true;
48 }
49
50
51
52 function gif_outputAsPng($gif, $lpszFileName, $bgColor = -1)
53 {
54     if(!isSet($gif) || (@get_class($gif) <> "cgif") || !$gif->loaded() || ($lpszFileName == "")) {
55         return false;
56     }
57
58     $fd = $gif->getPng($bgColor);
59     if(strlen($fd) <= 0) {
60         return false;
61     }
62
63     if(!($fh = @fOpen($lpszFileName, "wb"))) {
64         return false;
65     }
66     @fWrite($fh, $fd, strlen($fd));
67     @fFlush($fh);
68     @fClose($fh);
69     return true;
70 }
71
72
73
74 function gif_outputAsJpeg($gif, $lpszFileName, $bgColor = -1)
75 {
76     if(gif_outputAsBmp($gif, "$lpszFileName.bmp", $gbColor)) {
77         exec("cjpeg $lpszFileName.bmp >$lpszFileName 2>/dev/null");
78         @unLink("$lpszFileName.bmp");
79
80         if(@file_exists($lpszFileName)) {
81             if(@fileSize($lpszFileName) > 0) {
82                 return true;
83             }
84
85             @unLink($lpszFileName);
86         }
87     }
88
89     return false;
90 }
91
92
93
94 function gif_getSize($gif, &$width, &$height)
95 {
96     if(isSet($gif) && (@get_class($gif) == "cgif") && $gif->loaded()) {
97         $width  = $gif->width();
98         $height = $gif->height();
99     }
100     else if(@file_exists($gif)) {
101         $myGIF = new CGIF();
102         if(!$myGIF->getSize($gif, $width, $height)) {
103             return false;
104         }
105     }
106     else {
107         return false;
108     }
109
110     return true;
111 }
112
113
114
115 class CGIFLZW
116 {
117     var $MAX_LZW_BITS;
118     var $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode;
119     var $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte;
120
121     // CONSTRUCTOR
122     function CGIFLZW() {
123         $this->MAX_LZW_BITS = 12;
124         unSet($this->Next);
125         unSet($this->Vals);
126         unSet($this->Stack);
127         unSet($this->Buf);
128
129         $this->Next  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
130         $this->Vals  = range(0, (1 << $this->MAX_LZW_BITS)       - 1);
131         $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
132         $this->Buf   = range(0, 279);
133     }
134
135     function deCompress($data, &$datLen) {
136         $stLen  = strlen($data);
137         $datLen = 0;
138         $ret    = "";
139
140         // INITIALIZATION
141         $this->LZWCommand($data, true);
142
143         while(($iIndex = $this->LZWCommand($data, false)) >= 0) {
144             $ret .= chr($iIndex);
145         }
146
147         $datLen = $stLen - strlen($data);
148
149         if($iIndex != -2) {
150             return false;
151         }
152
153         return $ret;
154     }
155
156     function LZWCommand(&$data, $bInit) {
157         if($bInit) {
158             $this->SetCodeSize = ord($data{0});
159             $data = substr($data, 1);
160
161             $this->CodeSize    = $this->SetCodeSize + 1;
162             $this->ClearCode   = 1 << $this->SetCodeSize;
163             $this->EndCode     = $this->ClearCode + 1;
164             $this->MaxCode     = $this->ClearCode + 2;
165             $this->MaxCodeSize = $this->ClearCode << 1;
166
167             $this->GetCode($data, $bInit);
168
169             $this->Fresh = 1;
170             for($i = 0; $i < $this->ClearCode; $i++) {
171                 $this->Next[$i] = 0;
172                 $this->Vals[$i] = $i;
173             }
174
175             for(; $i < (1 << $this->MAX_LZW_BITS); $i++) {
176                 $this->Next[$i] = 0;
177                 $this->Vals[$i] = 0;
178             }
179
180             $this->sp = 0;
181             return 1;
182         }
183
184         if($this->Fresh) {
185             $this->Fresh = 0;
186             do {
187                 $this->FirstCode = $this->GetCode($data, $bInit);
188                 $this->OldCode   = $this->FirstCode;
189             }
190             while($this->FirstCode == $this->ClearCode);
191
192             return $this->FirstCode;
193         }
194
195         if($this->sp > 0) {
196             $this->sp--;
197             return $this->Stack[$this->sp];
198         }
199
200         while(($Code = $this->GetCode($data, $bInit)) >= 0) {
201             if($Code == $this->ClearCode) {
202                 for($i = 0; $i < $this->ClearCode; $i++) {
203                     $this->Next[$i] = 0;
204                     $this->Vals[$i] = $i;
205                 }
206
207                 for(; $i < (1 << $this->MAX_LZW_BITS); $i++) {
208                     $this->Next[$i] = 0;
209                     $this->Vals[$i] = 0;
210                 }
211
212                 $this->CodeSize    = $this->SetCodeSize + 1;
213                 $this->MaxCodeSize = $this->ClearCode << 1;
214                 $this->MaxCode     = $this->ClearCode + 2;
215                 $this->sp          = 0;
216                 $this->FirstCode   = $this->GetCode($data, $bInit);
217                 $this->OldCode     = $this->FirstCode;
218
219                 return $this->FirstCode;
220             }
221
222             if($Code == $this->EndCode) {
223                 return -2;
224             }
225
226             $InCode = $Code;
227             if($Code >= $this->MaxCode) {
228                 $this->Stack[$this->sp] = $this->FirstCode;
229                 $this->sp++;
230                 $Code = $this->OldCode;
231             }
232
233             while($Code >= $this->ClearCode) {
234                 $this->Stack[$this->sp] = $this->Vals[$Code];
235                 $this->sp++;
236
237                 if($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
238                     return -1;
239
240                 $Code = $this->Next[$Code];
241             }
242
243             $this->FirstCode = $this->Vals[$Code];
244             $this->Stack[$this->sp] = $this->FirstCode;
245             $this->sp++;
246
247             if(($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) {
248                 $this->Next[$Code] = $this->OldCode;
249                 $this->Vals[$Code] = $this->FirstCode;
250                 $this->MaxCode++;
251
252                 if(($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) {
253                     $this->MaxCodeSize *= 2;
254                     $this->CodeSize++;
255                 }
256             }
257
258             $this->OldCode = $InCode;
259             if($this->sp > 0) {
260                 $this->sp--;
261                 return $this->Stack[$this->sp];
262             }
263         }
264
265         return $Code;
266     }
267
268     function GetCode(&$data, $bInit) {
269         if($bInit) {
270             $this->CurBit   = 0;
271             $this->LastBit  = 0;
272             $this->Done     = 0;
273             $this->LastByte = 2;
274             return 1;
275         }
276
277         if(($this->CurBit + $this->CodeSize) >= $this->LastBit) {
278             if($this->Done) {
279                 if($this->CurBit >= $this->LastBit) {
280                     // Ran off the end of my bits
281                     return 0;
282                 }
283                 return -1;
284             }
285
286             $this->Buf[0] = $this->Buf[$this->LastByte - 2];
287             $this->Buf[1] = $this->Buf[$this->LastByte - 1];
288
289             $Count = ord($data{0});
290             $data  = substr($data, 1);
291
292             if($Count) {
293                 for($i = 0; $i < $Count; $i++) {
294                     $this->Buf[2 + $i] = ord($data{$i});
295                 }
296                 $data = substr($data, $Count);
297             }
298             else {
299                 $this->Done = 1;
300             }
301
302             $this->LastByte = 2 + $Count;
303             $this->CurBit   = ($this->CurBit - $this->LastBit) + 16;
304             $this->LastBit  = (2 + $Count) << 3;
305         }
306
307         $iRet = 0;
308         for($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) {
309             $iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
310         }
311
312         $this->CurBit += $this->CodeSize;
313         return $iRet;
314     }
315 }
316
317 class CGIFCOLORTABLE
318 {
319     var $m_nColors;
320     var $m_arColors;
321
322     // CONSTRUCTOR
323     function CGIFCOLORTABLE() {
324         unSet($this->m_nColors);
325         unSet($this->m_arColors);
326     }
327
328
329     function load($lpData, $num) {
330         $this->m_nColors  = 0;
331         $this->m_arColors = array();
332
333         for($i = 0; $i < $num; $i++) {
334             $rgb = substr($lpData, $i * 3, 3);
335             if(strlen($rgb) < 3) {
336                 return false;
337             }
338
339             $this->m_arColors[] = (ord($rgb{2}) << 16) + (ord($rgb{1}) << 8) + ord($rgb{0});
340             $this->m_nColors++;
341         }
342
343         return true;
344     }
345
346     function toString() {
347         $ret = "";
348
349         for($i = 0; $i < $this->m_nColors; $i++) {
350             $ret .=
351                 chr(($this->m_arColors[$i] & 0x000000FF))       . // R
352                 chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
353                 chr(($this->m_arColors[$i] & 0x00FF0000) >> 16);  // B
354         }
355
356         return $ret;
357     }
358
359     function toRGBQuad() {
360         $ret = "";
361
362         for($i = 0; $i < $this->m_nColors; $i++) {
363             $ret .=
364                 chr(($this->m_arColors[$i] & 0x00FF0000) >> 16) . // B
365                 chr(($this->m_arColors[$i] & 0x0000FF00) >>  8) . // G
366                 chr(($this->m_arColors[$i] & 0x000000FF))       . // R
367                 "\x00";
368         }
369
370         return $ret;
371     }
372
373     function colorIndex($rgb) {
374         $rgb  = intval($rgb) & 0xFFFFFF;
375         $r1   = ($rgb & 0x0000FF);
376         $g1   = ($rgb & 0x00FF00) >>  8;
377         $b1   = ($rgb & 0xFF0000) >> 16;
378         $idx  = -1;
379
380         for($i = 0; $i < $this->m_nColors; $i++) {
381             $r2 = ($this->m_arColors[$i] & 0x000000FF);
382             $g2 = ($this->m_arColors[$i] & 0x0000FF00) >>  8;
383             $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
384             $d  = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
385
386             if(($idx == -1) || ($d < $dif)) {
387                 $idx = $i;
388                 $dif = $d;
389             }
390         }
391
392         return $idx;
393     }
394 }
395
396 class CGIFFILEHEADER
397 {
398     var $m_lpVer;
399     var $m_nWidth;
400     var $m_nHeight;
401     var $m_bGlobalClr;
402     var $m_nColorRes;
403     var $m_bSorted;
404     var $m_nTableSize;
405     var $m_nBgColor;
406     var $m_nPixelRatio;
407     var $m_colorTable;
408
409     // CONSTRUCTOR
410     function CGIFFILEHEADER() {
411         unSet($this->m_lpVer);
412         unSet($this->m_nWidth);
413         unSet($this->m_nHeight);
414         unSet($this->m_bGlobalClr);
415         unSet($this->m_nColorRes);
416         unSet($this->m_bSorted);
417         unSet($this->m_nTableSize);
418         unSet($this->m_nBgColor);
419         unSet($this->m_nPixelRatio);
420         unSet($this->m_colorTable);
421     }
422
423
424     function load($lpData, &$hdrLen) {
425         $hdrLen = 0;
426
427         $this->m_lpVer = substr($lpData, 0, 6);
428         if(($this->m_lpVer <> "GIF87a") && ($this->m_lpVer <> "GIF89a")) {
429             return false;
430         }
431
432         $this->m_nWidth  = $this->w2i(substr($lpData, 6, 2));
433         $this->m_nHeight = $this->w2i(substr($lpData, 8, 2));
434         if(!$this->m_nWidth || !$this->m_nHeight) {
435             return false;
436         }
437
438         $b = ord(substr($lpData, 10, 1));
439         $this->m_bGlobalClr  = ($b & 0x80) ? true : false;
440         $this->m_nColorRes   = ($b & 0x70) >> 4;
441         $this->m_bSorted     = ($b & 0x08) ? true : false;
442         $this->m_nTableSize  = 2 << ($b & 0x07);
443         $this->m_nBgColor    = ord(substr($lpData, 11, 1));
444         $this->m_nPixelRatio = ord(substr($lpData, 12, 1));
445         $hdrLen = 13;
446
447         if($this->m_bGlobalClr) {
448             $this->m_colorTable = new CGIFCOLORTABLE();
449             if(!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
450                 return false;
451             }
452             $hdrLen += 3 * $this->m_nTableSize;
453         }
454
455         return true;
456     }
457
458     function w2i($str) {
459         return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
460     }
461 }
462
463 class CGIFIMAGEHEADER
464 {
465     var $m_nLeft;
466     var $m_nTop;
467     var $m_nWidth;
468     var $m_nHeight;
469     var $m_bLocalClr;
470     var $m_bInterlace;
471     var $m_bSorted;
472     var $m_nTableSize;
473     var $m_colorTable;
474
475     // CONSTRUCTOR
476     function CGIFIMAGEHEADER() {
477         unSet($this->m_nLeft);
478         unSet($this->m_nTop);
479         unSet($this->m_nWidth);
480         unSet($this->m_nHeight);
481         unSet($this->m_bLocalClr);
482         unSet($this->m_bInterlace);
483         unSet($this->m_bSorted);
484         unSet($this->m_nTableSize);
485         unSet($this->m_colorTable);
486     }
487
488     function load($lpData, &$hdrLen) {
489         $hdrLen = 0;
490
491         $this->m_nLeft   = $this->w2i(substr($lpData, 0, 2));
492         $this->m_nTop    = $this->w2i(substr($lpData, 2, 2));
493         $this->m_nWidth  = $this->w2i(substr($lpData, 4, 2));
494         $this->m_nHeight = $this->w2i(substr($lpData, 6, 2));
495
496         if(!$this->m_nWidth || !$this->m_nHeight) {
497             return false;
498         }
499
500         $b = ord($lpData{8});
501         $this->m_bLocalClr  = ($b & 0x80) ? true : false;
502         $this->m_bInterlace = ($b & 0x40) ? true : false;
503         $this->m_bSorted    = ($b & 0x20) ? true : false;
504         $this->m_nTableSize = 2 << ($b & 0x07);
505         $hdrLen = 9;
506
507         if($this->m_bLocalClr) {
508             $this->m_colorTable = new CGIFCOLORTABLE();
509             if(!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) {
510                 return false;
511             }
512             $hdrLen += 3 * $this->m_nTableSize;
513         }
514
515         return true;
516     }
517
518     function w2i($str) {
519         return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
520     }
521 }
522
523 class CGIFIMAGE
524 {
525     var $m_disp;
526     var $m_bUser;
527     var $m_bTrans;
528     var $m_nDelay;
529     var $m_nTrans;
530     var $m_lpComm;
531     var $m_gih;
532     var $m_data;
533     var $m_lzw;
534
535     function CGIFIMAGE() {
536         unSet($this->m_disp);
537         unSet($this->m_bUser);
538         unSet($this->m_bTrans);
539         unSet($this->m_nDelay);
540         unSet($this->m_nTrans);
541         unSet($this->m_lpComm);
542         unSet($this->m_data);
543         $this->m_gih = new CGIFIMAGEHEADER();
544         $this->m_lzw = new CGIFLZW();
545     }
546
547     function load($data, &$datLen) {
548         $datLen = 0;
549
550         while(true) {
551             $b = ord($data{0});
552             $data = substr($data, 1);
553             $datLen++;
554
555             switch($b) {
556             case 0x21: // Extension
557                 if(!$this->skipExt($data, $len = 0)) {
558                     return false;
559                 }
560                 $datLen += $len;
561                 break;
562
563             case 0x2C: // Image
564                 // LOAD HEADER & COLOR TABLE
565                 if(!$this->m_gih->load($data, $len = 0)) {
566                     return false;
567                 }
568                 $data = substr($data, $len);
569                 $datLen += $len;
570
571                 // ALLOC BUFFER
572                 if(!($this->m_data = $this->m_lzw->deCompress($data, $len = 0))) {
573                     return false;
574                 }
575                 $data = substr($data, $len);
576                 $datLen += $len;
577
578                 if($this->m_gih->m_bInterlace) {
579                     $this->deInterlace();
580                 }
581                 return true;
582
583             case 0x3B: // EOF
584             default:
585                 return false;
586             }
587         }
588         return false;
589     }
590
591     function skipExt(&$data, &$extLen) {
592         $extLen = 0;
593
594         $b = ord($data{0});
595         $data = substr($data, 1);
596         $extLen++;
597
598         switch($b) {
599         case 0xF9: // Graphic Control
600             $b = ord($data{1});
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});
606             break;
607
608         case 0xFE: // Comment
609             $this->m_lpComm = substr($data, 1, ord($data{0}));
610             break;
611
612         case 0x01: // Plain text
613             break;
614
615         case 0xFF: // Application
616             break;
617         }
618
619         // SKIP DEFAULT AS DEFS MAY CHANGE
620         $b = ord($data{0});
621         $data = substr($data, 1);
622         $extLen++;
623         while($b > 0) {
624             $data = substr($data, $b);
625             $extLen += $b;
626             $b    = ord($data{0});
627             $data = substr($data, 1);
628             $extLen++;
629         }
630         return true;
631     }
632
633     function w2i($str) {
634         return ord(substr($str, 0, 1)) + (ord(substr($str, 1, 1)) << 8);
635     }
636
637     function deInterlace() {
638         $data = $this->m_data;
639
640         for($i = 0; $i < 4; $i++) {
641             switch($i) {
642             case 0:
643                 $s = 8;
644                 $y = 0;
645                 break;
646
647             case 1:
648                 $s = 8;
649                 $y = 4;
650                 break;
651
652             case 2:
653                 $s = 4;
654                 $y = 2;
655                 break;
656
657             case 3:
658                 $s = 2;
659                 $y = 1;
660                 break;
661             }
662
663             for(; $y < $this->m_gih->m_nHeight; $y += $s) {
664                 $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
665                 $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
666
667                 $data =
668                     substr($data, 0, $y * $this->m_gih->m_nWidth) .
669                     $lne .
670                     substr($data, ($y + 1) * $this->m_gih->m_nWidth);
671             }
672         }
673
674         $this->m_data = $data;
675     }
676 }
677
678 class CGIF
679 {
680     var $m_gfh;
681     var $m_lpData;
682     var $m_img;
683     var $m_bLoaded;
684
685     // CONSTRUCTOR
686     function CGIF() {
687         $this->m_gfh     = new CGIFFILEHEADER();
688         $this->m_img     = new CGIFIMAGE();
689         $this->m_lpData  = "";
690         $this->m_bLoaded = false;
691     }
692
693     function loadFile($lpszFileName, $iIndex) {
694         if($iIndex < 0) {
695             return false;
696         }
697
698
699         // READ FILE
700         if(!($fh = @fOpen($lpszFileName, "rb"))) {
701             return false;
702         }
703         $data = @fRead($fh, @fileSize($lpszFileName));
704         //        @fClose($fh);
705         //      $data=fread($fh,filesize($lpszFileName));
706         while(!feof($fh)) {
707             $data = $data . @fread($fh, 1024);
708             @fClose($fh);
709             $this->m_lpData = @fRead($fh, @fileSize($lpszFileName));
710             fClose($fh);
711
712             // GET FILE HEADER
713             if(!$this->m_gfh->load($this->m_lpData, $len = 0)) {
714                 return false;
715             }
716             $this->m_lpData = substr($this->m_lpData, $len);
717
718             do {
719                 if(!$this->m_img->load($this->m_lpData, $imgLen = 0)) {
720                     return false;
721                 }
722                 $this->m_lpData = substr($this->m_lpData, $imgLen);
723             }
724             while($iIndex-- > 0);
725
726             $this->m_bLoaded = true;
727             return true;
728     }
729
730     function getSize($lpszFileName, &$width, &$height)
731     {
732             if(!($fh = @fOpen($lpszFileName, "rb"))) {
733                 return false;
734             }
735             $data = @fRead($fh, @fileSize($lpszFileName));
736             @fClose($fh);
737         }
738         $gfh = new CGIFFILEHEADER();
739         if(!$gfh->load($data, $len = 0)) {
740             return false;
741         }
742
743         $width  = $gfh->m_nWidth;
744         $height = $gfh->m_nHeight;
745         return true;
746     }
747
748
749
750     function getBmp($bgColor) {
751         $out = "";
752
753         if(!$this->m_bLoaded) {
754             return false;
755         }
756
757         // PREPARE COLOR TABLE (RGBQUADs)
758         if($this->m_img->m_gih->m_bLocalClr) {
759             $nColors = $this->m_img->m_gih->m_nTableSize;
760             $rgbq    = $this->m_img->m_gih->m_colorTable->toRGBQuad();
761             if($bgColor != -1) {
762                 $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
763             }
764         }
765         else if($this->m_gfh->m_bGlobalClr) {
766             $nColors = $this->m_gfh->m_nTableSize;
767             $rgbq    = $this->m_gfh->m_colorTable->toRGBQuad();
768             if($bgColor != -1) {
769                 $bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
770             }
771         }
772         else {
773             $nColors =  0;
774             $bgColor = -1;
775         }
776
777         // PREPARE BITMAP BITS
778         $data = $this->m_img->m_data;
779         $nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth;
780         $bmp  = "";
781
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++) {
785                 if(
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))) {
790                     // PART OF IMAGE
791                     if($this->m_img->m_bTrans && (ord($data{$nPxl}) == $this->m_img->m_nTrans)) {
792                         // TRANSPARENT -> BACKGROUND
793                         if($bgColor == -1) {
794                             $bmp .= chr($this->m_gfh->m_nBgColor);
795                         }
796                         else {
797                             $bmp .= chr($bgColor);
798                         }
799                     }
800                     else {
801                         $bmp .= $data{$nPxl};
802                     }
803                 }
804                 else {
805                     // BACKGROUND
806                     if($bgColor == -1) {
807                         $bmp .= chr($this->m_gfh->m_nBgColor);
808                     }
809                     else {
810                         $bmp .= chr($bgColor);
811                     }
812                 }
813             }
814             $nPxl -= $this->m_gfh->m_nWidth << 1;
815
816             // ADD PADDING
817             for($x = 0; $x < $nPad; $x++) {
818                 $bmp .= "\x00";
819             }
820         }
821
822         // BITMAPFILEHEADER
823         $out .= "BM";
824         $out .= $this->dword(14 + 40 + ($nColors << 2) + strlen($bmp));
825         $out .= "\x00\x00";
826         $out .= "\x00\x00";
827         $out .= $this->dword(14 + 40 + ($nColors << 2));
828
829         // BITMAPINFOHEADER
830         $out .= $this->dword(40);
831         $out .= $this->dword($this->m_gfh->m_nWidth);
832         $out .= $this->dword($this->m_gfh->m_nHeight);
833         $out .= "\x01\x00";
834         $out .= "\x08\x00";
835         $out .= "\x00\x00\x00\x00";
836         $out .= "\x00\x00\x00\x00";
837         $out .= "\x12\x0B\x00\x00";
838         $out .= "\x12\x0B\x00\x00";
839         $out .= $this->dword($nColors % 256);
840         $out .= "\x00\x00\x00\x00";
841
842         // COLOR TABLE
843         if($nColors > 0) {
844             $out .= $rgbq;
845         }
846
847         // DATA
848         $out .= $bmp;
849
850         return $out;
851     }
852
853
854
855     function getPng($bgColor) {
856         $out = "";
857
858         if(!$this->m_bLoaded) {
859             return false;
860         }
861
862         // PREPARE COLOR TABLE (RGBQUADs)
863         if($this->m_img->m_gih->m_bLocalClr) {
864             $nColors = $this->m_img->m_gih->m_nTableSize;
865             $pal     = $this->m_img->m_gih->m_colorTable->toString();
866             if($bgColor != -1) {
867                 $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor);
868             }
869         }
870         else if($this->m_gfh->m_bGlobalClr) {
871             $nColors = $this->m_gfh->m_nTableSize;
872             $pal     = $this->m_gfh->m_colorTable->toString();
873             if($bgColor != -1) {
874                 $bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor);
875             }
876         }
877         else {
878             $nColors =  0;
879             $bgColor = -1;
880         }
881
882         // PREPARE BITMAP BITS
883         $data = $this->m_img->m_data;
884         $nPxl = 0;
885         $bmp  = "";
886         for($y = 0; $y < $this->m_gfh->m_nHeight; $y++) {
887             $bmp .= "\x00";
888             for($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) {
889                 if(
890                    ($x >= $this->m_img->m_gih->m_nLeft) &&
891                    ($y >= $this->m_img->m_gih->m_nTop) &&
892                    ($x <  ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) &&
893                    ($y <  ($this->m_img->m_gih->m_nTop  + $this->m_img->m_gih->m_nHeight))) {
894                     // PART OF IMAGE
895                     $bmp .= $data{$nPxl};
896                 }
897                 else {
898                     // BACKGROUND
899                     if($bgColor == -1) {
900                         $bmp .= chr($this->m_gfh->m_nBgColor);
901                     }
902                     else {
903                         $bmp .= chr($bgColor);
904                     }
905                 }
906             }
907         }
908         $bmp = gzcompress($bmp, 9);
909
910
911         // SIGNATURE
912         $out .= "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
913
914         // HEADER
915         $out .= "\x00\x00\x00\x0D";
916         $tmp  = "IHDR";
917         $tmp .= $this->ndword($this->m_gfh->m_nWidth);
918         $tmp .= $this->ndword($this->m_gfh->m_nHeight);
919         $tmp .= "\x08\x03\x00\x00\x00";
920         $out .= $tmp;
921         $out .= $this->ndword(crc32($tmp));
922
923         // PALETTE
924         if($nColors > 0) {
925             $out .= $this->ndword($nColors * 3);
926             $tmp  = "PLTE";
927             $tmp .= $pal;
928             $out .= $tmp;
929             $out .= $this->ndword(crc32($tmp));
930         }
931
932         // TRANSPARENCY
933         if($this->m_img->m_bTrans && ($nColors > 0)) {
934             $out .= $this->ndword($nColors);
935             $tmp  = "tRNS";
936             for($i = 0; $i < $nColors; $i++) {
937                 $tmp .= ($i == $this->m_img->m_nTrans) ? "\x00" : "\xFF";
938             }
939             $out .= $tmp;
940             $out .= $this->ndword(crc32($tmp));
941         }
942
943         // DATA BITS
944         $out .= $this->ndword(strlen($bmp));
945         $tmp  = "IDAT";
946         $tmp .= $bmp;
947         $out .= $tmp;
948         $out .= $this->ndword(crc32($tmp));
949
950         // END OF FILE
951         $out .= "\x00\x00\x00\x00IEND\xAE\x42\x60\x82";
952
953         return $out;
954     }
955
956     function dword($val) {
957         $val = intval($val);
958         return chr($val & 0xFF).chr(($val & 0xFF00) >> 8).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF000000) >> 24);
959     }
960
961     function ndword($val) {
962         $val = intval($val);
963         return chr(($val & 0xFF000000) >> 24).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF00) >> 8).chr($val & 0xFF);
964     }
965
966     function width() {
967         return $this->m_gfh->m_nWidth;
968     }
969
970     function height() {
971         return $this->m_gfh->m_nHeight;
972     }
973
974     function comment() {
975         return $this->m_img->m_lpComm;
976     }
977
978     function loaded() {
979         return $this->m_bLoaded;
980     }
981 }
982
983 // Local Variables:
984 // mode: php
985 // tab-width: 8
986 // c-basic-offset: 4
987 // c-hanging-comment-ender-p: nil
988 // indent-tabs-mode: nil
989 // End:
990 ?>