added support for masks of higher resolutions than the bitmaps they mask
[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
26 #ifdef EXPORT
27 #undef EXPORT
28 #endif
29
30 #ifdef PNG_INLINE_EXPORTS
31 #define EXPORT static
32 #else
33 #define EXPORT
34 #include "png.h"
35 #endif
36
37 typedef unsigned u32;
38
39 typedef struct _COL {
40     unsigned char a,r,g,b;
41 } COL;
42
43 static int png_read_chunk(char (*head)[4], int*destlen, unsigned char**destdata, FILE*fi)
44 {
45     unsigned int len;
46     unsigned char blen[4];
47     if(destlen) *destlen=0;
48     if(destdata) *destdata=0;
49     if(!fread(&blen, 4, 1, fi)) {
50         return 0;
51     }
52     if(!fread(head, 4, 1, fi)) {
53         return 0;
54     }
55     len = blen[0]<<24|blen[1]<<16|blen[2]<<8|blen[3];
56     if(destlen) *destlen = len;
57     if(destdata) {
58         if(!len) {
59             *destdata = 0;
60         } else {
61             *destdata = (unsigned char*)malloc(len);
62             if(!fread(*destdata, len, 1, fi)) {
63                 *destdata = 0;
64                 if(destlen) *destlen=0;
65                 return 0;
66             }
67         }
68         fseek(fi, 4, SEEK_CUR);
69
70     } else {
71         fseek(fi, len+4, SEEK_CUR);
72     }
73     return 1;
74 }
75
76 static unsigned int png_get_dword(FILE*fi)
77 {
78     unsigned int a;
79     unsigned char b[4];
80     fread(&b,4,1,fi);
81     return b[0]<<24|b[1]<<16|b[2]<<8|b[3];
82 }
83
84 struct png_header
85 {
86     int width;
87     int height;
88     int bpp;
89     int mode;
90 };
91
92 static int png_read_header(FILE*fi, struct png_header*header)
93 {
94     char id[4];
95     int len;
96     int ok=0;
97     unsigned char head[8] = {137,80,78,71,13,10,26,10};
98     unsigned char head2[8];
99     unsigned char*data;
100     fread(head2,8,1,fi);
101     if(strncmp((const char*)head,(const char*)head2,4))
102         return 0;
103     
104     while(png_read_chunk(&id, &len, &data, fi))
105     {
106         //printf("Chunk: %c%c%c%c (len:%d)\n", id[0],id[1],id[2],id[3], len);
107         if(!strncmp(id, "IHDR", 4)) {
108             char a,b,c,f,i;
109             if(len < 8) exit(1);
110             header->width = data[0]<<24|data[1]<<16|data[2]<<8|data[3];
111             header->height = data[4]<<24|data[5]<<16|data[6]<<8|data[7];
112             a = data[8];      // should be 8
113             b = data[9];      // should be 3(indexed) or 2(rgb)
114
115             c = data[10];     // compression mode (0)
116             f = data[11];     // filter mode (0)
117             i = data[12];     // interlace mode (0)
118
119             if(b!=0 && b!=4 && b!=2 && b!=3 && b!=6) {
120                 fprintf(stderr, "Image mode %d not supported!\n", b);
121                 return 0;
122             }
123             if(a!=8 && (b==2 || b==6)) {
124                 printf("Bpp %d in mode %d not supported!\n", a);
125                 return 0;
126             }
127             if(c!=0) {
128                 printf("Compression mode %d not supported!\n", c);
129                 return 0;
130             }
131             if(f!=0) {
132                 printf("Filter mode %d not supported!\n", f);
133                 return 0;
134             }
135             if(i!=0) {
136                 printf("Interlace mode %d not supported!\n", i);
137                 return 0;
138             }
139             //printf("%dx%d bpp:%d mode:%d comp:%d filter:%d interlace:%d\n",header->width, header->height, a,b,c,f,i);
140             header->bpp = a;
141             header->mode = b;
142             ok = 1;
143         } 
144         
145         free(data);
146     }
147     return ok;
148 }
149
150 typedef unsigned char byte;
151 #define ABS(a) ((a)>0?(a):(-(a)))
152 static inline byte PaethPredictor (byte a,byte b,byte c)
153 {
154         // a = left, b = above, c = upper left
155         int p = a + b - c;        // initial estimate
156         int pa = ABS(p - a);      // distances to a, b, c
157         int pb = ABS(p - b);
158         int pc = ABS(p - c);
159         // return nearest of a,b,c,
160         // breaking ties in order a,b,c.
161         if (pa <= pb && pa <= pc) 
162                 return a;
163         else if (pb <= pc) 
164                 return b;
165         else return c;
166 }
167
168 static void applyfilter1(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width)
169 {
170     int x;
171     unsigned char last=0;
172     unsigned char upperlast=0;
173
174     if(mode==0) {
175         for(x=0;x<width;x++) {
176             *dest = *src;
177             dest++;
178             src++;
179         }
180     }
181     else if(mode==1) {
182         for(x=0;x<width;x++) {
183             *dest = *src+last;
184             last = *dest;
185             dest++;
186             src++;
187         }
188     }
189     else if(mode==2) {
190         for(x=0;x<width;x++) {
191             *dest = *src+*old;
192             dest++;
193             old++;
194             src++;
195         }
196     }
197     else if(mode==3) {
198         for(x=0;x<width;x++) {
199             *dest = *src+(*old+last)/2;
200             dest++;
201             old++;
202             src++;
203         }
204     }
205     else if(mode==4) {
206         for(x=0;x<width;x++) {
207             *dest = *src+PaethPredictor(last,*old,upperlast);
208             last = *dest;
209             upperlast = *old;
210             dest++;
211             old++;
212             src++;
213         }
214     }    
215
216 }
217
218 static void applyfilter2(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width)
219 {
220     int x;
221     unsigned char lasta=0;
222     unsigned char lastb=0;
223     unsigned char upperlasta=0;
224     unsigned char upperlastb=0;
225
226     if(mode==0) {
227         for(x=0;x<width;x++) {
228             dest[0] = src[0];
229             dest[1] = src[1];
230             dest+=2;
231             src+=2;
232         }
233     }
234     else if(mode==1) {
235         for(x=0;x<width;x++) {
236             dest[0] = src[0]+lasta;
237             dest[1] = src[1]+lastb;
238             lasta = dest[0];
239             lastb = dest[1];
240             dest+=2;
241             src+=2;
242         }
243     }
244     else if(mode==2) {
245         for(x=0;x<width;x++) {
246             dest[0] = src[0]+old[0];
247             dest[1] = src[1]+old[1];
248             dest+=2;
249             old+=2;
250             src+=2;
251         }
252     }
253     else if(mode==3) {
254         for(x=0;x<width;x++) {
255             dest[0] = src[0]+(old[0]+lasta)/2;
256             dest[1] = src[1]+(old[1]+lastb)/2;
257             lasta = dest[0];
258             lastb = dest[1];
259             dest+=2;
260             old+=2;
261             src+=2;
262         }
263     }
264     else if(mode==4) {
265         for(x=0;x<width;x++) {
266             dest[0] = src[0]+PaethPredictor(lasta,old[0],upperlasta);
267             dest[1] = src[1]+PaethPredictor(lastb,old[1],upperlastb);
268             lasta = dest[0];
269             lastb = dest[1];
270             upperlasta = old[0];
271             upperlastb = old[1];
272             dest+=2;
273             old+=2;
274             src+=2;
275         }
276     }    
277 }
278
279
280 /* also performs 24 bit conversion! */
281 static void applyfilter3(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width)
282 {
283     int x;
284     unsigned char lastr=0;
285     unsigned char lastg=0;
286     unsigned char lastb=0;
287     unsigned char upperlastr=0;
288     unsigned char upperlastg=0;
289     unsigned char upperlastb=0;
290
291     if(mode==0) {
292         for(x=0;x<width;x++) {
293             dest[0] = 255;
294             dest[1] = src[0];
295             dest[2] = src[1];
296             dest[3] = src[2];
297             dest+=4;
298             src+=3;
299         }
300     }
301     else if(mode==1) {
302         for(x=0;x<width;x++) {
303             dest[0] = 255;
304             dest[1] = src[0]+lastr;
305             dest[2] = src[1]+lastg;
306             dest[3] = src[2]+lastb;
307             lastr = dest[1];
308             lastg = dest[2];
309             lastb = dest[3];
310             dest+=4;
311             src+=3;
312         }
313     }
314     else if(mode==2) {
315         for(x=0;x<width;x++) {
316             dest[0] = 255;
317             dest[1] = src[0]+old[1];
318             dest[2] = src[1]+old[2];
319             dest[3] = src[2]+old[3];
320             dest+=4;
321             old+=4;
322             src+=3;
323         }
324     }
325     else if(mode==3) {
326         for(x=0;x<width;x++) {
327             dest[0] = 255;
328             dest[1] = src[0]+(old[1]+lastr)/2;
329             dest[2] = src[1]+(old[2]+lastg)/2;
330             dest[3] = src[2]+(old[3]+lastb)/2;
331             lastr = dest[1];
332             lastg = dest[2];
333             lastb = dest[3];
334             dest+=4;
335             old+=4;
336             src+=3;
337         }
338     }
339     else if(mode==4) {
340         for(x=0;x<width;x++) {
341             dest[0] = 255;
342             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
343             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
344             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
345             lastr = dest[1];
346             lastg = dest[2];
347             lastb = dest[3];
348             upperlastr = old[1];
349             upperlastg = old[2];
350             upperlastb = old[3];
351             dest+=4;
352             old+=4;
353             src+=3;
354         }
355     }    
356 }
357
358 static void inline applyfilter4(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width)
359 {
360     int x;
361     unsigned char lastr=0;
362     unsigned char lastg=0;
363     unsigned char lastb=0;
364     unsigned char lasta=0;
365     unsigned char upperlastr=0;
366     unsigned char upperlastg=0;
367     unsigned char upperlastb=0;
368     unsigned char upperlasta=0;
369
370     if(mode==0) {
371         for(x=0;x<width;x++) {
372             dest[0] = src[3];
373             dest[1] = src[0];
374             dest[2] = src[1];
375             dest[3] = src[2];
376             dest+=4;
377             src+=4;
378         }
379     }
380     else if(mode==1) {
381         for(x=0;x<width;x++) {
382             dest[0] = src[3]+lasta;
383             dest[1] = src[0]+lastr;
384             dest[2] = src[1]+lastg;
385             dest[3] = src[2]+lastb;
386             lasta = dest[0];
387             lastr = dest[1];
388             lastg = dest[2];
389             lastb = dest[3];
390             dest+=4;
391             src+=4;
392         }
393     }
394     else if(mode==2) {
395         for(x=0;x<width;x++) {
396             dest[0] = src[3]+old[0];
397             dest[1] = src[0]+old[1];
398             dest[2] = src[1]+old[2];
399             dest[3] = src[2]+old[3];
400             dest+=4;
401             old+=4;
402             src+=4;
403         }
404     }
405     else if(mode==3) {
406         for(x=0;x<width;x++) {
407             dest[0] = src[3]+(old[0]+lasta)/2;
408             dest[1] = src[0]+(old[1]+lastr)/2;
409             dest[2] = src[1]+(old[2]+lastg)/2;
410             dest[3] = src[2]+(old[3]+lastb)/2;
411             lasta = dest[0];
412             lastr = dest[1];
413             lastg = dest[2];
414             lastb = dest[3];
415             dest+=4;
416             old+=4;
417             src+=4;
418         }
419     }
420     else if(mode==4) {
421         for(x=0;x<width;x++) {
422             dest[0] = src[3]+PaethPredictor(lasta,old[0],upperlasta);
423             dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
424             dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
425             dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
426             lasta = dest[0];
427             lastr = dest[1];
428             lastg = dest[2];
429             lastb = dest[3];
430             upperlasta = old[0];
431             upperlastr = old[1];
432             upperlastg = old[2];
433             upperlastb = old[3];
434             dest+=4;
435             old+=4;
436             src+=4;
437         }
438     }    
439 }
440
441
442 EXPORT int getPNGdimensions(const char*sname, int*destwidth, int*destheight)
443 {
444     FILE*fi;
445     struct png_header header;
446     if ((fi = fopen(sname, "rb")) == NULL) {
447         fprintf(stderr, "Couldn't open %s\n", sname);
448         return 0;
449     }
450     if(!png_read_header(fi, &header)) {
451         fprintf(stderr, "Error reading header from file %s\n", sname);
452         return 0;
453     }
454
455     *destwidth = header.width;
456     *destheight = header.height;
457     return 1;
458 }
459
460 EXPORT int getPNG(const char*sname, int*destwidth, int*destheight, unsigned char**destdata)
461 {
462     char tagid[4];
463     int len;
464     unsigned char*data;
465     unsigned char*imagedata;
466     unsigned char*zimagedata=0;
467     unsigned long int imagedatalen;
468     unsigned long int zimagedatalen=0;
469     unsigned char*palette = 0;
470     int palettelen = 0;
471     unsigned char*alphapalette = 0;
472     int alphapalettelen = 0;
473     struct png_header header;
474     int bypp;
475     unsigned char*data2 = 0;
476     unsigned char alphacolor[3];
477     int hasalphacolor=0;
478
479     FILE *fi;
480     unsigned char *scanline;
481
482     if ((fi = fopen(sname, "rb")) == NULL) {
483         printf("Couldn't open %s\n", sname);
484         return 0;
485     }
486
487     if(!png_read_header(fi, &header)) {
488         printf("Error reading header from file %s\n", sname);
489         return 0;
490     }
491
492     if(header.mode == 3 || header.mode == 0) bypp = 1;
493     else if(header.mode == 4) bypp = 2;
494     else if(header.mode == 2) bypp = 3;
495     else if(header.mode == 6) bypp = 4;
496     else {
497         printf("ERROR: mode:%d\n", header.mode);
498         return 0;
499     }
500
501     imagedatalen = bypp * header.width * header.height + 65536;
502     imagedata = (unsigned char*)malloc(imagedatalen);
503
504     fseek(fi,8,SEEK_SET);
505     while(!feof(fi))
506     {
507         if(!png_read_chunk(&tagid, &len, &data, fi))
508             break;
509         if(!strncmp(tagid, "IEND", 4)) {
510             break;
511         }
512         if(!strncmp(tagid, "PLTE", 4)) {
513             palette = data;
514             palettelen = len/3;
515             data = 0; //don't free data
516             //printf("%d colors in palette\n", palettelen);
517         }
518         if(!strncmp(tagid, "tRNS", 4)) {
519             if(header.mode == 3) {
520                 alphapalette = data;
521                 alphapalettelen = len;
522                 data = 0; //don't free data
523                 //printf("found %d alpha colors\n", alphapalettelen);
524             } else if(header.mode == 0 || header.mode == 2) {
525                 int t;
526                 if(header.mode == 2) {
527                     alphacolor[0] = data[1];
528                     alphacolor[1] = data[3];
529                     alphacolor[2] = data[5];
530                 } else {
531                     alphacolor[0] = alphacolor[1] = alphacolor[2] = data[1];
532                 }
533                 hasalphacolor = 1;
534             }
535         }
536         if(!strncmp(tagid, "IDAT", 4)) {
537             if(!zimagedata) {
538                 zimagedatalen = len;
539                 zimagedata = (unsigned char*)malloc(len);
540                 memcpy(zimagedata,data,len);
541             } else {
542                 zimagedata = (unsigned char*)realloc(zimagedata, zimagedatalen+len);
543                 memcpy(&zimagedata[zimagedatalen], data, len);
544                 zimagedatalen += len;
545             }
546         }
547         if(!strncmp(tagid, "tEXt", 4)) {
548             /*int t;
549             printf("Image Text: ");
550             for(t=0;t<len;t++) {
551                 if(data[t]>=32 && data[t]<128)
552                     printf("%c", data[t]);
553                 else
554                     printf("?");
555             }
556             printf("\n");*/
557         }
558         if(data) {
559             free(data); data=0;
560         }
561     }
562     
563     if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
564         printf("Couldn't uncompress %s!\n", sname);
565         if(zimagedata)
566             free(zimagedata);
567         return 0;
568     }
569     free(zimagedata);
570     fclose(fi);
571
572     *destwidth = header.width;
573     *destheight = header.height;
574         
575     data2 = (unsigned char*)malloc(header.width*header.height*4);
576
577     if(header.mode == 4)
578     {
579         int i,s=0;
580         int x,y;
581         int pos=0;
582         unsigned char* old= (unsigned char*)malloc(header.width*2);
583         memset(old, 0, header.width*2);
584         *destdata = data2;
585         for(y=0;y<header.height;y++) {
586             int mode = imagedata[pos++]; //filter mode
587             unsigned char*src;
588             unsigned char*dest;
589             int x;
590             dest = &data2[(y*header.width)*4];
591
592             if(header.bpp == 8) {
593                 /* one byte per pixel */
594                 src = &imagedata[pos];
595                 pos+=header.width*2;
596             } else {
597                 /* not implemented yet */
598                 fprintf(stderr, "ERROR: mode=4 bpp:%d\n", header.bpp);
599                 free(data2);
600                 return 0;
601             }
602
603             applyfilter2(mode, src, old, dest, header.width);
604             memcpy(old, dest, header.width*2);
605
606             for(x=header.width-1;x>=0;x--) {
607                 unsigned char gray = dest[x*2+0];
608                 unsigned char alpha = dest[x*2+1];
609                 dest[x*4+0] = alpha;
610                 dest[x*4+1] = gray;
611                 dest[x*4+2] = gray;
612                 dest[x*4+3] = gray;
613             }
614         }
615         free(old);
616         free(imagedata);
617     } else if(header.mode == 6 || header.mode == 2) {
618         int i,s=0;
619         int x,y;
620         int pos=0;
621         *destdata = data2;
622         
623         unsigned char* firstline = malloc(header.width*4);
624         memset(firstline,0,header.width*4);
625         for(y=0;y<header.height;y++) {
626             int mode = imagedata[pos++]; //filter mode
627             unsigned char*src;
628             unsigned char*dest;
629             unsigned char*old;
630             dest = &data2[(y*header.width)*4];
631
632             if(header.bpp == 8)
633             {
634                 /* one byte per pixel */
635                 src = &imagedata[pos];
636                 pos+=header.width*(header.mode==6?4:3);
637             } else {
638                 /* not implemented yet */
639                 fprintf(stderr, "ERROR: bpp:%d\n", header.bpp);
640                 free(data2);
641                 return 0;
642             }
643
644             if(!y) {
645                 old = firstline;
646             } else {
647                 old = &data2[(y-1)*header.width*4];
648             }
649             if(header.mode == 6) { 
650                 applyfilter4(mode, src, old, dest, header.width);
651             } else { // header.mode = 2
652                 applyfilter3(mode, src, old, dest, header.width);
653                 /* replace alpha color */
654                 if(hasalphacolor) {
655                     int x;
656                     for(x=0;x<header.width;x++) {
657                         if(dest[x*4+1] == alphacolor[0] &&
658                            dest[x*4+2] == alphacolor[1] &&
659                            dest[x*4+3] == alphacolor[2]) {
660                             *(u32*)&dest[x*4] = 0;
661                         }
662                     }
663                 }
664             }
665         }
666         free(firstline);
667         free(imagedata);
668     } else if(header.mode == 0 || header.mode == 3) {
669         COL*rgba = 0;
670         unsigned char*tmpline = (unsigned char*)malloc(header.width+1);
671         unsigned char*destline = (unsigned char*)malloc(header.width+1);
672         int i,x,y;
673         int pos=0;
674         
675         *destdata = data2;
676         
677         if(header.mode == 0) { // grayscale palette
678             int mult = (0x1ff>>header.bpp);
679             palettelen = 1<<header.bpp;
680             rgba = (COL*)malloc(palettelen*sizeof(COL));
681             for(i=0;i<palettelen;i++) {
682                 rgba[i].a = 255;
683                 rgba[i].r = i*mult;
684                 rgba[i].g = i*mult;
685                 rgba[i].b = i*mult;
686                 if(hasalphacolor) {
687                     if(rgba[i].r == alphacolor[0])
688                         rgba[i].a = 0;
689                 }
690             }
691         } else {
692             if(!palette) {
693                 fprintf(stderr, "Error: No palette found!\n");
694                 exit(1);
695             }
696             rgba = (COL*)malloc(palettelen*4);
697             /* 24->32 bit conversion */
698             for(i=0;i<palettelen;i++) {
699                 rgba[i].r = palette[i*3+0];
700                 rgba[i].g = palette[i*3+1];
701                 rgba[i].b = palette[i*3+2];
702                 if(alphapalette && i<alphapalettelen) {
703                     rgba[i].a = alphapalette[i];
704                     /*rgba[i].r = ((int)rgba[i].r*rgba[i].a)/255;
705                     rgba[i].g = ((int)rgba[i].g*rgba[i].a)/255;
706                     rgba[i].b = ((int)rgba[i].b*rgba[i].a)/255;*/
707                 } else {
708                     rgba[i].a = 255;
709                 }
710                 if(hasalphacolor) {
711                     if(rgba[i].r == alphacolor[0] &&
712                        rgba[i].g == alphacolor[1] &&
713                        rgba[i].b == alphacolor[2])
714                         rgba[i].a = 0;
715                 }
716             }
717         }
718
719         for(y=0;y<header.height;y++) {
720             int mode = imagedata[pos++]; //filter mode
721             int x;
722             unsigned char*old;
723             unsigned char*src;
724             src = &imagedata[pos];
725             if(header.bpp == 8) {
726                 pos+=header.width;
727             } else {
728                 int x,s=0;
729                 int bitpos = 0;
730                 u32 v = (1<<header.bpp)-1;
731                 for(x=0;x<header.width;x++) {
732                     u32 r = src[s/8]<<8 | 
733                             src[s/8+1];
734                     int t;
735                     tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
736                     s+=header.bpp;
737                 }
738                 src = tmpline;
739                 pos+=(header.width*header.bpp+7)/8;
740             }
741
742             if(!y) {
743                 memset(destline,0,header.width);
744                 old = &destline[y*header.width];
745             } else {
746                 old = tmpline;
747             }
748             applyfilter1(mode, src, old, destline, header.width);
749             memcpy(tmpline,destline,header.width);
750             for(x=0;x<header.width;x++) {
751                 *(COL*)&data2[y*header.width*4+x*4+0] = rgba[destline[x]];
752             }
753         }
754         free(tmpline);
755         free(destline);
756         free(rgba);
757         free(imagedata);
758     } else {
759         printf("expected PNG mode to be 2, 3 or 6 (is:%d)\n", header.mode);
760         return 0;
761     }
762
763     return 1;
764 }
765
766 static u32 mycrc32;
767
768 static u32*crc32_table = 0;
769 static void make_crc32_table(void)
770 {
771   int t;
772   if(crc32_table) 
773       return;
774   crc32_table = (u32*)malloc(1024);
775
776   for (t = 0; t < 256; t++) {
777     u32 c = t;
778     int s;
779     for (s = 0; s < 8; s++) {
780       c = (0xedb88320L*(c&1)) ^ (c >> 1);
781     }
782     crc32_table[t] = c;
783   }
784 }
785 static inline void png_write_byte(FILE*fi, unsigned char byte)
786 {
787     fwrite(&byte,1,1,fi);
788     mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
789 }
790 static long png_start_chunk(FILE*fi, char*type, int len)
791 {
792     unsigned char mytype[4]={0,0,0,0};
793     unsigned char mylen[4];
794     long filepos;
795     mylen[0] = len>>24;
796     mylen[1] = len>>16;
797     mylen[2] = len>>8;
798     mylen[3] = len;
799     memcpy(mytype,type,strlen(type));
800     filepos = ftell(fi);
801     fwrite(&mylen, 4, 1, fi);
802     mycrc32=0xffffffff;
803     png_write_byte(fi,mytype[0]);
804     png_write_byte(fi,mytype[1]);
805     png_write_byte(fi,mytype[2]);
806     png_write_byte(fi,mytype[3]);
807     return filepos;
808 }
809 static void png_patch_len(FILE*fi, int pos, int len)
810 {
811     unsigned char mylen[4];
812     long filepos;
813     mylen[0] = len>>24;
814     mylen[1] = len>>16;
815     mylen[2] = len>>8;
816     mylen[3] = len;
817     fseek(fi, pos, SEEK_SET);
818     fwrite(&mylen, 4, 1, fi);
819     fseek(fi, 0, SEEK_END);
820 }
821 static void png_write_bytes(FILE*fi, unsigned char*bytes, int len)
822 {
823     int t;
824     for(t=0;t<len;t++)
825         png_write_byte(fi,bytes[t]);
826 }
827 static void png_write_dword(FILE*fi, u32 dword)
828 {
829     png_write_byte(fi,dword>>24);
830     png_write_byte(fi,dword>>16);
831     png_write_byte(fi,dword>>8);
832     png_write_byte(fi,dword);
833 }
834 static void png_end_chunk(FILE*fi)
835 {
836     u32 tmp = mycrc32^0xffffffff;
837     unsigned char tmp2[4];
838     tmp2[0] = tmp>>24;
839     tmp2[1] = tmp>>16;
840     tmp2[2] = tmp>>8;
841     tmp2[3] = tmp;
842     fwrite(&tmp2,4,1,fi);
843 }
844
845 #define ZLIB_BUFFER_SIZE 16384
846
847 static long compress_line(z_stream*zs, Bytef*line, int len, FILE*fi)
848 {
849     long size = 0;
850     zs->next_in = line;
851     zs->avail_in = len;
852
853     while(1) {
854         int ret = deflate(zs, Z_NO_FLUSH);
855         if (ret != Z_OK) {
856             fprintf(stderr, "error in deflate(): %s", zs->msg?zs->msg:"unknown");
857             return 0;
858         }
859         if(zs->avail_out != ZLIB_BUFFER_SIZE) {
860             int consumed = ZLIB_BUFFER_SIZE - zs->avail_out;
861             size += consumed;
862             png_write_bytes(fi, zs->next_out - consumed , consumed);
863             zs->next_out = zs->next_out - consumed;
864             zs->avail_out = ZLIB_BUFFER_SIZE;
865         }
866         if(!zs->avail_in) {
867             break;
868         }
869     }
870     return size;
871 }
872
873 static int test_line(z_stream*zs_orig, Bytef*line, int linelen)
874 {
875     z_stream zs;
876     int ret = deflateCopy(&zs, zs_orig);
877     if(ret != Z_OK) {
878         fprintf(stderr, "Couldn't copy stream\n");
879         return 0;
880     }
881
882     zs.next_in = line;
883     zs.avail_in = linelen;
884
885     long size = 0;
886
887     int mode = Z_SYNC_FLUSH;
888     while(1) {
889         int ret = deflate(&zs, mode);
890         if (ret != Z_OK && ret != Z_STREAM_END) {
891             fprintf(stderr, "error in deflate(): %s (mode %s, %d bytes remaining)\n", zs.msg?zs.msg:"unknown", 
892                     mode==Z_SYNC_FLUSH?"Z_SYNC_FLUSH":"Z_FINISH", zs.avail_in);
893             return 0;
894         }
895         if(zs.avail_out != ZLIB_BUFFER_SIZE) {
896             int consumed = ZLIB_BUFFER_SIZE - zs.avail_out;
897             size += consumed;
898             zs.next_out = zs.next_out - consumed;
899             zs.avail_out = ZLIB_BUFFER_SIZE;
900         }
901         if (ret == Z_STREAM_END) {
902             break;
903         }
904         if(!zs.avail_in) {
905             mode = Z_FINISH;
906         }
907     }
908     ret = deflateEnd(&zs);
909     if (ret != Z_OK) {
910         fprintf(stderr, "error in deflateEnd(): %s\n", zs.msg?zs.msg:"unknown");
911         return 0;
912     }
913     return size;
914 }
915
916 static int finishzlib(z_stream*zs, FILE*fi)
917 {
918     int size = 0;
919     int ret;
920     while(1) {
921         ret = deflate(zs, Z_FINISH);
922         if (ret != Z_OK &&
923             ret != Z_STREAM_END) {
924             fprintf(stderr, "error in deflate(finish): %s\n", zs->msg?zs->msg:"unknown");
925             return 0;
926         }
927
928         if(zs->avail_out != ZLIB_BUFFER_SIZE) {
929             int consumed = ZLIB_BUFFER_SIZE - zs->avail_out;
930             size += consumed;
931             png_write_bytes(fi, zs->next_out - consumed , consumed);
932             zs->next_out = zs->next_out - consumed;
933             zs->avail_out = ZLIB_BUFFER_SIZE;
934         }
935         if (ret == Z_STREAM_END) {
936             break;
937         }
938     }
939     ret = deflateEnd(zs);
940     if (ret != Z_OK) {
941         fprintf(stderr, "error in deflateEnd(): %s\n", zs->msg?zs->msg:"unknown");
942         return 0;
943     }
944     return size;
945 }
946
947 static void filter_line(int filtermode, unsigned char*dest, unsigned char*src, int width)
948 {
949     int pos2 = 0;
950     int pos = 0;
951     int srcwidth = width*4;
952     int x;
953     if(filtermode == 0) {
954         for(x=0;x<width;x++) {
955             dest[pos2++]=src[pos+1];
956             dest[pos2++]=src[pos+2];
957             dest[pos2++]=src[pos+3];
958             dest[pos2++]=src[pos+0]; //alpha
959             pos+=4;
960         }
961     } else if(filtermode == 1) {
962         /* x difference filter */
963         dest[pos2++]=src[pos+1];
964         dest[pos2++]=src[pos+2];
965         dest[pos2++]=src[pos+3];
966         dest[pos2++]=src[pos+0];
967         pos+=4;
968         for(x=1;x<width;x++) {
969             dest[pos2++]=src[pos+1] - src[pos-4+1];
970             dest[pos2++]=src[pos+2] - src[pos-4+2];
971             dest[pos2++]=src[pos+3] - src[pos-4+3];
972             dest[pos2++]=src[pos+0] - src[pos-4+0]; //alpha
973             pos+=4;
974         }
975     } else if(filtermode == 2) {
976         /* y difference filter */
977         for(x=0;x<width;x++) {
978             dest[pos2++]=src[pos+1] - src[pos-srcwidth+1];
979             dest[pos2++]=src[pos+2] - src[pos-srcwidth+2];
980             dest[pos2++]=src[pos+3] - src[pos-srcwidth+3];
981             dest[pos2++]=src[pos+0] - src[pos-srcwidth+0]; //alpha
982             pos+=4;
983         }
984     } else if(filtermode == 3) {
985         dest[pos2++]=src[pos+1] - src[pos-srcwidth+1]/2;
986         dest[pos2++]=src[pos+2] - src[pos-srcwidth+2]/2;
987         dest[pos2++]=src[pos+3] - src[pos-srcwidth+3]/2;
988         dest[pos2++]=src[pos+0] - src[pos-srcwidth+0]/2;
989         pos+=4;
990         /* x+y difference filter */
991         for(x=1;x<width;x++) {
992             dest[pos2++]=src[pos+1] - (src[pos-4+1] + src[pos-srcwidth+1])/2;
993             dest[pos2++]=src[pos+2] - (src[pos-4+2] + src[pos-srcwidth+2])/2;
994             dest[pos2++]=src[pos+3] - (src[pos-4+3] + src[pos-srcwidth+3])/2;
995             dest[pos2++]=src[pos+0] - (src[pos-4+0] + src[pos-srcwidth+0])/2; //alpha
996             pos+=4;
997         }
998     } else if(filtermode == 4) {
999         dest[pos2++]=src[pos+1] - PaethPredictor(0, src[pos-srcwidth+1], 0);
1000         dest[pos2++]=src[pos+2] - PaethPredictor(0, src[pos-srcwidth+2], 0);
1001         dest[pos2++]=src[pos+3] - PaethPredictor(0, src[pos-srcwidth+3], 0);
1002         dest[pos2++]=src[pos+0] - PaethPredictor(0, src[pos-srcwidth+0], 0);
1003         pos+=4;
1004         /* paeth difference filter */
1005         for(x=1;x<width;x++) {
1006             dest[pos2++]=src[pos+1] - PaethPredictor(src[pos-4+1], src[pos-srcwidth+1], src[pos-4-srcwidth+1]);
1007             dest[pos2++]=src[pos+2] - PaethPredictor(src[pos-4+2], src[pos-srcwidth+2], src[pos-4-srcwidth+2]);
1008             dest[pos2++]=src[pos+3] - PaethPredictor(src[pos-4+3], src[pos-srcwidth+3], src[pos-4-srcwidth+3]);
1009             dest[pos2++]=src[pos+0] - PaethPredictor(src[pos-4+0], src[pos-srcwidth+0], src[pos-4-srcwidth+0]);
1010             pos+=4;
1011         }
1012     }
1013 }
1014
1015 EXPORT void writePNG(const char*filename, unsigned char*data, int width, int height)
1016 {
1017     FILE*fi;
1018     int crc;
1019     int t;
1020     unsigned char format;
1021     unsigned char tmp;
1022     unsigned char* data2=0;
1023     u32 datalen;
1024     u32 datalen2;
1025     unsigned char head[] = {137,80,78,71,13,10,26,10}; // PNG header
1026     int cols;
1027     char alpha = 1;
1028     int pos = 0;
1029     int error;
1030     u32 tmp32;
1031     int bpp;
1032     int ret;
1033     z_stream zs;
1034
1035     make_crc32_table();
1036
1037     bpp = 32;
1038     cols = 0;
1039     format = 5;
1040
1041     datalen = (width*height*bpp/8+cols*8);
1042     
1043     fi = fopen(filename, "wb");
1044     if(!fi) {
1045         perror("open");
1046         return;
1047     }
1048     fwrite(head,sizeof(head),1,fi);     
1049
1050     png_start_chunk(fi, "IHDR", 13);
1051      png_write_dword(fi,width);
1052      png_write_dword(fi,height);
1053      png_write_byte(fi,8);
1054      if(format == 3)
1055      png_write_byte(fi,3); //indexed
1056      else if(format == 5 && alpha==0)
1057      png_write_byte(fi,2); //rgb
1058      else if(format == 5 && alpha==1)
1059      png_write_byte(fi,6); //rgba
1060      else return;
1061
1062      png_write_byte(fi,0); //compression mode
1063      png_write_byte(fi,0); //filter mode
1064      png_write_byte(fi,0); //interlace mode
1065     png_end_chunk(fi);
1066
1067 /*    if(format == 3) {
1068         png_start_chunk(fi, "PLTE", 768);
1069          
1070          for(t=0;t<256;t++) {
1071              png_write_byte(fi,palette[t].r);
1072              png_write_byte(fi,palette[t].g);
1073              png_write_byte(fi,palette[t].b);
1074          }
1075         png_end_chunk(fi);
1076     }*/
1077     long idatpos = png_start_chunk(fi, "IDAT", 0);
1078     
1079     memset(&zs,0,sizeof(z_stream));
1080     Bytef*writebuf = (Bytef*)malloc(ZLIB_BUFFER_SIZE);
1081     zs.zalloc = Z_NULL;
1082     zs.zfree  = Z_NULL;
1083     zs.opaque = Z_NULL;
1084     zs.next_out = writebuf;
1085     zs.avail_out = ZLIB_BUFFER_SIZE;
1086     ret = deflateInit(&zs, 9);
1087     if (ret != Z_OK) {
1088         fprintf(stderr, "error in deflateInit(): %s", zs.msg?zs.msg:"unknown");
1089         return;
1090     }
1091
1092     long idatsize = 0;
1093     {
1094         int x,y;
1095         int srcwidth = width * (bpp/8);
1096         int linelen = 1 + ((srcwidth+3)&~3);
1097         unsigned char* line = (unsigned char*)malloc(linelen);
1098         unsigned char* bestline = (unsigned char*)malloc(linelen);
1099         memset(line, 0, linelen);
1100         for(y=0;y<height;y++)
1101         {
1102             int filtermode;
1103             int bestsize = 0x7fffffff;
1104             for(filtermode=0;filtermode<5;filtermode++) {
1105
1106                 if(!y && filtermode>=2)
1107                     continue; // don't do y direction filters in the first row
1108                 
1109                 line[0]=filtermode; //filter type
1110                 filter_line(filtermode, line+1, &data[y*srcwidth], width);
1111
1112                 int size = test_line(&zs, line, linelen);
1113                 if(size < bestsize) {
1114                     memcpy(bestline, line, linelen);
1115                     bestsize = size;
1116                 }
1117             }
1118             idatsize += compress_line(&zs, bestline, linelen, fi);
1119         }
1120         free(line);free(bestline);
1121     }
1122     idatsize += finishzlib(&zs, fi);
1123     png_patch_len(fi, idatpos, idatsize);
1124     png_end_chunk(fi);
1125
1126     png_start_chunk(fi, "IEND", 0);
1127     png_end_chunk(fi);
1128
1129     free(writebuf);
1130     free(data2);
1131     fclose(fi);
1132 }
1133