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