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