+static int png_apply_specific_filter_8(int filtermode, unsigned char*dest, unsigned char*src, int width)
+{
+ int pos2 = 0;
+ int pos = 0;
+ int srcwidth = width;
+ int x;
+ if(filtermode == 0) {
+ for(x=0;x<width;x++) {
+ dest[pos2++]=src[pos++]; //alpha
+ }
+ } else if(filtermode == 1) {
+ /* x difference filter */
+ dest[pos2++]=src[pos++];
+ for(x=1;x<width;x++) {
+ dest[pos2++]=src[pos] - src[pos-1];
+ pos++;
+ }
+ } else if(filtermode == 2) {
+ /* y difference filter */
+ for(x=0;x<width;x++) {
+ dest[pos2++]=src[pos+0] - src[pos-srcwidth+0]; //alpha
+ pos++;
+ }
+ } else if(filtermode == 3) {
+ dest[pos2++]=src[pos+0] - src[pos-srcwidth+0]/2;
+ pos++;
+ /* x+y difference filter */
+ for(x=1;x<width;x++) {
+ dest[pos2++]=src[pos+0] - (src[pos-1+0] + src[pos-srcwidth+0])/2; //alpha
+ pos++;
+ }
+ } else if(filtermode == 4) {
+ dest[pos2++]=src[pos+0] - PaethPredictor(0, src[pos-srcwidth+0], 0);
+ pos++;
+ /* paeth difference filter */
+ for(x=1;x<width;x++) {
+ dest[pos2++]=src[pos+0] - PaethPredictor(src[pos-1+0], src[pos-srcwidth+0], src[pos-1-srcwidth+0]);
+ pos++;
+ }
+ }
+ return filtermode;
+}
+
+static int png_apply_specific_filter_32(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;
+ }
+ }
+ return filtermode;
+}
+
+static int*num_bits_table = 0;
+
+static void make_num_bits_table()
+{
+ if(num_bits_table) return;
+ num_bits_table = malloc(sizeof(num_bits_table[0])*256);
+ int t;
+ for(t=0;t<256;t++) {
+ int bits=0;
+ int v = t;
+ while(v) {
+ bits++;
+ v&=v-1;
+ }
+ num_bits_table[t]=bits;
+ }
+}
+
+static int png_apply_filter(unsigned char*dest, unsigned char*src, int width, int y, int bpp)
+{
+ make_num_bits_table();
+
+ int num_filters = y>0?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<num_filters;f++) {
+ if(bpp==8)
+ png_apply_specific_filter_8(f, dest, src, width);
+ else
+ png_apply_specific_filter_32(f, dest, src, width);
+ int x;
+
+ /* approximation for zlib compressability: test how many different
+ (byte1,byte2) occur */
+ memset(pairs, 0, 8192);
+ int different_pairs = 0;
+ for(x=0;x<w-1;x++) {
+ int v = dest[x+1]<<8|dest[x];
+ int p = v>>3;
+ int b = 1<<(v&7);
+ if(!pairs[p]&b) {
+ pairs[p]|=b;
+ different_pairs ++;
+ }
+ }
+ int energy = different_pairs;
+ if(energy<best_energy) {
+ best_nr = f;
+ best_energy = energy;
+ }
+ }
+ if(bpp==8)
+ png_apply_specific_filter_8(best_nr, dest, src, width);
+ else
+ png_apply_specific_filter_32(best_nr, dest, src, width);
+ free(pairs);
+ return best_nr;
+}
+
+int png_apply_filter_8(unsigned char*dest, unsigned char*src, int width, int y)
+{
+ return png_apply_filter(dest, src, width, y, 8);
+}
+int png_apply_filter_32(unsigned char*dest, unsigned char*src, int width, int y)
+{
+ return png_apply_filter(dest, src, width, y, 32);
+}
+
+EXPORT void savePNG(const char*filename, unsigned char*data, int width, int height, int numcolors)