3 Copyright (c) 2003/2004/2005 Matthias Kramm <kramm@quiss.org>
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.
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.
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 */
27 typedef unsigned char U8;
28 typedef unsigned long U32;
33 int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
36 unsigned char blen[4];
37 if(destlen) *destlen=0;
38 if(destdata) *destdata=0;
39 if(!fread(&blen, 4, 1, fi)) {
42 if(!fread(head, 4, 1, fi)) {
45 len = blen[0]<<24|blen[1]<<16|blen[2]<<8|blen[3];
46 if(destlen) *destlen = len;
51 *destdata = (U8*)malloc(len);
52 if(!fread(*destdata, len, 1, fi)) {
54 if(destlen) *destlen=0;
58 fseek(fi, 4, SEEK_CUR);
61 fseek(fi, len+4, SEEK_CUR);
66 unsigned int png_get_dword(FILE*fi)
71 return b[0]<<24|b[1]<<16|b[2]<<8|b[3];
82 int png_read_header(FILE*fi, struct png_header*header)
87 U8 head[8] = {137,80,78,71,13,10,26,10};
91 if(strncmp((const char*)head,(const char*)head2,4))
94 while(png_read_chunk(&id, &len, &data, fi))
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)) {
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)
105 c = data[10]; // compression mode (0)
106 f = data[11]; // filter mode (0)
107 i = data[12]; // interlace mode (0)
109 if(b!=0 && b!=4 && b!=2 && b!=3 && b!=6) {
110 fprintf(stderr, "Image mode %d not supported!\n", b);
113 if(a!=8 && (b==2 || b==6)) {
114 printf("Bpp %d in mode %d not supported!\n", a);
118 printf("Compression mode %d not supported!\n", c);
122 printf("Filter mode %d not supported!\n", f);
126 printf("Interlace mode %d not supported!\n", i);
129 //printf("%dx%d bpp:%d mode:%d comp:%d filter:%d interlace:%d\n",header->width, header->height, a,b,c,f,i);
140 typedef unsigned char byte;
141 #define ABS(a) ((a)>0?(a):(-(a)))
142 byte inline PaethPredictor (byte a,byte b,byte c)
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
149 // return nearest of a,b,c,
150 // breaking ties in order a,b,c.
151 if (pa <= pb && pa <= pc)
158 void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width)
161 unsigned char last=0;
162 unsigned char upperlast=0;
165 for(x=0;x<width;x++) {
172 for(x=0;x<width;x++) {
180 for(x=0;x<width;x++) {
188 for(x=0;x<width;x++) {
189 *dest = *src+(*old+last)/2;
196 for(x=0;x<width;x++) {
197 *dest = *src+PaethPredictor(last,*old,upperlast);
208 void applyfilter2(int mode, U8*src, U8*old, U8*dest, int width)
211 unsigned char lasta=0;
212 unsigned char lastb=0;
213 unsigned char upperlasta=0;
214 unsigned char upperlastb=0;
217 for(x=0;x<width;x++) {
225 for(x=0;x<width;x++) {
226 dest[0] = src[0]+lasta;
227 dest[1] = src[1]+lastb;
235 for(x=0;x<width;x++) {
236 dest[0] = src[0]+old[0];
237 dest[1] = src[1]+old[1];
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;
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);
270 /* also performs 24 bit conversion! */
271 void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width)
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;
282 for(x=0;x<width;x++) {
292 for(x=0;x<width;x++) {
294 dest[1] = src[0]+lastr;
295 dest[2] = src[1]+lastg;
296 dest[3] = src[2]+lastb;
305 for(x=0;x<width;x++) {
307 dest[1] = src[0]+old[1];
308 dest[2] = src[1]+old[2];
309 dest[3] = src[2]+old[3];
316 for(x=0;x<width;x++) {
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;
330 for(x=0;x<width;x++) {
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);
348 void inline applyfilter4(int mode, U8*src, U8*old, U8*dest, int width)
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;
361 for(x=0;x<width;x++) {
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;
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];
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;
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);
432 int getPNGdimensions(char*sname, int*destwidth, int*destheight)
435 struct png_header header;
436 if ((fi = fopen(sname, "rb")) == NULL) {
437 fprintf(stderr, "Couldn't open %s\n", sname);
440 if(!png_read_header(fi, &header)) {
441 fprintf(stderr, "Error reading header from file %s\n", sname);
445 *destwidth = header.width;
446 *destheight = header.height;
450 int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata)
457 unsigned long int imagedatalen;
458 unsigned long int zimagedatalen=0;
462 int alphapalettelen = 0;
463 struct png_header header;
472 if ((fi = fopen(sname, "rb")) == NULL) {
473 printf("Couldn't open %s\n", sname);
477 if(!png_read_header(fi, &header)) {
478 printf("Error reading header from file %s\n", sname);
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;
487 printf("ERROR: mode:%d\n", header.mode);
491 imagedatalen = bypp * header.width * header.height + 65536;
492 imagedata = (U8*)malloc(imagedatalen);
494 fseek(fi,8,SEEK_SET);
497 if(!png_read_chunk(&tagid, &len, &data, fi))
499 if(!strncmp(tagid, "IEND", 4)) {
502 if(!strncmp(tagid, "PLTE", 4)) {
505 data = 0; //don't free data
506 //printf("%d colors in palette\n", palettelen);
508 if(!strncmp(tagid, "tRNS", 4)) {
509 if(header.mode == 3) {
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) {
516 if(header.mode == 2) {
517 alphacolor[0] = data[1];
518 alphacolor[1] = data[3];
519 alphacolor[2] = data[5];
521 alphacolor[0] = alphacolor[1] = alphacolor[2] = data[1];
526 if(!strncmp(tagid, "IDAT", 4)) {
529 zimagedata = (U8*)malloc(len);
530 memcpy(zimagedata,data,len);
532 zimagedata = (U8*)realloc(zimagedata, zimagedatalen+len);
533 memcpy(&zimagedata[zimagedatalen], data, len);
534 zimagedatalen += len;
537 if(!strncmp(tagid, "tEXt", 4)) {
539 printf("Image Text: ");
541 if(data[t]>=32 && data[t]<128)
542 printf("%c", data[t]);
553 if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
554 printf("Couldn't uncompress %s!\n", sname);
562 *destwidth = header.width;
563 *destheight = header.height;
565 data2 = (U8*)malloc(header.width*header.height*4);
572 U8* old= (U8*)malloc(header.width*2);
573 memset(old, 0, header.width*2);
575 for(y=0;y<header.height;y++) {
576 int mode = imagedata[pos++]; //filter mode
580 dest = &data2[(y*header.width)*4];
582 if(header.bpp == 8) {
583 /* one byte per pixel */
584 src = &imagedata[pos];
587 /* not implemented yet */
588 fprintf(stderr, "ERROR: mode=4 bpp:%d\n", header.bpp);
593 applyfilter2(mode, src, old, dest, header.width);
594 memcpy(old, dest, header.width*2);
596 for(x=header.width-1;x>=0;x--) {
597 U8 gray = dest[x*2+0];
598 U8 alpha = dest[x*2+1];
607 } else if(header.mode == 6 || header.mode == 2) {
612 for(y=0;y<header.height;y++) {
613 int mode = imagedata[pos++]; //filter mode
617 dest = &data2[(y*header.width)*4];
621 /* one byte per pixel */
622 src = &imagedata[pos];
623 pos+=header.width*(header.mode==6?4:3);
625 /* not implemented yet */
626 fprintf(stderr, "ERROR: bpp:%d\n", header.bpp);
632 memset(data2,0,header.width*4);
633 old = &data2[y*header.width*4];
635 old = &data2[(y-1)*header.width*4];
637 if(header.mode == 6) {
638 applyfilter4(mode, src, old, dest, header.width);
639 } else { // header.mode = 2
640 applyfilter3(mode, src, old, dest, header.width);
641 /* replace alpha color */
644 for(x=0;x<header.width;x++) {
645 if(dest[x*4+1] == alphacolor[0] &&
646 dest[x*4+2] == alphacolor[1] &&
647 dest[x*4+3] == alphacolor[2]) {
648 *(U32*)&dest[x*4] = 0;
655 } else if(header.mode == 0 || header.mode == 3) {
657 U8*tmpline = (U8*)malloc(header.width+1);
658 U8*destline = (U8*)malloc(header.width+1);
664 if(header.mode == 0) { // grayscale palette
665 int mult = (0x1ff>>header.bpp);
666 palettelen = 1<<header.bpp;
667 rgba = (COL*)malloc(palettelen*sizeof(COL));
668 for(i=0;i<palettelen;i++) {
674 if(rgba[i].r == alphacolor[0])
680 fprintf(stderr, "Error: No palette found!\n");
683 rgba = (COL*)malloc(palettelen*4);
684 /* 24->32 bit conversion */
685 for(i=0;i<palettelen;i++) {
686 rgba[i].r = palette[i*3+0];
687 rgba[i].g = palette[i*3+1];
688 rgba[i].b = palette[i*3+2];
689 if(alphapalette && i<alphapalettelen) {
690 rgba[i].a = alphapalette[i];
691 /*rgba[i].r = ((int)rgba[i].r*rgba[i].a)/255;
692 rgba[i].g = ((int)rgba[i].g*rgba[i].a)/255;
693 rgba[i].b = ((int)rgba[i].b*rgba[i].a)/255;*/
698 if(rgba[i].r == alphacolor[0] &&
699 rgba[i].g == alphacolor[1] &&
700 rgba[i].b == alphacolor[2])
706 for(y=0;y<header.height;y++) {
707 int mode = imagedata[pos++]; //filter mode
711 src = &imagedata[pos];
712 if(header.bpp == 8) {
717 U32 v = (1<<header.bpp)-1;
718 for(x=0;x<header.width;x++) {
719 U32 r = src[s/8]<<8 |
722 tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
726 pos+=(header.width*header.bpp+7)/8;
730 memset(destline,0,header.width);
731 old = &destline[y*header.width];
735 applyfilter1(mode, src, old, destline, header.width);
736 memcpy(tmpline,destline,header.width);
737 for(x=0;x<header.width;x++) {
738 *(COL*)&data2[y*header.width*4+x*4+0] = rgba[destline[x]];
746 printf("expected PNG mode to be 2, 3 or 6 (is:%d)\n", header.mode);
755 static U32*crc32_table = 0;
756 static void make_crc32_table(void)
761 crc32_table = (U32*)malloc(1024);
763 for (t = 0; t < 256; t++) {
766 for (s = 0; s < 8; s++) {
767 c = (0xedb88320L*(c&1)) ^ (c >> 1);
772 static inline void png_write_byte(FILE*fi, U8 byte)
774 fwrite(&byte,1,1,fi);
775 mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
777 static void png_start_chunk(FILE*fi, char*type, int len)
779 U8 mytype[4]={0,0,0,0};
785 memcpy(mytype,type,strlen(type));
786 fwrite(&mylen, 4, 1, fi);
788 png_write_byte(fi,mytype[0]);
789 png_write_byte(fi,mytype[1]);
790 png_write_byte(fi,mytype[2]);
791 png_write_byte(fi,mytype[3]);
793 static void png_write_bytes(FILE*fi, U8*bytes, int len)
797 png_write_byte(fi,bytes[t]);
799 static void png_write_dword(FILE*fi, U32 dword)
801 png_write_byte(fi,dword>>24);
802 png_write_byte(fi,dword>>16);
803 png_write_byte(fi,dword>>8);
804 png_write_byte(fi,dword);
806 static void png_end_chunk(FILE*fi)
808 U32 tmp = mycrc32^0xffffffff;
814 fwrite(&tmp2,4,1,fi);
817 void writePNG(char*filename, unsigned char*data, int width, int height)
829 U8 head[] = {137,80,78,71,13,10,26,10}; // PNG header
844 datalen = (width*height*bpp/8+cols*8);
846 fi = fopen(filename, "wb");
851 fwrite(head,sizeof(head),1,fi);
853 png_start_chunk(fi, "IHDR", 13);
854 png_write_dword(fi,width);
855 png_write_dword(fi,height);
856 png_write_byte(fi,8);
858 png_write_byte(fi,3); //indexed
859 else if(format == 5 && alpha==0)
860 png_write_byte(fi,2); //rgb
861 else if(format == 5 && alpha==1)
862 png_write_byte(fi,6); //rgba
865 png_write_byte(fi,0); //compression mode
866 png_write_byte(fi,0); //filter mode
867 png_write_byte(fi,0); //interlace mode
871 png_start_chunk(fi, "PLTE", 768);
874 png_write_byte(fi,palette[t].r);
875 png_write_byte(fi,palette[t].g);
876 png_write_byte(fi,palette[t].b);
883 int srcwidth = width * (bpp/8);
884 datalen3 = (width*4+5)*height;
885 data3 = (U8*)malloc(datalen3);
886 for(y=0;y<height;y++)
888 data3[pos2++]=0; //filter type
889 for(x=0;x<width;x++) {
890 data3[pos2++]=data[pos+1];
891 data3[pos2++]=data[pos+2];
892 data3[pos2++]=data[pos+3];
893 data3[pos2++]=data[pos+0]; //alpha
896 pos+=((srcwidth+3)&~3)-srcwidth; //align
902 data2 = (U8*)malloc(datalen2);
904 if((ret = compress (data2, &datalen2, data3, datalen3)) != Z_OK) {
905 fprintf(stderr, "zlib error in pic %d\n", ret);
908 png_start_chunk(fi, "IDAT", datalen2);
909 png_write_bytes(fi,data2,datalen2);
911 png_start_chunk(fi, "IEND", 0);