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