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 #define REVERSESWAP16(s) ((((s)>>8)&0x00ff)|(((s)<<8)&0xff00))
34 #define REVERSESWAP32(s) (REVERSESWAP16(((s)>>16)&0x0000ffff)|((REVERSESWAP16(s)<<16)&0xffff0000))
36 int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi)
39 if(destlen) *destlen=0;
40 if(destdata) *destdata=0;
41 if(!fread(&len, 4, 1, fi)) {
44 if(!fread(head, 4, 1, fi)) {
47 len = REVERSESWAP32(len);
48 if(destlen) *destlen = len;
53 *destdata = (U8*)malloc(len);
54 if(!fread(*destdata, len, 1, fi)) {
56 if(destlen) *destlen=0;
60 fseek(fi, 4, SEEK_CUR);
63 fseek(fi, len+4, SEEK_CUR);
68 unsigned int png_get_dword(FILE*fi)
72 return REVERSESWAP32(a);
83 int png_read_header(FILE*fi, struct png_header*header)
88 U8 head[8] = {137,80,78,71,13,10,26,10};
92 if(strncmp((const char*)head,(const char*)head2,4))
95 while(png_read_chunk(&id, &len, &data, fi))
97 //printf("Chunk: %c%c%c%c (len:%d)\n", id[0],id[1],id[2],id[3], len);
98 if(!strncasecmp(id, "IHDR", 4)) {
101 header->width = REVERSESWAP32(*(U32*)&data[0]);
102 header->height = REVERSESWAP32(*(U32*)&data[4]);
103 a = data[8]; // should be 8
104 b = data[9]; // should be 3(indexed) or 2(rgb)
106 c = data[10]; // compression mode (0)
107 f = data[11]; // filter mode (0)
108 i = data[12]; // interlace mode (0)
110 if(b!=0 && b!=4 && b!=2 && b!=3 && b!=6) {
111 fprintf(stderr, "Image mode %d not supported!\n", b);
114 if(a!=8 && (b==2 || b==6)) {
115 printf("Bpp %d in mode %d not supported!\n", a);
119 printf("Compression mode %d not supported!\n", c);
123 printf("Filter mode %d not supported!\n", f);
127 printf("Interlace mode %d not supported!\n", i);
130 //printf("%dx%d bpp:%d mode:%d comp:%d filter:%d interlace:%d\n",header->width, header->height, a,b,c,f,i);
141 typedef unsigned char byte;
142 #define ABS(a) ((a)>0?(a):(-(a)))
143 byte inline PaethPredictor (byte a,byte b,byte c)
145 // a = left, b = above, c = upper left
146 int p = a + b - c; // initial estimate
147 int pa = ABS(p - a); // distances to a, b, c
150 // return nearest of a,b,c,
151 // breaking ties in order a,b,c.
152 if (pa <= pb && pa <= pc)
159 void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width)
162 unsigned char last=0;
163 unsigned char upperlast=0;
166 for(x=0;x<width;x++) {
173 for(x=0;x<width;x++) {
181 for(x=0;x<width;x++) {
189 for(x=0;x<width;x++) {
190 *dest = *src+(*old+last)/2;
197 for(x=0;x<width;x++) {
198 *dest = *src+PaethPredictor(last,*old,upperlast);
209 void applyfilter2(int mode, U8*src, U8*old, U8*dest, int width)
212 unsigned char lasta=0;
213 unsigned char lastb=0;
214 unsigned char upperlasta=0;
215 unsigned char upperlastb=0;
218 for(x=0;x<width;x++) {
226 for(x=0;x<width;x++) {
227 dest[0] = src[0]+lasta;
228 dest[1] = src[1]+lastb;
236 for(x=0;x<width;x++) {
237 dest[0] = src[0]+old[0];
238 dest[1] = src[1]+old[1];
245 for(x=0;x<width;x++) {
246 dest[0] = src[0]+(old[0]+lasta)/2;
247 dest[1] = src[1]+(old[1]+lastb)/2;
256 for(x=0;x<width;x++) {
257 dest[0] = src[0]+PaethPredictor(lasta,old[0],upperlasta);
258 dest[1] = src[1]+PaethPredictor(lastb,old[1],upperlastb);
271 /* also performs 24 bit conversion! */
272 void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width)
275 unsigned char lastr=0;
276 unsigned char lastg=0;
277 unsigned char lastb=0;
278 unsigned char upperlastr=0;
279 unsigned char upperlastg=0;
280 unsigned char upperlastb=0;
283 for(x=0;x<width;x++) {
293 for(x=0;x<width;x++) {
295 dest[1] = src[0]+lastr;
296 dest[2] = src[1]+lastg;
297 dest[3] = src[2]+lastb;
306 for(x=0;x<width;x++) {
308 dest[1] = src[0]+old[1];
309 dest[2] = src[1]+old[2];
310 dest[3] = src[2]+old[3];
317 for(x=0;x<width;x++) {
319 dest[1] = src[0]+(old[1]+lastr)/2;
320 dest[2] = src[1]+(old[2]+lastg)/2;
321 dest[3] = src[2]+(old[3]+lastb)/2;
331 for(x=0;x<width;x++) {
333 dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
334 dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
335 dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
349 void inline applyfilter4(int mode, U8*src, U8*old, U8*dest, int width)
352 unsigned char lastr=0;
353 unsigned char lastg=0;
354 unsigned char lastb=0;
355 unsigned char lasta=0;
356 unsigned char upperlastr=0;
357 unsigned char upperlastg=0;
358 unsigned char upperlastb=0;
359 unsigned char upperlasta=0;
362 for(x=0;x<width;x++) {
372 for(x=0;x<width;x++) {
373 dest[0] = src[3]+lasta;
374 dest[1] = src[0]+lastr;
375 dest[2] = src[1]+lastg;
376 dest[3] = src[2]+lastb;
386 for(x=0;x<width;x++) {
387 dest[0] = src[3]+old[0];
388 dest[1] = src[0]+old[1];
389 dest[2] = src[1]+old[2];
390 dest[3] = src[2]+old[3];
397 for(x=0;x<width;x++) {
398 dest[0] = src[3]+(old[0]+lasta)/2;
399 dest[1] = src[0]+(old[1]+lastr)/2;
400 dest[2] = src[1]+(old[2]+lastg)/2;
401 dest[3] = src[2]+(old[3]+lastb)/2;
412 for(x=0;x<width;x++) {
413 dest[0] = src[3]+PaethPredictor(lasta,old[0],upperlasta);
414 dest[1] = src[0]+PaethPredictor(lastr,old[1],upperlastr);
415 dest[2] = src[1]+PaethPredictor(lastg,old[2],upperlastg);
416 dest[3] = src[2]+PaethPredictor(lastb,old[3],upperlastb);
433 int getPNGdimensions(char*sname, int*destwidth, int*destheight)
436 struct png_header header;
437 if ((fi = fopen(sname, "rb")) == NULL) {
438 fprintf(stderr, "Couldn't open %s\n", sname);
441 if(!png_read_header(fi, &header)) {
442 fprintf(stderr, "Error reading header from file %s\n", sname);
446 *destwidth = header.width;
447 *destheight = header.height;
451 int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata)
458 unsigned long int imagedatalen;
459 unsigned long int zimagedatalen=0;
463 int alphapalettelen = 0;
464 struct png_header header;
471 if ((fi = fopen(sname, "rb")) == NULL) {
472 printf("Couldn't open %s\n", sname);
476 if(!png_read_header(fi, &header)) {
477 printf("Error reading header from file %s\n", sname);
481 if(header.mode == 3 || header.mode == 0) bypp = 1;
482 else if(header.mode == 4) bypp = 2;
483 else if(header.mode == 2) bypp = 3;
484 else if(header.mode == 6) bypp = 4;
486 printf("ERROR: mode:%d\n", header.mode);
490 imagedatalen = bypp * header.width * header.height + 65536;
491 imagedata = (U8*)malloc(imagedatalen);
493 fseek(fi,8,SEEK_SET);
496 if(!png_read_chunk(&tagid, &len, &data, fi))
498 if(!strncmp(tagid, "IEND", 4)) {
501 if(!strncmp(tagid, "PLTE", 4)) {
504 data = 0; //don't free data
505 //printf("%d colors in palette\n", palettelen);
507 if(!strncmp(tagid, "tRNS", 4)) {
508 if(header.mode == 3) {
510 alphapalettelen = len;
511 data = 0; //don't free data
512 //printf("found %d alpha colors\n", alphapalettelen);
515 if(!strncmp(tagid, "IDAT", 4)) {
518 zimagedata = (U8*)malloc(len);
519 memcpy(zimagedata,data,len);
521 zimagedata = (U8*)realloc(zimagedata, zimagedatalen+len);
522 memcpy(&zimagedata[zimagedatalen], data, len);
523 zimagedatalen += len;
526 if(!strncmp(tagid, "tEXt", 4)) {
528 printf("Image Text: ");
530 if(data[t]>=32 && data[t]<128)
531 printf("%c", data[t]);
541 if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) {
542 printf("Couldn't uncompress %s!\n", sname);
550 *destwidth = header.width;
551 *destheight = header.height;
553 data2 = (U8*)malloc(header.width*header.height*4);
560 U8* old= (U8*)malloc(header.width*2);
561 memset(old, 0, header.width*2);
563 for(y=0;y<header.height;y++) {
564 int mode = imagedata[pos++]; //filter mode
568 dest = &data2[(y*header.width)*4];
570 if(header.bpp == 8) {
571 /* one byte per pixel */
572 src = &imagedata[pos];
575 /* not implemented yet */
576 fprintf(stderr, "ERROR: mode=4 bpp:%d\n", header.bpp);
581 applyfilter2(mode, src, old, dest, header.width);
582 memcpy(old, dest, header.width*2);
584 for(x=header.width-1;x>=0;x--) {
585 U8 gray = dest[x*2+0];
586 U8 alpha = dest[x*2+1];
594 } else if(header.mode == 6 || header.mode == 2) {
599 for(y=0;y<header.height;y++) {
600 int mode = imagedata[pos++]; //filter mode
604 dest = &data2[(y*header.width)*4];
608 /* one byte per pixel */
609 src = &imagedata[pos];
610 pos+=header.width*(header.mode==6?4:3);
612 /* not implemented yet */
613 fprintf(stderr, "ERROR: bpp:%d\n", header.bpp);
619 memset(data2,0,header.width*4);
620 old = &data2[y*header.width*4];
622 old = &data2[(y-1)*header.width*4];
625 applyfilter4(mode, src, old, dest, header.width);
626 else // header.mode = 2
627 applyfilter3(mode, src, old, dest, header.width);
629 } else if(header.mode == 0 || header.mode == 3) {
631 U8*tmpline = (U8*)malloc(header.width+1);
632 U8*destline = (U8*)malloc(header.width+1);
638 if(header.mode == 0) { // grayscale palette
639 int mult = (0x1ff>>header.bpp);
640 palettelen = 1<<header.bpp;
641 rgba = (COL*)malloc(palettelen*sizeof(COL));
642 for(i=0;i<palettelen;i++) {
650 fprintf(stderr, "Error: No palette found!\n");
653 rgba = (COL*)malloc(palettelen*4);
654 /* 24->32 bit conversion */
655 for(i=0;i<palettelen;i++) {
656 rgba[i].r = palette[i*3+0];
657 rgba[i].g = palette[i*3+1];
658 rgba[i].b = palette[i*3+2];
659 if(alphapalette && i<alphapalettelen) {
660 rgba[i].a = alphapalette[i];
661 /*rgba[i].r = ((int)rgba[i].r*rgba[i].a)/255;
662 rgba[i].g = ((int)rgba[i].g*rgba[i].a)/255;
663 rgba[i].b = ((int)rgba[i].b*rgba[i].a)/255;*/
670 for(y=0;y<header.height;y++) {
671 int mode = imagedata[pos++]; //filter mode
675 src = &imagedata[pos];
676 if(header.bpp == 8) {
681 U32 v = (1<<header.bpp)-1;
682 for(x=0;x<header.width;x++) {
683 U32 r = src[s/8]<<8 |
686 tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
690 pos+=(header.width*header.bpp+7)/8;
694 memset(destline,0,header.width);
695 old = &destline[y*header.width];
699 applyfilter1(mode, src, old, destline, header.width);
700 memcpy(tmpline,destline,header.width);
701 for(x=0;x<header.width;x++) {
702 *(COL*)&data2[y*header.width*4+x*4+0] = rgba[destline[x]];
709 printf("expected PNG mode to be 2, 3 or 6 (is:%d)\n", header.mode);
718 static U32*crc32_table = 0;
719 static void make_crc32_table(void)
724 crc32_table = (U32*)malloc(1024);
726 for (t = 0; t < 256; t++) {
729 for (s = 0; s < 8; s++) {
730 c = (0xedb88320L*(c&1)) ^ (c >> 1);
735 static inline void png_write_byte(FILE*fi, U8 byte)
737 fwrite(&byte,1,1,fi);
738 mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
740 static void png_start_chunk(FILE*fi, char*type, int len)
742 U8 mytype[4]={0,0,0,0};
743 U32 mylen = REVERSESWAP32(len);
744 memcpy(mytype,type,strlen(type));
745 fwrite(&mylen, 4, 1, fi);
747 png_write_byte(fi,mytype[0]);
748 png_write_byte(fi,mytype[1]);
749 png_write_byte(fi,mytype[2]);
750 png_write_byte(fi,mytype[3]);
752 static void png_write_bytes(FILE*fi, U8*bytes, int len)
756 png_write_byte(fi,bytes[t]);
758 static void png_write_dword(FILE*fi, U32 dword)
760 png_write_byte(fi,dword>>24);
761 png_write_byte(fi,dword>>16);
762 png_write_byte(fi,dword>>8);
763 png_write_byte(fi,dword);
765 static void png_end_chunk(FILE*fi)
767 U32 tmp = REVERSESWAP32((mycrc32^0xffffffff));
771 void writePNG(char*filename, unsigned char*data, int width, int height)
783 U8 head[] = {137,80,78,71,13,10,26,10}; // PNG header
798 datalen = (width*height*bpp/8+cols*8);
800 fi = fopen(filename, "wb");
805 fwrite(head,sizeof(head),1,fi);
807 png_start_chunk(fi, "IHDR", 13);
808 png_write_dword(fi,width);
809 png_write_dword(fi,height);
810 png_write_byte(fi,8);
812 png_write_byte(fi,3); //indexed
813 else if(format == 5 && alpha==0)
814 png_write_byte(fi,2); //rgb
815 else if(format == 5 && alpha==1)
816 png_write_byte(fi,6); //rgba
819 png_write_byte(fi,0); //compression mode
820 png_write_byte(fi,0); //filter mode
821 png_write_byte(fi,0); //interlace mode
825 png_start_chunk(fi, "PLTE", 768);
828 png_write_byte(fi,palette[t].r);
829 png_write_byte(fi,palette[t].g);
830 png_write_byte(fi,palette[t].b);
837 int srcwidth = width * (bpp/8);
838 datalen3 = (width*4+5)*height;
839 data3 = (U8*)malloc(datalen3);
840 for(y=0;y<height;y++)
842 data3[pos2++]=0; //filter type
843 for(x=0;x<width;x++) {
844 data3[pos2++]=data[pos+1];
845 data3[pos2++]=data[pos+2];
846 data3[pos2++]=data[pos+3];
847 data3[pos2++]=data[pos+0]; //alpha
850 pos+=((srcwidth+3)&~3)-srcwidth; //align
856 data2 = malloc(datalen2);
858 if((ret = compress (data2, &datalen2, data3, datalen3)) != Z_OK) {
859 fprintf(stderr, "zlib error in pic %d\n", ret);
862 png_start_chunk(fi, "IDAT", datalen2);
863 png_write_bytes(fi,data2,datalen2);
865 png_start_chunk(fi, "IEND", 0);