X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fpng.c;h=fac4f77347625b116b8fa6d282c694c74192cc1f;hb=6c3ab5574d31504d24710c2756899d49275c1a37;hp=9669f89db27923a84eda9fa76a5122e8dc5d6713;hpb=61a17d8fd634fda91865af922cb5ca241408d0c3;p=swftools.git diff --git a/lib/png.c b/lib/png.c index 9669f89..fac4f77 100644 --- a/lib/png.c +++ b/lib/png.c @@ -22,35 +22,43 @@ #include #include #include + +#ifdef EXPORT +#undef EXPORT +#endif + +#ifdef PNG_INLINE_EXPORTS +#define EXPORT static +#else +#define EXPORT #include "png.h" +#endif + +typedef unsigned u32; -typedef unsigned char U8; -typedef unsigned long U32; typedef struct _COL { - U8 a,r,g,b; + unsigned char a,r,g,b; } COL; -#define REVERSESWAP16(s) ((((s)>>8)&0x00ff)|(((s)<<8)&0xff00)) -#define REVERSESWAP32(s) (REVERSESWAP16(((s)>>16)&0x0000ffff)|((REVERSESWAP16(s)<<16)&0xffff0000)) - -int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi) +static int png_read_chunk(char (*head)[4], int*destlen, unsigned char**destdata, FILE*fi) { unsigned int len; + unsigned char blen[4]; if(destlen) *destlen=0; if(destdata) *destdata=0; - if(!fread(&len, 4, 1, fi)) { + if(!fread(&blen, 4, 1, fi)) { return 0; } if(!fread(head, 4, 1, fi)) { return 0; } - len = REVERSESWAP32(len); + len = blen[0]<<24|blen[1]<<16|blen[2]<<8|blen[3]; if(destlen) *destlen = len; if(destdata) { if(!len) { *destdata = 0; } else { - *destdata = (U8*)malloc(len); + *destdata = (unsigned char*)malloc(len); if(!fread(*destdata, len, 1, fi)) { *destdata = 0; if(destlen) *destlen=0; @@ -65,11 +73,12 @@ int png_read_chunk(char (*head)[4], int*destlen, U8**destdata, FILE*fi) return 1; } -unsigned int png_get_dword(FILE*fi) +static unsigned int png_get_dword(FILE*fi) { unsigned int a; - fread(&a,4,1,fi); - return REVERSESWAP32(a); + unsigned char b[4]; + fread(&b,4,1,fi); + return b[0]<<24|b[1]<<16|b[2]<<8|b[3]; } struct png_header @@ -80,14 +89,14 @@ struct png_header int mode; }; -int png_read_header(FILE*fi, struct png_header*header) +static int png_read_header(FILE*fi, struct png_header*header) { char id[4]; int len; int ok=0; - U8 head[8] = {137,80,78,71,13,10,26,10}; - U8 head2[8]; - U8*data; + unsigned char head[8] = {137,80,78,71,13,10,26,10}; + unsigned char head2[8]; + unsigned char*data; fread(head2,8,1,fi); if(strncmp((const char*)head,(const char*)head2,4)) return 0; @@ -95,11 +104,11 @@ int png_read_header(FILE*fi, struct png_header*header) while(png_read_chunk(&id, &len, &data, fi)) { //printf("Chunk: %c%c%c%c (len:%d)\n", id[0],id[1],id[2],id[3], len); - if(!strncasecmp(id, "IHDR", 4)) { + if(!strncmp(id, "IHDR", 4)) { char a,b,c,f,i; if(len < 8) exit(1); - header->width = REVERSESWAP32(*(U32*)&data[0]); - header->height = REVERSESWAP32(*(U32*)&data[4]); + header->width = data[0]<<24|data[1]<<16|data[2]<<8|data[3]; + header->height = data[4]<<24|data[5]<<16|data[6]<<8|data[7]; a = data[8]; // should be 8 b = data[9]; // should be 3(indexed) or 2(rgb) @@ -140,7 +149,7 @@ int png_read_header(FILE*fi, struct png_header*header) typedef unsigned char byte; #define ABS(a) ((a)>0?(a):(-(a))) -byte inline PaethPredictor (byte a,byte b,byte c) +static inline byte PaethPredictor (byte a,byte b,byte c) { // a = left, b = above, c = upper left int p = a + b - c; // initial estimate @@ -156,7 +165,7 @@ byte inline PaethPredictor (byte a,byte b,byte c) else return c; } -void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width) +static void applyfilter1(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width) { int x; unsigned char last=0; @@ -206,7 +215,7 @@ void applyfilter1(int mode, U8*src, U8*old, U8*dest, int width) } -void applyfilter2(int mode, U8*src, U8*old, U8*dest, int width) +static void applyfilter2(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width) { int x; unsigned char lasta=0; @@ -269,7 +278,7 @@ void applyfilter2(int mode, U8*src, U8*old, U8*dest, int width) /* also performs 24 bit conversion! */ -void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width) +static void applyfilter3(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width) { int x; unsigned char lastr=0; @@ -346,7 +355,7 @@ void applyfilter3(int mode, U8*src, U8*old, U8*dest, int width) } } -void inline applyfilter4(int mode, U8*src, U8*old, U8*dest, int width) +static void inline applyfilter4(int mode, unsigned char*src, unsigned char*old, unsigned char*dest, int width) { int x; unsigned char lastr=0; @@ -430,7 +439,7 @@ void inline applyfilter4(int mode, U8*src, U8*old, U8*dest, int width) } -int getPNGdimensions(char*sname, int*destwidth, int*destheight) +EXPORT int getPNGdimensions(const char*sname, int*destwidth, int*destheight) { FILE*fi; struct png_header header; @@ -448,25 +457,27 @@ int getPNGdimensions(char*sname, int*destwidth, int*destheight) return 1; } -int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) +EXPORT int getPNG(const char*sname, int*destwidth, int*destheight, unsigned char**destdata) { char tagid[4]; int len; - U8*data; - U8*imagedata; - U8*zimagedata=0; + unsigned char*data; + unsigned char*imagedata; + unsigned char*zimagedata=0; unsigned long int imagedatalen; unsigned long int zimagedatalen=0; - U8*palette = 0; + unsigned char*palette = 0; int palettelen = 0; - U8*alphapalette = 0; + unsigned char*alphapalette = 0; int alphapalettelen = 0; struct png_header header; int bypp; - U8*data2 = 0; + unsigned char*data2 = 0; + unsigned char alphacolor[3]; + int hasalphacolor=0; FILE *fi; - U8 *scanline; + unsigned char *scanline; if ((fi = fopen(sname, "rb")) == NULL) { printf("Couldn't open %s\n", sname); @@ -488,7 +499,7 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) } imagedatalen = bypp * header.width * header.height + 65536; - imagedata = (U8*)malloc(imagedatalen); + imagedata = (unsigned char*)malloc(imagedatalen); fseek(fi,8,SEEK_SET); while(!feof(fi)) @@ -510,15 +521,25 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) alphapalettelen = len; data = 0; //don't free data //printf("found %d alpha colors\n", alphapalettelen); + } else if(header.mode == 0 || header.mode == 2) { + int t; + if(header.mode == 2) { + alphacolor[0] = data[1]; + alphacolor[1] = data[3]; + alphacolor[2] = data[5]; + } else { + alphacolor[0] = alphacolor[1] = alphacolor[2] = data[1]; + } + hasalphacolor = 1; } } if(!strncmp(tagid, "IDAT", 4)) { if(!zimagedata) { zimagedatalen = len; - zimagedata = (U8*)malloc(len); + zimagedata = (unsigned char*)malloc(len); memcpy(zimagedata,data,len); } else { - zimagedata = (U8*)realloc(zimagedata, zimagedatalen+len); + zimagedata = (unsigned char*)realloc(zimagedata, zimagedatalen+len); memcpy(&zimagedata[zimagedatalen], data, len); zimagedatalen += len; } @@ -534,8 +555,9 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) } printf("\n");*/ } - if(data) - free(data); + if(data) { + free(data); data=0; + } } if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) { @@ -550,20 +572,20 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) *destwidth = header.width; *destheight = header.height; - data2 = (U8*)malloc(header.width*header.height*4); + data2 = (unsigned char*)malloc(header.width*header.height*4); if(header.mode == 4) { int i,s=0; int x,y; int pos=0; - U8* old= (U8*)malloc(header.width*2); + unsigned char* old= (unsigned char*)malloc(header.width*2); memset(old, 0, header.width*2); *destdata = data2; for(y=0;y=0;x--) { - U8 gray = dest[x*2+0]; - U8 alpha = dest[x*2+1]; + unsigned char gray = dest[x*2+0]; + unsigned char alpha = dest[x*2+1]; dest[x*4+0] = alpha; dest[x*4+1] = gray; dest[x*4+2] = gray; @@ -591,6 +613,7 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) } } free(old); + free(imagedata); } else if(header.mode == 6 || header.mode == 2) { int i,s=0; int x,y; @@ -598,9 +621,9 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) *destdata = data2; for(y=0;y>(16-header.bpp-(s&7)))&v; @@ -705,6 +751,7 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) free(tmpline); free(destline); free(rgba); + free(imagedata); } else { printf("expected PNG mode to be 2, 3 or 6 (is:%d)\n", header.mode); return 0; @@ -713,18 +760,18 @@ int getPNG(char*sname, int*destwidth, int*destheight, unsigned char**destdata) return 1; } -static U32 mycrc32; +static u32 mycrc32; -static U32*crc32_table = 0; +static u32*crc32_table = 0; static void make_crc32_table(void) { int t; if(crc32_table) return; - crc32_table = (U32*)malloc(1024); + crc32_table = (u32*)malloc(1024); for (t = 0; t < 256; t++) { - U32 c = t; + u32 c = t; int s; for (s = 0; s < 8; s++) { c = (0xedb88320L*(c&1)) ^ (c >> 1); @@ -732,30 +779,49 @@ static void make_crc32_table(void) crc32_table[t] = c; } } -static inline void png_write_byte(FILE*fi, U8 byte) +static inline void png_write_byte(FILE*fi, unsigned char byte) { fwrite(&byte,1,1,fi); mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8); } -static void png_start_chunk(FILE*fi, char*type, int len) +static long png_start_chunk(FILE*fi, char*type, int len) { - U8 mytype[4]={0,0,0,0}; - U32 mylen = REVERSESWAP32(len); + unsigned char mytype[4]={0,0,0,0}; + unsigned char mylen[4]; + long filepos; + mylen[0] = len>>24; + mylen[1] = len>>16; + mylen[2] = len>>8; + mylen[3] = len; memcpy(mytype,type,strlen(type)); + filepos = ftell(fi); fwrite(&mylen, 4, 1, fi); mycrc32=0xffffffff; png_write_byte(fi,mytype[0]); png_write_byte(fi,mytype[1]); png_write_byte(fi,mytype[2]); png_write_byte(fi,mytype[3]); + return filepos; } -static void png_write_bytes(FILE*fi, U8*bytes, int len) +static void png_patch_len(FILE*fi, int pos, int len) +{ + unsigned char mylen[4]; + long filepos; + mylen[0] = len>>24; + mylen[1] = len>>16; + mylen[2] = len>>8; + mylen[3] = len; + fseek(fi, pos, SEEK_SET); + fwrite(&mylen, 4, 1, fi); + fseek(fi, 0, SEEK_END); +} +static void png_write_bytes(FILE*fi, unsigned char*bytes, int len) { int t; for(t=0;t>24); png_write_byte(fi,dword>>16); @@ -764,30 +830,204 @@ static void png_write_dword(FILE*fi, U32 dword) } static void png_end_chunk(FILE*fi) { - U32 tmp = REVERSESWAP32((mycrc32^0xffffffff)); - fwrite(&tmp,4,1,fi); + u32 tmp = mycrc32^0xffffffff; + unsigned char tmp2[4]; + tmp2[0] = tmp>>24; + tmp2[1] = tmp>>16; + tmp2[2] = tmp>>8; + tmp2[3] = tmp; + fwrite(&tmp2,4,1,fi); +} + +#define ZLIB_BUFFER_SIZE 16384 + +static long compress_line(z_stream*zs, Bytef*line, int len, FILE*fi) +{ + long size = 0; + zs->next_in = line; + zs->avail_in = len; + + while(1) { + int ret = deflate(zs, Z_NO_FLUSH); + if (ret != Z_OK) { + fprintf(stderr, "error in deflate(): %s", zs->msg?zs->msg:"unknown"); + return 0; + } + if(zs->avail_out != ZLIB_BUFFER_SIZE) { + int consumed = ZLIB_BUFFER_SIZE - zs->avail_out; + size += consumed; + png_write_bytes(fi, zs->next_out - consumed , consumed); + zs->next_out = zs->next_out - consumed; + zs->avail_out = ZLIB_BUFFER_SIZE; + } + if(!zs->avail_in) { + break; + } + } + return size; +} + +static int test_line(z_stream*zs_orig, Bytef*line, int linelen) +{ + z_stream zs; + int ret = deflateCopy(&zs, zs_orig); + if(ret != Z_OK) { + fprintf(stderr, "Couldn't copy stream\n"); + return 0; + } + + zs.next_in = line; + zs.avail_in = linelen; + + long size = 0; + + int mode = Z_SYNC_FLUSH; + while(1) { + int ret = deflate(&zs, mode); + if (ret != Z_OK && ret != Z_STREAM_END) { + fprintf(stderr, "error in deflate(): %s (mode %s, %d bytes remaining)\n", zs.msg?zs.msg:"unknown", + mode==Z_SYNC_FLUSH?"Z_SYNC_FLUSH":"Z_FINISH", zs.avail_in); + return 0; + } + if(zs.avail_out != ZLIB_BUFFER_SIZE) { + int consumed = ZLIB_BUFFER_SIZE - zs.avail_out; + size += consumed; + zs.next_out = zs.next_out - consumed; + zs.avail_out = ZLIB_BUFFER_SIZE; + } + if (ret == Z_STREAM_END) { + break; + } + if(!zs.avail_in) { + mode = Z_FINISH; + } + } + ret = deflateEnd(&zs); + if (ret != Z_OK) { + fprintf(stderr, "error in deflateEnd(): %s\n", zs.msg?zs.msg:"unknown"); + return 0; + } + return size; } -void writePNG(char*filename, unsigned char*data, int width, int height) +static int finishzlib(z_stream*zs, FILE*fi) +{ + int size = 0; + int ret; + while(1) { + ret = deflate(zs, Z_FINISH); + if (ret != Z_OK && + ret != Z_STREAM_END) { + fprintf(stderr, "error in deflate(finish): %s\n", zs->msg?zs->msg:"unknown"); + return 0; + } + + if(zs->avail_out != ZLIB_BUFFER_SIZE) { + int consumed = ZLIB_BUFFER_SIZE - zs->avail_out; + size += consumed; + png_write_bytes(fi, zs->next_out - consumed , consumed); + zs->next_out = zs->next_out - consumed; + zs->avail_out = ZLIB_BUFFER_SIZE; + } + if (ret == Z_STREAM_END) { + break; + } + } + ret = deflateEnd(zs); + if (ret != Z_OK) { + fprintf(stderr, "error in deflateEnd(): %s\n", zs->msg?zs->msg:"unknown"); + return 0; + } + return size; +} + +static void filter_line(int filtermode, unsigned char*dest, unsigned char*src, int width) +{ + int pos2 = 0; + int pos = 0; + int srcwidth = width*4; + int x; + if(filtermode == 0) { + for(x=0;x=2) + continue; // don't do y direction filters in the first row + + line[0]=filtermode; //filter type + filter_line(filtermode, line+1, &data[y*srcwidth], width); - if((ret = compress (data2, &datalen2, data3, datalen3)) != Z_OK) { - fprintf(stderr, "zlib error in pic %d\n", ret); - return; + int size = test_line(&zs, line, linelen); + if(size < bestsize) { + memcpy(bestline, line, linelen); + bestsize = size; + } + } + idatsize += compress_line(&zs, bestline, linelen, fi); + } + free(line);free(bestline); } - png_start_chunk(fi, "IDAT", datalen2); - png_write_bytes(fi,data2,datalen2); + idatsize += finishzlib(&zs, fi); + png_patch_len(fi, idatpos, idatsize); png_end_chunk(fi); + png_start_chunk(fi, "IEND", 0); png_end_chunk(fi); + free(writebuf); free(data2); - free(data3); + fclose(fi); } +