removed the need for REVERSESWAP
[swftools.git] / lib / png.c
1 /*  png.c
2    
3    Copyright (c) 2003/2004/2005 Matthias Kramm <kramm@quiss.org>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <math.h>
23 #include <fcntl.h>
24 #include <zlib.h>
25 #include "png.h"
26
27 typedef unsigned char U8;
28 typedef unsigned long U32;
29 typedef struct _COL {
30     U8 a,r,g,b;
31 } COL;
32
33 int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
34 {
35     unsigned int len;
36     unsigned char blen;
37     if(destlen) *destlen=0;
38     if(destdata) *destdata=0;
39     if(!fread(&blen, 4, 1, fi)) {
40         return 0;
41     }
42     if(!fread(head, 4, 1, fi)) {
43         return 0;
44     }
45     len = blen[0]<<24|blen[1]<<16|blen[2]<<8|blen[3];
46     if(destlen) *destlen = len;
47     if(destdata) {
48         if(!len) {
49             *destdata = 0;
50         } else {
51             *destdata = (U8*)malloc(len);
52             if(!fread(*destdata, len, 1, fi)) {
53                 *destdata = 0;
54                 if(destlen) *destlen=0;
55                 return 0;
56             }
57         }
58         fseek(fi, 4, SEEK_CUR);
59
60     } else {
61         fseek(fi, len+4, SEEK_CUR);
62     }
63     return 1;
64 }
65
66 unsigned int png_get_dword(FILE*fi)
67 {
68     unsigned int a;
69     unsigned char b;
70     fread(&b,4,1,fi);
71     return b[0]<<24|b[1]<<16|b[2]<<8|b[3];
72 }
73
74 struct png_header
75 {
76     int width;
77     int height;
78     int bpp;
79     int mode;
80 };
81
82 int png_read_header(FILE*fi, struct png_header*header)
83 {
84     char id[4];
85     int len;
86     int ok=0;
87     U8 head[8] = {137,80,78,71,13,10,26,10};
88     U8 head2[8];
89     U8*data;
90     fread(head2,8,1,fi);
91     if(strncmp((const char*)head,(const char*)head2,4))
92         return 0;
93     
94     while(png_read_chunk(&id, &len, &data, fi))
95     {
96         //printf("Chunk: %c%c%c%c (len:%d)\n", id[0],id[1],id[2],id[3], len);
97         if(!strncasecmp(id, "IHDR", 4)) {
98             char a,b,c,f,i;
99             if(len < 8) exit(1);
100             header->width = data[0]<<24|data[1]<<16|data[2]<<8|data[3];
101             header->height = data[4]<<24|data[5]<<16|data[6]<<8|data[7];
102             a = data[8];      // should be 8
103             b = data[9];      // should be 3(indexed) or 2(rgb)
104
105             c = data[10];     // compression mode (0)
106             f = data[11];     // filter mode (0)
107             i = data[12];     // interlace mode (0)
108
109             if(b!=0 && b!=4 && b!=2 && b!=3 && b!=6) {
110                 fprintf(stderr, "Image mode %d not supported!\n", b);
111                 return 0;
112             }
113             if(a!=8 && (b==2 || b==6)) {
114                 printf("Bpp %d in mode %d not supported!\n", a);
115                 return 0;
116             }
117             if(c!=0) {
118                 printf("Compression mode %d not supported!\n", c);
119                 return 0;
120             }
121             if(f!=0) {
122                 printf("Filter mode %d not supported!\n", f);
123                 return 0;
124             }
125             if(i!=0) {
126                 printf("Interlace mode %d not supported!\n", i);
127                 return 0;
128             }
129             //printf("%dx%d bpp:%d mode:%d comp:%d filter:%d interlace:%d\n",header->width, header->height, a,b,c,f,i);
130             header->bpp = a;
131             header->mode = b;
132             ok = 1;
133         } 
134         
135         free(data);
136     }
137     return ok;
138 }
139
140 typedef unsigned char byte;
141 #define ABS(a) ((a)>0?(a):(-(a)))
142 byte inline PaethPredictor (byte a,byte b,byte c)
143 {
144         // a = left, b = above, c = upper left
145         int p = a + b - c;        // initial estimate
146         int pa = ABS(p - a);      // distances to a, b, c
147         int pb = ABS(p - b);
148         int pc = ABS(p - c);
149         // return nearest of a,b,c,
150         // breaking ties in order a,b,c.
151         if (pa <= pb && pa <= pc) 
152                 return a;
153         else if (pb <= pc) 
154                 return b;
155         else return c;
156 }
157
158 void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width)
159 {
160     int x;
161     unsigned char last=0;
162     unsigned char upperlast=0;
163
164     if(mode==0) {
165         for(x=0;x<width;x++) {
166             *dest = *src;
167             dest++;
168             src++;
169         }
170     }
171     else if(mode==1) {
172         for(x=0;x<width;x++) {
173             *dest = *src+last;
174             last = *dest;
175             dest++;
176             src++;
177         }
178     }
179     else if(mode==2) {
180         for(x=0;x<width;x++) {
181             *dest = *src+*old;
182             dest++;
183             old++;
184             src++;
185         }
186     }
187     else if(mode==3) {
188         for(x=0;x<width;x++) {
189             *dest = *src+(*old+last)/2;
190             dest++;
191             old++;
192             src++;
193         }
194     }
195     else if(mode==4) {
196         for(x=0;x<width;x++) {
197             *dest = *src+PaethPredictor(last,*old,upperlast);
198             last = *dest;
199             upperlast = *old;
200             dest++;
201             old++;
202             src++;
203         }
204     }    
205
206 }
207
208 void applyfilter2(int mode, U8*src, U8*old, U8*dest, int width)
209 {
210     int x;
211     unsigned char lasta=0;
212     unsigned char lastb=0;
213     unsigned char upperlasta=0;
214     unsigned char upperlastb=0;
215
216     if(mode==0) {
217         for(x=0;x<width;x++) {
218             dest[0] = src[0];
219             dest[1] = src[1];
220             dest+=2;
221             src+=2;
222         }
223     }
224     else if(mode==1) {
225         for(x=0;x<width;x++) {
226             dest[0] = src[0]+lasta;
227             dest[1] = src[1]+lastb;
228             lasta = dest[0];
229             lastb = dest[1];
230             dest+=2;
231             src+=2;
232         }
233     }
234     else if(mode==2) {
235         for(x=0;x<width;x++) {
236             dest[0] = src[0]+old[0];
237             dest[1] = src[1]+old[1];
238             dest+=2;
239             old+=2;
240             src+=2;
241         }
242     }
243     else if(mode==3) {
244         for(x=0;x<width;x++) {
245             dest[0] = src[0]+(old[0]+lasta)/2;
246             dest[1] = src[1]+(old[1]+lastb)/2;
247             lasta = dest[0];
248             lastb = dest[1];
249             dest+=2;
250             old+=2;
251             src+=2;
252         }
253     }
254     else if(mode==4) {
255         for(x=0;x<width;x++) {
256             dest[0] = src[0]+PaethPredictor(lasta,old[0],upperlasta);
257             dest[1] = src[1]+PaethPredictor(lastb,old[1],upperlastb);
258             lasta = dest[0];
259             lastb = dest[1];
260             upperlasta = old[0];
261             upperlastb = old[1];
262             dest+=2;
263             old+=2;
264             src+=2;
265         }
266     }    
267 }
268
269
270 /* also performs 24 bit conversion! */
271 void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width)
272 {
273     int x;
274     unsigned char lastr=0;
275     unsigned char lastg=0;
276     unsigned char lastb=0;
277     unsigned char upperlastr=0;
278     unsigned char upperlastg=0;
279     unsigned char upperlastb=0;
280
281     if(mode==0) {
282         for(x=0;x<width;x++) {
283             dest[0] = 255;
284             dest[1] = src[0];
285             dest[2] = src[1];
286             dest[3] = src[2];
287             dest+=4;
288             src+=3;
289         }
290     }
291     else if(mode==1) {
292         for(x=0;x<width;x++) {
293             dest[0] = 255;
294             dest[1] = src[0]+lastr;
295             dest[2] = src[1]+lastg;
296             dest[3] = src[2]+lastb;
297             lastr = dest[1];
298             lastg = dest[2];
299             lastb = dest[3];
300             dest+=4;
301             src+=3;
302         }
303     }
304     else if(mode==2) {
305         for(x=0;x<width;x++) {
306             dest[0] = 255;
307             dest[1] = src[0]+old[1];
308             dest[2] = src[1]+old[2];
309             dest[3] = src[2]+old[3];
310             dest+=4;
311             old+=4;
312             src+=3;
313         }
314     }
315     else if(mode==3) {
316         for(x=0;x<width;x++) {
317             dest[0] = 255;
318             dest[1] = src[0]+(old[1]+lastr)/2;
319             dest[2] = src[1]+(old[2]+lastg)/2;
320             dest[3] = src[2]+(old[3]+lastb)/2;
321             lastr = dest[1];
322             lastg = dest[2];
323             lastb = dest[3];
324             dest+=4;
325             old+=4;
326             src+=3;
327         }
328     }
329     else if(mode==4) {
330         for(x=0;x<width;x++) {
331             dest[0] = 255;
332             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
333             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
334             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
335             lastr = dest[1];
336             lastg = dest[2];
337             lastb = dest[3];
338             upperlastr = old[1];
339             upperlastg = old[2];
340             upperlastb = old[3];
341             dest+=4;
342             old+=4;
343             src+=3;
344         }
345     }    
346 }
347
348 void inline applyfilter4(int mode, U8*src, U8*old, U8*dest, int width)
349 {
350     int x;
351     unsigned char lastr=0;
352     unsigned char lastg=0;
353     unsigned char lastb=0;
354     unsigned char lasta=0;
355     unsigned char upperlastr=0;
356     unsigned char upperlastg=0;
357     unsigned char upperlastb=0;
358     unsigned char upperlasta=0;
359
360     if(mode==0) {
361         for(x=0;x<width;x++) {
362             dest[0] = src[3];
363             dest[1] = src[0];
364             dest[2] = src[1];
365             dest[3] = src[2];
366             dest+=4;
367             src+=4;
368         }
369     }
370     else if(mode==1) {
371         for(x=0;x<width;x++) {
372             dest[0] = src[3]+lasta;
373             dest[1] = src[0]+lastr;
374             dest[2] = src[1]+lastg;
375             dest[3] = src[2]+lastb;
376             lasta = dest[0];
377             lastr = dest[1];
378             lastg = dest[2];
379             lastb = dest[3];
380             dest+=4;
381             src+=4;
382         }
383     }
384     else if(mode==2) {
385         for(x=0;x<width;x++) {
386             dest[0] = src[3]+old[0];
387             dest[1] = src[0]+old[1];
388             dest[2] = src[1]+old[2];
389             dest[3] = src[2]+old[3];
390             dest+=4;
391             old+=4;
392             src+=4;
393         }
394     }
395     else if(mode==3) {
396         for(x=0;x<width;x++) {
397             dest[0] = src[3]+(old[0]+lasta)/2;
398             dest[1] = src[0]+(old[1]+lastr)/2;
399             dest[2] = src[1]+(old[2]+lastg)/2;
400             dest[3] = src[2]+(old[3]+lastb)/2;
401             lasta = dest[0];
402             lastr = dest[1];
403             lastg = dest[2];
404             lastb = dest[3];
405             dest+=4;
406             old+=4;
407             src+=4;
408         }
409     }
410     else if(mode==4) {
411         for(x=0;x<width;x++) {
412             dest[0] = src[3]+PaethPredictor(lasta,old[0],upperlasta);
413             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
414             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
415             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
416             lasta = dest[0];
417             lastr = dest[1];
418             lastg = dest[2];
419             lastb = dest[3];
420             upperlasta = old[0];
421             upperlastr = old[1];
422             upperlastg = old[2];
423             upperlastb = old[3];
424             dest+=4;
425             old+=4;
426             src+=4;
427         }
428     }    
429 }
430
431
432 int getPNGdimensions(char*sname, int*destwidth, int*destheight)
433 {
434     FILE*fi;
435     struct png_header header;
436     if ((fi = fopen(sname, "rb")) == NULL) {
437         fprintf(stderr, "Couldn't open %s\n", sname);
438         return 0;
439     }
440     if(!png_read_header(fi, &header)) {
441         fprintf(stderr, "Error reading header from file %s\n", sname);
442         return 0;
443     }
444
445     *destwidth = header.width;
446     *destheight = header.height;
447     return 1;
448 }
449
450 int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata)
451 {
452     char tagid[4];
453     int len;
454     U8*data;
455     U8*imagedata;
456     U8*zimagedata=0;
457     unsigned long int imagedatalen;
458     unsigned long int zimagedatalen=0;
459     U8*palette = 0;
460     int palettelen = 0;
461     U8*alphapalette = 0;
462     int alphapalettelen = 0;
463     struct png_header header;
464     int bypp;
465     U8*data2 = 0;
466
467     FILE *fi;
468     U8 *scanline;
469
470     if ((fi = fopen(sname, "rb")) == NULL) {
471         printf("Couldn't open %s\n", sname);
472         return 0;
473     }
474
475     if(!png_read_header(fi, &header)) {
476         printf("Error reading header from file %s\n", sname);
477         return 0;
478     }
479
480     if(header.mode == 3 || header.mode == 0) bypp = 1;
481     else if(header.mode == 4) bypp = 2;
482     else if(header.mode == 2) bypp = 3;
483     else if(header.mode == 6) bypp = 4;
484     else {
485         printf("ERROR: mode:%d\n", header.mode);
486         return 0;
487     }
488
489     imagedatalen = bypp * header.width * header.height + 65536;
490     imagedata = (U8*)malloc(imagedatalen);
491
492     fseek(fi,8,SEEK_SET);
493     while(!feof(fi))
494     {
495         if(!png_read_chunk(&tagid, &len, &data, fi))
496             break;
497         if(!strncmp(tagid, "IEND", 4)) {
498             break;
499         }
500         if(!strncmp(tagid, "PLTE", 4)) {
501             palette = data;
502             palettelen = len/3;
503             data = 0; //don't free data
504             //printf("%d colors in palette\n", palettelen);
505         }
506         if(!strncmp(tagid, "tRNS", 4)) {
507             if(header.mode == 3) {
508                 alphapalette = data;
509                 alphapalettelen = len;
510                 data = 0; //don't free data
511                 //printf("found %d alpha colors\n", alphapalettelen);
512             }
513         }
514         if(!strncmp(tagid, "IDAT", 4)) {
515             if(!zimagedata) {
516                 zimagedatalen = len;
517                 zimagedata = (U8*)malloc(len);
518                 memcpy(zimagedata,data,len);
519             } else {
520                 zimagedata = (U8*)realloc(zimagedata, zimagedatalen+len);
521                 memcpy(&zimagedata[zimagedatalen], data, len);
522                 zimagedatalen += len;
523             }
524         }
525         if(!strncmp(tagid, "tEXt", 4)) {
526             /*int t;
527             printf("Image Text: ");
528             for(t=0;t<len;t++) {
529                 if(data[t]>=32 && data[t]<128)
530                     printf("%c", data[t]);
531                 else
532                     printf("?");
533             }
534             printf("\n");*/
535         }
536         if(data)
537             free(data);
538     }
539     
540     if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
541         printf("Couldn't uncompress %s!\n", sname);
542         if(zimagedata)
543             free(zimagedata);
544         return 0;
545     }
546     free(zimagedata);
547     fclose(fi);
548
549     *destwidth = header.width;
550     *destheight = header.height;
551         
552     data2 = (U8*)malloc(header.width*header.height*4);
553
554     if(header.mode == 4)
555     {
556         int i,s=0;
557         int x,y;
558         int pos=0;
559         U8* old= (U8*)malloc(header.width*2);
560         memset(old, 0, header.width*2);
561         *destdata = data2;
562         for(y=0;y<header.height;y++) {
563             int mode = imagedata[pos++]; //filter mode
564             U8*src;
565             U8*dest;
566             int x;
567             dest = &data2[(y*header.width)*4];
568
569             if(header.bpp == 8) {
570                 /* one byte per pixel */
571                 src = &imagedata[pos];
572                 pos+=header.width*2;
573             } else {
574                 /* not implemented yet */
575                 fprintf(stderr, "ERROR: mode=4 bpp:%d\n", header.bpp);
576                 free(data2);
577                 return 0;
578             }
579
580             applyfilter2(mode, src, old, dest, header.width);
581             memcpy(old, dest, header.width*2);
582
583             for(x=header.width-1;x>=0;x--) {
584                 U8 gray = dest[x*2+0];
585                 U8 alpha = dest[x*2+1];
586                 dest[x*4+0] = alpha;
587                 dest[x*4+1] = gray;
588                 dest[x*4+2] = gray;
589                 dest[x*4+3] = gray;
590             }
591         }
592         free(old);
593     } else if(header.mode == 6 || header.mode == 2) {
594         int i,s=0;
595         int x,y;
596         int pos=0;
597         *destdata = data2;
598         for(y=0;y<header.height;y++) {
599             int mode = imagedata[pos++]; //filter mode
600             U8*src;
601             U8*dest;
602             U8*old;
603             dest = &data2[(y*header.width)*4];
604
605             if(header.bpp == 8)
606             {
607                 /* one byte per pixel */
608                 src = &imagedata[pos];
609                 pos+=header.width*(header.mode==6?4:3);
610             } else {
611                 /* not implemented yet */
612                 fprintf(stderr, "ERROR: bpp:%d\n", header.bpp);
613                 free(data2);
614                 return 0;
615             }
616
617             if(!y) {
618                 memset(data2,0,header.width*4);
619                 old = &data2[y*header.width*4];
620             } else {
621                 old = &data2[(y-1)*header.width*4];
622             }
623             if(header.mode == 6)
624                 applyfilter4(mode, src, old, dest, header.width);
625             else // header.mode = 2
626                 applyfilter3(mode, src, old, dest, header.width);
627         }
628     } else if(header.mode == 0 || header.mode == 3) {
629         COL*rgba = 0;
630         U8*tmpline = (U8*)malloc(header.width+1);
631         U8*destline = (U8*)malloc(header.width+1);
632         int i,x,y;
633         int pos=0;
634         
635         *destdata = data2;
636         
637         if(header.mode == 0) { // grayscale palette
638             int mult = (0x1ff>>header.bpp);
639             palettelen = 1<<header.bpp;
640             rgba = (COL*)malloc(palettelen*sizeof(COL));
641             for(i=0;i<palettelen;i++) {
642                 rgba[i].a = 255;
643                 rgba[i].r = i*mult;
644                 rgba[i].g = i*mult;
645                 rgba[i].b = i*mult;
646             }
647         } else {
648             if(!palette) {
649                 fprintf(stderr, "Error: No palette found!\n");
650                 exit(1);
651             }
652             rgba = (COL*)malloc(palettelen*4);
653             /* 24->32 bit conversion */
654             for(i=0;i<palettelen;i++) {
655                 rgba[i].r = palette[i*3+0];
656                 rgba[i].g = palette[i*3+1];
657                 rgba[i].b = palette[i*3+2];
658                 if(alphapalette && i<alphapalettelen) {
659                     rgba[i].a = alphapalette[i];
660                     /*rgba[i].r = ((int)rgba[i].r*rgba[i].a)/255;
661                     rgba[i].g = ((int)rgba[i].g*rgba[i].a)/255;
662                     rgba[i].b = ((int)rgba[i].b*rgba[i].a)/255;*/
663                 } else {
664                     rgba[i].a = 255;
665                 }
666             }
667         }
668
669         for(y=0;y<header.height;y++) {
670             int mode = imagedata[pos++]; //filter mode
671             int x;
672             U8*old;
673             U8*src;
674             src = &imagedata[pos];
675             if(header.bpp == 8) {
676                 pos+=header.width;
677             } else {
678                 int x,s=0;
679                 int bitpos = 0;
680                 U32 v = (1<<header.bpp)-1;
681                 for(x=0;x<header.width;x++) {
682                     U32 r = src[s/8]<<8 | 
683                             src[s/8+1];
684                     int t;
685                     tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
686                     s+=header.bpp;
687                 }
688                 src = tmpline;
689                 pos+=(header.width*header.bpp+7)/8;
690             }
691
692             if(!y) {
693                 memset(destline,0,header.width);
694                 old = &destline[y*header.width];
695             } else {
696                 old = tmpline;
697             }
698             applyfilter1(mode, src, old, destline, header.width);
699             memcpy(tmpline,destline,header.width);
700             for(x=0;x<header.width;x++) {
701                 *(COL*)&data2[y*header.width*4+x*4+0] = rgba[destline[x]];
702             }
703         }
704         free(tmpline);
705         free(destline);
706         free(rgba);
707     } else {
708         printf("expected PNG mode to be 2, 3 or 6 (is:%d)\n", header.mode);
709         return 0;
710     }
711
712     return 1;
713 }
714
715 static U32 mycrc32;
716
717 static U32*crc32_table = 0;
718 static void make_crc32_table(void)
719 {
720   int t;
721   if(crc32_table) 
722       return;
723   crc32_table = (U32*)malloc(1024);
724
725   for (t = 0; t < 256; t++) {
726     U32 c = t;
727     int s;
728     for (s = 0; s < 8; s++) {
729       c = (0xedb88320L*(c&1)) ^ (c >> 1);
730     }
731     crc32_table[t] = c;
732   }
733 }
734 static inline void png_write_byte(FILE*fi, U8 byte)
735 {
736     fwrite(&byte,1,1,fi);
737     mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
738 }
739 static void png_start_chunk(FILE*fi, char*type, int len)
740 {
741     U8 mytype[4]={0,0,0,0};
742     U8 mylen[4];
743     mylen[0] = len>>24;
744     mylen[1] = len>>16;
745     mylen[2] = len>>8;
746     mylen[3] = len;
747     memcpy(mytype,type,strlen(type));
748     fwrite(&mylen, 4, 1, fi);
749     mycrc32=0xffffffff;
750     png_write_byte(fi,mytype[0]);
751     png_write_byte(fi,mytype[1]);
752     png_write_byte(fi,mytype[2]);
753     png_write_byte(fi,mytype[3]);
754 }
755 static void png_write_bytes(FILE*fi, U8*bytes, int len)
756 {
757     int t;
758     for(t=0;t<len;t++)
759         png_write_byte(fi,bytes[t]);
760 }
761 static void png_write_dword(FILE*fi, U32 dword)
762 {
763     png_write_byte(fi,dword>>24);
764     png_write_byte(fi,dword>>16);
765     png_write_byte(fi,dword>>8);
766     png_write_byte(fi,dword);
767 }
768 static void png_end_chunk(FILE*fi)
769 {
770     U32 tmp = mycrc32^0xffffffff;
771     U8 tmp2[4];
772     tmp2[0] = tmp>>24;
773     tmp2[1] = tmp>>16;
774     tmp2[2] = tmp>>8;
775     tmp2[3] = tmp;
776     fwrite(&tmp2,4,1,fi);
777 }
778
779 void writePNG(char*filename, unsigned char*data, int width, int height)
780 {
781     FILE*fi;
782     int crc;
783     int t;
784     U8 format;
785     U8 tmp;
786     U8* data2=0;
787     U8* data3=0;
788     U32 datalen;
789     U32 datalen2;
790     U32 datalen3;
791     U8 head[] = {137,80,78,71,13,10,26,10}; // PNG header
792     int cols;
793     char alpha = 1;
794     int pos = 0;
795     int error;
796     U32 tmp32;
797     int bpp;
798     int ret;
799
800     make_crc32_table();
801
802     bpp = 32;
803     cols = 0;
804     format = 5;
805
806     datalen = (width*height*bpp/8+cols*8);
807     
808     fi = fopen(filename, "wb");
809     if(!fi) {
810         perror("open");
811         return;
812     }
813     fwrite(head,sizeof(head),1,fi);     
814
815     png_start_chunk(fi, "IHDR", 13);
816      png_write_dword(fi,width);
817      png_write_dword(fi,height);
818      png_write_byte(fi,8);
819      if(format == 3)
820      png_write_byte(fi,3); //indexed
821      else if(format == 5 && alpha==0)
822      png_write_byte(fi,2); //rgb
823      else if(format == 5 && alpha==1)
824      png_write_byte(fi,6); //rgba
825      else return;
826
827      png_write_byte(fi,0); //compression mode
828      png_write_byte(fi,0); //filter mode
829      png_write_byte(fi,0); //interlace mode
830     png_end_chunk(fi);
831    
832 /*    if(format == 3) {
833         png_start_chunk(fi, "PLTE", 768);
834          
835          for(t=0;t<256;t++) {
836              png_write_byte(fi,palette[t].r);
837              png_write_byte(fi,palette[t].g);
838              png_write_byte(fi,palette[t].b);
839          }
840         png_end_chunk(fi);
841     }*/
842     {
843         int pos2 = 0;
844         int x,y;
845         int srcwidth = width * (bpp/8);
846         datalen3 = (width*4+5)*height;
847         data3 = (U8*)malloc(datalen3);
848         for(y=0;y<height;y++)
849         {
850            data3[pos2++]=0; //filter type
851            for(x=0;x<width;x++) {
852                data3[pos2++]=data[pos+1];
853                data3[pos2++]=data[pos+2];
854                data3[pos2++]=data[pos+3];
855                data3[pos2++]=data[pos+0]; //alpha
856                pos+=4;
857            }
858            pos+=((srcwidth+3)&~3)-srcwidth; //align
859         }
860         datalen3=pos2;
861     }
862
863     datalen2 = datalen3;
864     data2 = malloc(datalen2);
865
866     if((ret = compress (data2, &datalen2, data3, datalen3)) != Z_OK) {
867         fprintf(stderr, "zlib error in pic %d\n", ret);
868         return;
869     }
870     png_start_chunk(fi, "IDAT", datalen2);
871     png_write_bytes(fi,data2,datalen2);
872     png_end_chunk(fi);
873     png_start_chunk(fi, "IEND", 0);
874     png_end_chunk(fi);
875
876     free(data2);
877     free(data3);
878 }