made a few functions static
[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 static int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
34 {
35     unsigned int len;
36     unsigned char blen[4];
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 static unsigned int png_get_dword(FILE*fi)
67 {
68     unsigned int a;
69     unsigned char b[4];
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 static 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 static inline byte 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 static 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 static 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 static 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 static 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     U8 alphacolor[3];
467     int hasalphacolor=0;
468
469     FILE *fi;
470     U8 *scanline;
471
472     if ((fi = fopen(sname, "rb")) == NULL) {
473         printf("Couldn't open %s\n", sname);
474         return 0;
475     }
476
477     if(!png_read_header(fi, &header)) {
478         printf("Error reading header from file %s\n", sname);
479         return 0;
480     }
481
482     if(header.mode == 3 || header.mode == 0) bypp = 1;
483     else if(header.mode == 4) bypp = 2;
484     else if(header.mode == 2) bypp = 3;
485     else if(header.mode == 6) bypp = 4;
486     else {
487         printf("ERROR: mode:%d\n", header.mode);
488         return 0;
489     }
490
491     imagedatalen = bypp * header.width * header.height + 65536;
492     imagedata = (U8*)malloc(imagedatalen);
493
494     fseek(fi,8,SEEK_SET);
495     while(!feof(fi))
496     {
497         if(!png_read_chunk(&tagid, &len, &data, fi))
498             break;
499         if(!strncmp(tagid, "IEND", 4)) {
500             break;
501         }
502         if(!strncmp(tagid, "PLTE", 4)) {
503             palette = data;
504             palettelen = len/3;
505             data = 0; //don't free data
506             //printf("%d colors in palette\n", palettelen);
507         }
508         if(!strncmp(tagid, "tRNS", 4)) {
509             if(header.mode == 3) {
510                 alphapalette = data;
511                 alphapalettelen = len;
512                 data = 0; //don't free data
513                 //printf("found %d alpha colors\n", alphapalettelen);
514             } else if(header.mode == 0 || header.mode == 2) {
515                 int t;
516                 if(header.mode == 2) {
517                     alphacolor[0] = data[1];
518                     alphacolor[1] = data[3];
519                     alphacolor[2] = data[5];
520                 } else {
521                     alphacolor[0] = alphacolor[1] = alphacolor[2] = data[1];
522                 }
523                 hasalphacolor = 1;
524             }
525         }
526         if(!strncmp(tagid, "IDAT", 4)) {
527             if(!zimagedata) {
528                 zimagedatalen = len;
529                 zimagedata = (U8*)malloc(len);
530                 memcpy(zimagedata,data,len);
531             } else {
532                 zimagedata = (U8*)realloc(zimagedata, zimagedatalen+len);
533                 memcpy(&zimagedata[zimagedatalen], data, len);
534                 zimagedatalen += len;
535             }
536         }
537         if(!strncmp(tagid, "tEXt", 4)) {
538             /*int t;
539             printf("Image Text: ");
540             for(t=0;t<len;t++) {
541                 if(data[t]>=32 && data[t]<128)
542                     printf("%c", data[t]);
543                 else
544                     printf("?");
545             }
546             printf("\n");*/
547         }
548         if(data) {
549             free(data); data=0;
550         }
551     }
552     
553     if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
554         printf("Couldn't uncompress %s!\n", sname);
555         if(zimagedata)
556             free(zimagedata);
557         return 0;
558     }
559     free(zimagedata);
560     fclose(fi);
561
562     *destwidth = header.width;
563     *destheight = header.height;
564         
565     data2 = (U8*)malloc(header.width*header.height*4);
566
567     if(header.mode == 4)
568     {
569         int i,s=0;
570         int x,y;
571         int pos=0;
572         U8* old= (U8*)malloc(header.width*2);
573         memset(old, 0, header.width*2);
574         *destdata = data2;
575         for(y=0;y<header.height;y++) {
576             int mode = imagedata[pos++]; //filter mode
577             U8*src;
578             U8*dest;
579             int x;
580             dest = &data2[(y*header.width)*4];
581
582             if(header.bpp == 8) {
583                 /* one byte per pixel */
584                 src = &imagedata[pos];
585                 pos+=header.width*2;
586             } else {
587                 /* not implemented yet */
588                 fprintf(stderr, "ERROR: mode=4 bpp:%d\n", header.bpp);
589                 free(data2);
590                 return 0;
591             }
592
593             applyfilter2(mode, src, old, dest, header.width);
594             memcpy(old, dest, header.width*2);
595
596             for(x=header.width-1;x>=0;x--) {
597                 U8 gray = dest[x*2+0];
598                 U8 alpha = dest[x*2+1];
599                 dest[x*4+0] = alpha;
600                 dest[x*4+1] = gray;
601                 dest[x*4+2] = gray;
602                 dest[x*4+3] = gray;
603             }
604         }
605         free(old);
606         free(imagedata);
607     } else if(header.mode == 6 || header.mode == 2) {
608         int i,s=0;
609         int x,y;
610         int pos=0;
611         *destdata = data2;
612         for(y=0;y<header.height;y++) {
613             int mode = imagedata[pos++]; //filter mode
614             U8*src;
615             U8*dest;
616             U8*old;
617             dest = &data2[(y*header.width)*4];
618
619             if(header.bpp == 8)
620             {
621                 /* one byte per pixel */
622                 src = &imagedata[pos];
623                 pos+=header.width*(header.mode==6?4:3);
624             } else {
625                 /* not implemented yet */
626                 fprintf(stderr, "ERROR: bpp:%d\n", header.bpp);
627                 free(data2);
628                 return 0;
629             }
630
631             if(!y) {
632                 memset(data2,0,header.width*4);
633                 old = &data2[y*header.width*4];
634             } else {
635                 old = &data2[(y-1)*header.width*4];
636             }
637             if(header.mode == 6) {
638                 applyfilter4(mode, src, old, dest, header.width);
639             } else { // header.mode = 2
640                 applyfilter3(mode, src, old, dest, header.width);
641                 /* replace alpha color */
642                 if(hasalphacolor) {
643                     int x;
644                     for(x=0;x<header.width;x++) {
645                         if(dest[x*4+1] == alphacolor[0] &&
646                            dest[x*4+2] == alphacolor[1] &&
647                            dest[x*4+3] == alphacolor[2]) {
648                             *(U32*)&dest[x*4] = 0;
649                         }
650                     }
651                 }
652             }
653         }
654         free(imagedata);
655     } else if(header.mode == 0 || header.mode == 3) {
656         COL*rgba = 0;
657         U8*tmpline = (U8*)malloc(header.width+1);
658         U8*destline = (U8*)malloc(header.width+1);
659         int i,x,y;
660         int pos=0;
661         
662         *destdata = data2;
663         
664         if(header.mode == 0) { // grayscale palette
665             int mult = (0x1ff>>header.bpp);
666             palettelen = 1<<header.bpp;
667             rgba = (COL*)malloc(palettelen*sizeof(COL));
668             for(i=0;i<palettelen;i++) {
669                 rgba[i].a = 255;
670                 rgba[i].r = i*mult;
671                 rgba[i].g = i*mult;
672                 rgba[i].b = i*mult;
673                 if(hasalphacolor) {
674                     if(rgba[i].r == alphacolor[0])
675                         rgba[i].a = 0;
676                 }
677             }
678         } else {
679             if(!palette) {
680                 fprintf(stderr, "Error: No palette found!\n");
681                 exit(1);
682             }
683             rgba = (COL*)malloc(palettelen*4);
684             /* 24->32 bit conversion */
685             for(i=0;i<palettelen;i++) {
686                 rgba[i].r = palette[i*3+0];
687                 rgba[i].g = palette[i*3+1];
688                 rgba[i].b = palette[i*3+2];
689                 if(alphapalette && i<alphapalettelen) {
690                     rgba[i].a = alphapalette[i];
691                     /*rgba[i].r = ((int)rgba[i].r*rgba[i].a)/255;
692                     rgba[i].g = ((int)rgba[i].g*rgba[i].a)/255;
693                     rgba[i].b = ((int)rgba[i].b*rgba[i].a)/255;*/
694                 } else {
695                     rgba[i].a = 255;
696                 }
697                 if(hasalphacolor) {
698                     if(rgba[i].r == alphacolor[0] &&
699                        rgba[i].g == alphacolor[1] &&
700                        rgba[i].b == alphacolor[2])
701                         rgba[i].a = 0;
702                 }
703             }
704         }
705
706         for(y=0;y<header.height;y++) {
707             int mode = imagedata[pos++]; //filter mode
708             int x;
709             U8*old;
710             U8*src;
711             src = &imagedata[pos];
712             if(header.bpp == 8) {
713                 pos+=header.width;
714             } else {
715                 int x,s=0;
716                 int bitpos = 0;
717                 U32 v = (1<<header.bpp)-1;
718                 for(x=0;x<header.width;x++) {
719                     U32 r = src[s/8]<<8 | 
720                             src[s/8+1];
721                     int t;
722                     tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
723                     s+=header.bpp;
724                 }
725                 src = tmpline;
726                 pos+=(header.width*header.bpp+7)/8;
727             }
728
729             if(!y) {
730                 memset(destline,0,header.width);
731                 old = &destline[y*header.width];
732             } else {
733                 old = tmpline;
734             }
735             applyfilter1(mode, src, old, destline, header.width);
736             memcpy(tmpline,destline,header.width);
737             for(x=0;x<header.width;x++) {
738                 *(COL*)&data2[y*header.width*4+x*4+0] = rgba[destline[x]];
739             }
740         }
741         free(tmpline);
742         free(destline);
743         free(rgba);
744         free(imagedata);
745     } else {
746         printf("expected PNG mode to be 2, 3 or 6 (is:%d)\n", header.mode);
747         return 0;
748     }
749
750     return 1;
751 }
752
753 static U32 mycrc32;
754
755 static U32*crc32_table = 0;
756 static void make_crc32_table(void)
757 {
758   int t;
759   if(crc32_table) 
760       return;
761   crc32_table = (U32*)malloc(1024);
762
763   for (t = 0; t < 256; t++) {
764     U32 c = t;
765     int s;
766     for (s = 0; s < 8; s++) {
767       c = (0xedb88320L*(c&1)) ^ (c >> 1);
768     }
769     crc32_table[t] = c;
770   }
771 }
772 static inline void png_write_byte(FILE*fi, U8 byte)
773 {
774     fwrite(&byte,1,1,fi);
775     mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
776 }
777 static void png_start_chunk(FILE*fi, char*type, int len)
778 {
779     U8 mytype[4]={0,0,0,0};
780     U8 mylen[4];
781     mylen[0] = len>>24;
782     mylen[1] = len>>16;
783     mylen[2] = len>>8;
784     mylen[3] = len;
785     memcpy(mytype,type,strlen(type));
786     fwrite(&mylen, 4, 1, fi);
787     mycrc32=0xffffffff;
788     png_write_byte(fi,mytype[0]);
789     png_write_byte(fi,mytype[1]);
790     png_write_byte(fi,mytype[2]);
791     png_write_byte(fi,mytype[3]);
792 }
793 static void png_write_bytes(FILE*fi, U8*bytes, int len)
794 {
795     int t;
796     for(t=0;t<len;t++)
797         png_write_byte(fi,bytes[t]);
798 }
799 static void png_write_dword(FILE*fi, U32 dword)
800 {
801     png_write_byte(fi,dword>>24);
802     png_write_byte(fi,dword>>16);
803     png_write_byte(fi,dword>>8);
804     png_write_byte(fi,dword);
805 }
806 static void png_end_chunk(FILE*fi)
807 {
808     U32 tmp = mycrc32^0xffffffff;
809     U8 tmp2[4];
810     tmp2[0] = tmp>>24;
811     tmp2[1] = tmp>>16;
812     tmp2[2] = tmp>>8;
813     tmp2[3] = tmp;
814     fwrite(&tmp2,4,1,fi);
815 }
816
817 void writePNG(char*filename, unsigned char*data, int width, int height)
818 {
819     FILE*fi;
820     int crc;
821     int t;
822     U8 format;
823     U8 tmp;
824     U8* data2=0;
825     U8* data3=0;
826     U32 datalen;
827     U32 datalen2;
828     U32 datalen3;
829     U8 head[] = {137,80,78,71,13,10,26,10}; // PNG header
830     int cols;
831     char alpha = 1;
832     int pos = 0;
833     int error;
834     U32 tmp32;
835     int bpp;
836     int ret;
837
838     make_crc32_table();
839
840     bpp = 32;
841     cols = 0;
842     format = 5;
843
844     datalen = (width*height*bpp/8+cols*8);
845     
846     fi = fopen(filename, "wb");
847     if(!fi) {
848         perror("open");
849         return;
850     }
851     fwrite(head,sizeof(head),1,fi);     
852
853     png_start_chunk(fi, "IHDR", 13);
854      png_write_dword(fi,width);
855      png_write_dword(fi,height);
856      png_write_byte(fi,8);
857      if(format == 3)
858      png_write_byte(fi,3); //indexed
859      else if(format == 5 && alpha==0)
860      png_write_byte(fi,2); //rgb
861      else if(format == 5 && alpha==1)
862      png_write_byte(fi,6); //rgba
863      else return;
864
865      png_write_byte(fi,0); //compression mode
866      png_write_byte(fi,0); //filter mode
867      png_write_byte(fi,0); //interlace mode
868     png_end_chunk(fi);
869    
870 /*    if(format == 3) {
871         png_start_chunk(fi, "PLTE", 768);
872          
873          for(t=0;t<256;t++) {
874              png_write_byte(fi,palette[t].r);
875              png_write_byte(fi,palette[t].g);
876              png_write_byte(fi,palette[t].b);
877          }
878         png_end_chunk(fi);
879     }*/
880     {
881         int pos2 = 0;
882         int x,y;
883         int srcwidth = width * (bpp/8);
884         datalen3 = (width*4+5)*height;
885         data3 = (U8*)malloc(datalen3);
886         for(y=0;y<height;y++)
887         {
888            data3[pos2++]=0; //filter type
889            for(x=0;x<width;x++) {
890                data3[pos2++]=data[pos+1];
891                data3[pos2++]=data[pos+2];
892                data3[pos2++]=data[pos+3];
893                data3[pos2++]=data[pos+0]; //alpha
894                pos+=4;
895            }
896            pos+=((srcwidth+3)&~3)-srcwidth; //align
897         }
898         datalen3=pos2;
899     }
900
901     datalen2 = datalen3;
902     data2 = (U8*)malloc(datalen2);
903
904     if((ret = compress (data2, &datalen2, data3, datalen3)) != Z_OK) {
905         fprintf(stderr, "zlib error in pic %d\n", ret);
906         return;
907     }
908     png_start_chunk(fi, "IDAT", datalen2);
909     png_write_bytes(fi,data2,datalen2);
910     png_end_chunk(fi);
911     png_start_chunk(fi, "IEND", 0);
912     png_end_chunk(fi);
913
914     free(data2);
915     free(data3);
916 }