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