+#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(char*filename, unsigned char*data, int width, int height)