#include <math.h>
#include <fcntl.h>
#include <zlib.h>
+
+#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;
-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(!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;
return 1;
}
-unsigned int png_get_dword(FILE*fi)
+static unsigned int png_get_dword(FILE*fi)
{
unsigned int a;
unsigned char b[4];
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;
+ 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];
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
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;
else if(mode==3) {
for(x=0;x<width;x++) {
*dest = *src+(*old+last)/2;
+ last = *dest;
dest++;
old++;
src++;
}
-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;
/* 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;
}
}
-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;
}
-int getPNGdimensions(char*sname, int*destwidth, int*destheight)
+EXPORT int getPNGdimensions(const char*sname, int*destwidth, int*destheight)
{
FILE*fi;
struct png_header header;
return 0;
}
if(!png_read_header(fi, &header)) {
- fprintf(stderr, "Error reading header from file %s\n", sname);
return 0;
}
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;
- U8 alphacolor[3];
+ 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);
}
if(!png_read_header(fi, &header)) {
- printf("Error reading header from file %s\n", sname);
return 0;
}
}
imagedatalen = bypp * header.width * header.height + 65536;
- imagedata = (U8*)malloc(imagedatalen);
+ imagedata = (unsigned char*)malloc(imagedatalen);
fseek(fi,8,SEEK_SET);
while(!feof(fi))
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;
}
*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<header.height;y++) {
int mode = imagedata[pos++]; //filter mode
- U8*src;
- U8*dest;
+ unsigned char*src;
+ unsigned char*dest;
int x;
dest = &data2[(y*header.width)*4];
memcpy(old, dest, header.width*2);
for(x=header.width-1;x>=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;
int x,y;
int pos=0;
*destdata = data2;
+
+ unsigned char* firstline = malloc(header.width*4);
+ memset(firstline,0,header.width*4);
for(y=0;y<header.height;y++) {
int mode = imagedata[pos++]; //filter mode
- U8*src;
- U8*dest;
- U8*old;
+ unsigned char*src;
+ unsigned char*dest;
+ unsigned char*old;
dest = &data2[(y*header.width)*4];
if(header.bpp == 8)
}
if(!y) {
- memset(data2,0,header.width*4);
- old = &data2[y*header.width*4];
+ old = firstline;
} else {
old = &data2[(y-1)*header.width*4];
}
- if(header.mode == 6)
+ if(header.mode == 6) {
applyfilter4(mode, src, old, dest, header.width);
- else // header.mode = 2
+ } else { // header.mode = 2
applyfilter3(mode, src, old, dest, header.width);
+ /* replace alpha color */
+ if(hasalphacolor) {
+ int x;
+ for(x=0;x<header.width;x++) {
+ if(dest[x*4+1] == alphacolor[0] &&
+ dest[x*4+2] == alphacolor[1] &&
+ dest[x*4+3] == alphacolor[2]) {
+ *(u32*)&dest[x*4] = 0;
+ }
+ }
+ }
+ }
}
+ free(firstline);
free(imagedata);
} else if(header.mode == 0 || header.mode == 3) {
COL*rgba = 0;
- U8*tmpline = (U8*)malloc(header.width+1);
- U8*destline = (U8*)malloc(header.width+1);
+ unsigned char*tmpline = (unsigned char*)malloc(header.width+1);
+ unsigned char*destline = (unsigned char*)malloc(header.width+1);
int i,x,y;
int pos=0;
for(y=0;y<header.height;y++) {
int mode = imagedata[pos++]; //filter mode
int x;
- U8*old;
- U8*src;
+ unsigned char*old;
+ unsigned char*src;
src = &imagedata[pos];
if(header.bpp == 8) {
pos+=header.width;
} else {
int x,s=0;
int bitpos = 0;
- U32 v = (1<<header.bpp)-1;
+ u32 v = (1<<header.bpp)-1;
for(x=0;x<header.width;x++) {
- U32 r = src[s/8]<<8 |
+ u32 r = src[s/8]<<8 |
src[s/8+1];
int t;
tmpline[x] = (r>>(16-header.bpp-(s&7)))&v;
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);
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};
- U8 mylen[4];
+ 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, U8*bytes, int len)
+static void png_write_bytes(FILE*fi, unsigned char*bytes, int len)
{
int t;
for(t=0;t<len;t++)
png_write_byte(fi,bytes[t]);
}
-static void png_write_dword(FILE*fi, U32 dword)
+static void png_write_dword(FILE*fi, u32 dword)
{
png_write_byte(fi,dword>>24);
png_write_byte(fi,dword>>16);
}
static void png_end_chunk(FILE*fi)
{
- U32 tmp = mycrc32^0xffffffff;
- U8 tmp2[4];
+ u32 tmp = mycrc32^0xffffffff;
+ unsigned char tmp2[4];
tmp2[0] = tmp>>24;
tmp2[1] = tmp>>16;
tmp2[2] = tmp>>8;
fwrite(&tmp2,4,1,fi);
}
-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 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<width;x++) {
+ dest[pos2++]=src[pos+1];
+ dest[pos2++]=src[pos+2];
+ dest[pos2++]=src[pos+3];
+ dest[pos2++]=src[pos+0]; //alpha
+ pos+=4;
+ }
+ } else if(filtermode == 1) {
+ /* x difference filter */
+ dest[pos2++]=src[pos+1];
+ dest[pos2++]=src[pos+2];
+ dest[pos2++]=src[pos+3];
+ dest[pos2++]=src[pos+0];
+ pos+=4;
+ for(x=1;x<width;x++) {
+ dest[pos2++]=src[pos+1] - src[pos-4+1];
+ dest[pos2++]=src[pos+2] - src[pos-4+2];
+ dest[pos2++]=src[pos+3] - src[pos-4+3];
+ dest[pos2++]=src[pos+0] - src[pos-4+0]; //alpha
+ pos+=4;
+ }
+ } else if(filtermode == 2) {
+ /* y difference filter */
+ for(x=0;x<width;x++) {
+ dest[pos2++]=src[pos+1] - src[pos-srcwidth+1];
+ dest[pos2++]=src[pos+2] - src[pos-srcwidth+2];
+ dest[pos2++]=src[pos+3] - src[pos-srcwidth+3];
+ dest[pos2++]=src[pos+0] - src[pos-srcwidth+0]; //alpha
+ pos+=4;
+ }
+ } else if(filtermode == 3) {
+ dest[pos2++]=src[pos+1] - src[pos-srcwidth+1]/2;
+ dest[pos2++]=src[pos+2] - src[pos-srcwidth+2]/2;
+ dest[pos2++]=src[pos+3] - src[pos-srcwidth+3]/2;
+ dest[pos2++]=src[pos+0] - src[pos-srcwidth+0]/2;
+ pos+=4;
+ /* x+y difference filter */
+ for(x=1;x<width;x++) {
+ dest[pos2++]=src[pos+1] - (src[pos-4+1] + src[pos-srcwidth+1])/2;
+ dest[pos2++]=src[pos+2] - (src[pos-4+2] + src[pos-srcwidth+2])/2;
+ dest[pos2++]=src[pos+3] - (src[pos-4+3] + src[pos-srcwidth+3])/2;
+ dest[pos2++]=src[pos+0] - (src[pos-4+0] + src[pos-srcwidth+0])/2; //alpha
+ pos+=4;
+ }
+ } else if(filtermode == 4) {
+ dest[pos2++]=src[pos+1] - PaethPredictor(0, src[pos-srcwidth+1], 0);
+ dest[pos2++]=src[pos+2] - PaethPredictor(0, src[pos-srcwidth+2], 0);
+ dest[pos2++]=src[pos+3] - PaethPredictor(0, src[pos-srcwidth+3], 0);
+ dest[pos2++]=src[pos+0] - PaethPredictor(0, src[pos-srcwidth+0], 0);
+ pos+=4;
+ /* paeth difference filter */
+ for(x=1;x<width;x++) {
+ dest[pos2++]=src[pos+1] - PaethPredictor(src[pos-4+1], src[pos-srcwidth+1], src[pos-4-srcwidth+1]);
+ dest[pos2++]=src[pos+2] - PaethPredictor(src[pos-4+2], src[pos-srcwidth+2], src[pos-4-srcwidth+2]);
+ dest[pos2++]=src[pos+3] - PaethPredictor(src[pos-4+3], src[pos-srcwidth+3], src[pos-4-srcwidth+3]);
+ dest[pos2++]=src[pos+0] - PaethPredictor(src[pos-4+0], src[pos-srcwidth+0], src[pos-4-srcwidth+0]);
+ pos+=4;
+ }
+ }
+}
+
+EXPORT void writePNG(const char*filename, unsigned char*data, int width, int height)
{
FILE*fi;
int crc;
int t;
- U8 format;
- U8 tmp;
- U8* data2=0;
- U8* data3=0;
- U32 datalen;
- U32 datalen2;
- U32 datalen3;
- U8 head[] = {137,80,78,71,13,10,26,10}; // PNG header
+ unsigned char format;
+ unsigned char tmp;
+ unsigned char* data2=0;
+ u32 datalen;
+ u32 datalen2;
+ unsigned char head[] = {137,80,78,71,13,10,26,10}; // PNG header
int cols;
char alpha = 1;
int pos = 0;
int error;
- U32 tmp32;
+ u32 tmp32;
int bpp;
int ret;
+ z_stream zs;
make_crc32_table();
png_write_byte(fi,0); //filter mode
png_write_byte(fi,0); //interlace mode
png_end_chunk(fi);
-
+
/* if(format == 3) {
png_start_chunk(fi, "PLTE", 768);
}
png_end_chunk(fi);
}*/
+ long idatpos = png_start_chunk(fi, "IDAT", 0);
+
+ memset(&zs,0,sizeof(z_stream));
+ Bytef*writebuf = (Bytef*)malloc(ZLIB_BUFFER_SIZE);
+ zs.zalloc = Z_NULL;
+ zs.zfree = Z_NULL;
+ zs.opaque = Z_NULL;
+ zs.next_out = writebuf;
+ zs.avail_out = ZLIB_BUFFER_SIZE;
+ ret = deflateInit(&zs, Z_NO_COMPRESSION);
+ if (ret != Z_OK) {
+ fprintf(stderr, "error in deflateInit(): %s", zs.msg?zs.msg:"unknown");
+ return;
+ }
+
+ long idatsize = 0;
{
- int pos2 = 0;
int x,y;
int srcwidth = width * (bpp/8);
- datalen3 = (width*4+5)*height;
- data3 = (U8*)malloc(datalen3);
+ int linelen = 1 + ((srcwidth+3)&~3);
+ unsigned char* line = (unsigned char*)malloc(linelen);
+ unsigned char* bestline = (unsigned char*)malloc(linelen);
+ memset(line, 0, linelen);
for(y=0;y<height;y++)
{
- data3[pos2++]=0; //filter type
- for(x=0;x<width;x++) {
- data3[pos2++]=data[pos+1];
- data3[pos2++]=data[pos+2];
- data3[pos2++]=data[pos+3];
- data3[pos2++]=data[pos+0]; //alpha
- pos+=4;
- }
- pos+=((srcwidth+3)&~3)-srcwidth; //align
- }
- datalen3=pos2;
- }
+ int filtermode;
+ int bestsize = 0x7fffffff;
+ for(filtermode=0;filtermode<=0;filtermode++) {
- datalen2 = datalen3;
- data2 = (U8*)malloc(datalen2);
+ if(!y && filtermode>=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);
}
+