X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fpng.c;h=5895269fd8cfcf0385bb4a94e62252bbe3a138f2;hp=141218e6d4e0f138479e253e2d849c3274d0ab62;hb=27215f1c64457c50a5e1a5b2790bf2b9e73d3c4e;hpb=d66545795a2323237dec29186b714d67149dc24d diff --git a/lib/png.c b/lib/png.c index 141218e..5895269 100644 --- a/lib/png.c +++ b/lib/png.c @@ -19,9 +19,11 @@ #include #include #include +#include #include #include #include +#include #ifdef EXPORT #undef EXPORT @@ -99,12 +101,12 @@ static int png_read_header(FILE*fi, struct png_header*header) unsigned char*data; fread(head2,8,1,fi); if(strncmp((const char*)head,(const char*)head2,4)) - return 0; + return 0; // not a png file 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 = data[0]<<24|data[1]<<16|data[2]<<8|data[3]; @@ -121,7 +123,7 @@ static int png_read_header(FILE*fi, struct png_header*header) return 0; } if(a!=8 && (b==2 || b==6)) { - printf("Bpp %d in mode %d not supported!\n", a); + printf("Bpp %d in mode %d not supported!\n", b, a); return 0; } if(c!=0) { @@ -197,6 +199,7 @@ static void applyfilter1(int mode, unsigned char*src, unsigned char*old, unsigne else if(mode==3) { for(x=0;xnum - c1->num; +} + +static colornum_t* getColors(COL*image, int size, int*num) +{ + unsigned char*colexists = malloc((256*256*256)/8); + memset(colexists, 0, (256*256*256)/8); + int t; + int count=0; + + /* find all different colors in the image */ + for(t=0;t= col) max=i; + else min=i+1; + } + assert(colors[i].color==col); + colors[i].num++; + } + free(colexists); + *num = count; + return colors; +} + +static void getOptimalPalette(COL*image, int size, int palettesize, COL*palette) +{ + int num; + memset(palette, 0, sizeof(COL)*256); + colornum_t*colors = getColors(image, size, &num); + + assert(palettesize<=256); + + qsort(colors, num, sizeof(colornum_t), compare_colors); + + if(num<=palettesize) { + /* if there are not more than palettesize different colors in + the image anyway, we are done */ + int t; + for(t=0;t>8; + palette[t].b = colors[t].color>>16; + palette[t].a = 255; + } + return; + } + + if(num>2048) { + /* if there are too many different colors, pick the ones that + occur most often */ + num = 2048; + } + + colornum_t*centers = malloc(sizeof(colornum_t)*palettesize); + int t; + for(t=0;t= (palettesize+num)*2) { + fprintf(stderr, "Warning: didn't find optimal palette\n"); + break; + } + change = 0; + int s,t; + for(s=0;s>0&0xff) - (colors[t].color>>0&0xff)); + distance += abs((centers[s].color>>8&0xff) - (colors[t].color>>8&0xff)); + distance += abs((centers[s].color>>16&0xff) - (colors[t].color>>16&0xff)); + distance *= colors[t].num; + if(distance>0)&0xff)*colors[t].num; + g += ((colors[t].color>>8)&0xff)*colors[t].num; + b += ((colors[t].color>>16)&0xff)*colors[t].num; + count+=colors[t].num; + } + } + if(!count) { + int random = rand()%num; + centers[s].color = colors[random].color; + centers[s].num = 0; + change = 1; + } else { + r /= count; + g /= count; + b /= count; + centers[s].color = r|g<<8|b<<16; + centers[s].num = count; + } + } + } + free(belongsto); + free(colors); + for(t=0;t>8; + palette[t].b = centers[t].color>>16; + palette[t].a = 255; + } + free(centers); +} + +static int sqr(const int x) {return x*x;} + +static void png_quantize_image(unsigned char*_image, int size, int numcolors, unsigned char**newimage, COL*palette) +{ + COL*image = (COL*)_image; + getOptimalPalette(image, size, numcolors, palette); + *newimage = (unsigned char*)malloc(size); + int t; + for(t=0;t> 8); } -static void png_start_chunk(FILE*fi, char*type, int len) +static long png_start_chunk(FILE*fi, char*type, int 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_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) { @@ -824,7 +1041,416 @@ static void png_end_chunk(FILE*fi) fwrite(&tmp2,4,1,fi); } -EXPORT void writePNG(char*filename, unsigned char*data, int width, int height) +#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; +} + +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 inline u32 color_hash(COL*col) +{ + u32 col32 = *(u32*)col; + u32 hash = (col32 >> 17) ^ col32; + hash ^= ((hash>>8) + 1) ^ hash; + return hash; +} + +static int png_get_number_of_palette_entries(COL*img, int width, int height, COL*palette, char*has_alpha) +{ + int len = width*height; + int t; + int palsize = 0; + int size[256]; + int palette_overflow = 0; + u32 lastcol32 = 0; + + memset(size, 0, sizeof(size)); + + u32*pal = (u32*)malloc(65536*sizeof(u32)); + int*count = (int*)malloc(65536*sizeof(int)); + + assert(sizeof(COL)==sizeof(u32)); + assert(width && height); + + lastcol32 = (*(u32*)&img[0])^0xffffffff; // don't match + + for(t=0;t0?5:2; //don't apply y-direction filter in first line + int f; + int best_nr = 0; + int best_energy = INT_MAX; + int w = width*(bpp/8); + unsigned char* pairs = malloc(8192); + assert(bpp==8 || bpp==32); + for(f=0;f>3; + int b = 1<<(v&7); + if(!pairs[p]&b) { + pairs[p]|=b; + different_pairs ++; + } + } + int energy = different_pairs; + if(energy=2) + continue; // don't do y direction filters in the first row + + line[0]=filtermode; //filter type + if(bpp==8) + png_apply_specific_filter_8(filtermode, line+1, &data[y*srcwidth], width); + else + png_apply_specific_filter_32(filtermode, line+1, &data[y*srcwidth], width); - datalen2 = datalen3; - data2 = (unsigned char*)malloc(datalen2); + int size = test_line(&zs, line, linelen); + if(size < bestsize) { + memcpy(bestline, line, linelen); + bestsize = size; + } + } + idatsize += compress_line(&zs, bestline, linelen, fi); + } + free(bestline); +#else + for(y=0;y