add redeye and improve shadow removal

bug:7234321
Change-Id: I12c2eb28555d7594fddf86dfa224219b70137681
diff --git a/jni/Android.mk b/jni/Android.mk
index 213663c..92789c1 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -34,7 +34,9 @@
                    filters/hsv.c \
                    filters/vibrance.c \
                    filters/geometry.c \
-                   filters/vignette.c
+                   filters/vignette.c \
+                   filters/redEyeMath.c \
+                   filters/redeye.c
 
 LOCAL_CFLAGS    += -ffast-math -O3 -funroll-loops
 LOCAL_ARM_MODE := arm
diff --git a/jni/filters/filters.h b/jni/filters/filters.h
index 954d02d..d8728f0 100644
--- a/jni/filters/filters.h
+++ b/jni/filters/filters.h
@@ -47,5 +47,6 @@
 
 extern void rgb2hsv( unsigned char *rgb,int rgbOff,unsigned short *hsv,int hsvOff);
 extern void hsv2rgb(unsigned short *hsv,int hsvOff,unsigned char  *rgb,int rgbOff);
-
+extern void filterRedEye(unsigned char *src, unsigned char *dest, int iw, int ih, short *rect);
+extern double fastevalPoly(double *poly,int n, double x);
 #endif // FILTERS_H
diff --git a/jni/filters/hsv.c b/jni/filters/hsv.c
index af438f8..aabd053 100644
--- a/jni/filters/hsv.c
+++ b/jni/filters/hsv.c
@@ -17,128 +17,140 @@
 #include <math.h>
 #include "filters.h"
 
+double fastevalPoly(double *poly,int n, double x){
+
+    double f =x;
+    double sum = poly[0]+poly[1]*f;
+    int i;
+    for (i = 2; i < n; i++) {
+        f*=x;
+        sum += poly[i]*f;
+    }
+    return sum;
+}
+
 void rgb2hsv( unsigned char *rgb,int rgbOff,unsigned short *hsv,int hsvOff)
 {
-      int iMin,iMax,chroma;
-      int ABITS = 4;
-      int HSCALE = 256;
+    int iMin,iMax,chroma;
+    int ABITS = 4;
+    int HSCALE = 256;
 
-      int k1=255 << ABITS;
-      int k2=HSCALE << ABITS;
+    int k1=255 << ABITS;
+    int k2=HSCALE << ABITS;
 
-      int ri = rgb[rgbOff+0];
-      int gi = rgb[rgbOff+1];
-      int bi = rgb[rgbOff+2];
-      short rv,rs,rh;
+    int ri = rgb[rgbOff+0];
+    int gi = rgb[rgbOff+1];
+    int bi = rgb[rgbOff+2];
+    short rv,rs,rh;
 
-      if (ri > gi) {
-          iMax = MAX (ri, bi);
-          iMin = MIN (gi, bi);
-      } else {
-          iMax = MAX (gi, bi);
-          iMin = MIN (ri, bi);
-      }
-
-      chroma = iMax - iMin;
-      // set value
-      rv = (short)( iMax << ABITS);
-
-      // set saturation
-      if (rv == 0)
-        rs = 0;
-      else
-        rs = (short)((k1*chroma)/iMax);
-
-      // set hue
-      if (rs == 0)
-          rh = 0;
-      else {
-          if ( ri == iMax ) {
-            rh  = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
-            if (rh >= k2) rh -= k2;
-          } else if (gi  == iMax)
-            rh  = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
-          else // (bi == iMax )
-            rh  = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
-      }
-      hsv[hsvOff+0] = rv;
-      hsv[hsvOff+1] = rs;
-      hsv[hsvOff+2] = rh;
+    if (ri > gi) {
+        iMax = MAX (ri, bi);
+        iMin = MIN (gi, bi);
+    } else {
+        iMax = MAX (gi, bi);
+        iMin = MIN (ri, bi);
     }
 
- void hsv2rgb(unsigned short *hsv,int hsvOff, unsigned char *rgb,int rgbOff)
- {
-   int ABITS = 4;
-   int HSCALE = 256;
-   int m;
-   int H,X,ih,is,iv;
-   int k1=255<<ABITS;
-   int k2=HSCALE<<ABITS;
-   int k3=1<<(ABITS-1);
-   int rr=0;
-   int rg=0;
-   int rb=0;
-   short cv = hsv[hsvOff+0];
-   short cs = hsv[hsvOff+1];
-   short ch = hsv[hsvOff+2];
+    chroma = iMax - iMin;
+    // set value
+    rv = (short)( iMax << ABITS);
 
-   // set chroma and min component value m
-   //chroma = ( cv * cs )/k1;
-   //m = cv - chroma;
-   m = ((int)cv*(k1 - (int)cs ))/k1;
+    // set saturation
+    if (rv == 0)
+        rs = 0;
+    else
+        rs = (short)((k1*chroma)/iMax);
 
-   // chroma  == 0 <-> cs == 0 --> m=cv
-   if (cs == 0) {
-       rb = ( rg = ( rr =( cv >> ABITS) ));
-   } else {
-     ih=(int)ch;
-     is=(int)cs;
-     iv=(int)cv;
+    // set hue
+    if (rs == 0)
+        rh = 0;
+    else {
+        if ( ri == iMax ) {
+            rh  = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
+            if (rh >= k2) rh -= k2;
+        } else if (gi  == iMax)
+            rh  = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
+        else // (bi == iMax )
+                    rh  = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
+    }
+    hsv[hsvOff+0] = rv;
+    hsv[hsvOff+1] = rs;
+    hsv[hsvOff+2] = rh;
+}
 
-     H = (6*ih)/k2;
-     X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+void hsv2rgb(unsigned short *hsv,int hsvOff, unsigned char *rgb,int rgbOff)
+{
+    int ABITS = 4;
+    int HSCALE = 256;
+    int m;
+    int H,X,ih,is,iv;
+    int k1=255<<ABITS;
+    int k2=HSCALE<<ABITS;
+    int k3=1<<(ABITS-1);
+    int rr=0;
+    int rg=0;
+    int rb=0;
+    short cv = hsv[hsvOff+0];
+    short cs = hsv[hsvOff+1];
+    short ch = hsv[hsvOff+2];
 
-     // removing additional bits --> unit8
-     X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
-     m=m >> ABITS;
+    // set chroma and min component value m
+    //chroma = ( cv * cs )/k1;
+    //m = cv - chroma;
+    m = ((int)cv*(k1 - (int)cs ))/k1;
 
-     // ( chroma + m ) --> cv ;
-     cv=(short) (cv >> ABITS);
-     switch (H) {
-         case 0:
-           rr = cv;
-           rg = X;
-           rb = m;
-           break;
-         case 1:
-           rr = X;
-           rg = cv;
-           rb = m;
-           break;
-         case 2:
-           rr = m;
-           rg = cv;
-           rb = X;
-           break;
-         case 3:
-           rr = m;
-           rg = X;
-           rb = cv;
-           break;
-         case 4:
-           rr = X;
-           rg = m;
-           rb = cv;
-           break;
-         case 5:
-           rr = cv;
-           rg = m ;
-           rb = X;
-           break;
-     }
-   }
-   rgb[rgbOff+0] =  rr;
-   rgb[rgbOff+1] =  rg;
-   rgb[rgbOff+2] =  rb;
- }
+    // chroma  == 0 <-> cs == 0 --> m=cv
+    if (cs == 0) {
+        rb = ( rg = ( rr =( cv >> ABITS) ));
+    } else {
+        ih=(int)ch;
+        is=(int)cs;
+        iv=(int)cv;
+
+        H = (6*ih)/k2;
+        X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;
+
+        // removing additional bits --> unit8
+        X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
+        m=m >> ABITS;
+
+        // ( chroma + m ) --> cv ;
+        cv=(short) (cv >> ABITS);
+        switch (H) {
+        case 0:
+            rr = cv;
+            rg = X;
+            rb = m;
+            break;
+        case 1:
+            rr = X;
+            rg = cv;
+            rb = m;
+            break;
+        case 2:
+            rr = m;
+            rg = cv;
+            rb = X;
+            break;
+        case 3:
+            rr = m;
+            rg = X;
+            rb = cv;
+            break;
+        case 4:
+            rr = X;
+            rg = m;
+            rb = cv;
+            break;
+        case 5:
+            rr = cv;
+            rg = m ;
+            rb = X;
+            break;
+        }
+    }
+    rgb[rgbOff+0] =  rr;
+    rgb[rgbOff+1] =  rg;
+    rgb[rgbOff+2] =  rb;
+}
 
diff --git a/jni/filters/redEyeMath.c b/jni/filters/redEyeMath.c
new file mode 100644
index 0000000..26f3f76
--- /dev/null
+++ b/jni/filters/redEyeMath.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+#include "filters.h"
+
+int value(int r, int g, int b) {
+    return MAX(r, MAX(g, b));
+}
+
+int isRed(unsigned char *src, int p) {
+    int b = src[p + 2];
+    int g = src[p + 1];
+    int r = src[p];
+    int max = MAX(g, b);
+
+    return ((r * 100 / (max + 2) > 160) & (max < 80));
+}
+
+void findPossible(unsigned char *src, unsigned char *mask, int iw, int ih,
+        short *rect) {
+    int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+    int y, x;
+
+    for (y = 0; y < recH; y++) {
+        int sy = (recY + y) * iw;
+        for (x = 0; x < recW; x++) {
+            int p = (recX + x + sy) * 4;
+
+            int b = src[p + 2];
+            int g = src[p + 1];
+            int r = src[p];
+            mask[x + y * recW] = (
+                    mask[x + y * recW] > 0 && (value(r, g, b) > 240) ? 1 : 0);
+
+        }
+
+    }
+}
+
+void findReds(unsigned char *src, unsigned char *mask, int iw, int ih,
+        short *rect) {
+    int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+    int y, x;
+
+    for (y = 0; y < recH; y++) {
+        int sy = (recY + y) * iw;
+        for (x = 0; x < recW; x++) {
+            int p = (recX + x + sy) * 4;
+
+            mask[x + y * recW] = ((isRed(src, p)) ? 1 : 0);
+
+        }
+
+    }
+}
+
+void dialateMaskIfRed(unsigned char *src, int iw, int ih, unsigned char *mask,
+        unsigned char *out, short *rect) {
+    int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+    int y, x;
+
+    for (y = 1; y < recH - 1; y++) {
+        int row = recW * y;
+        int sy = (recY + y) * iw;
+        for (x = 1; x < recW - 1; x++) {
+            int p = (recX + x + sy) * 4;
+
+            char b = (mask[row + x] | mask[row + x + 1] | mask[row + x - 1]
+                    | mask[row + x - recW] | mask[row + x + recW]);
+            if (b != 0 && isRed(src, p))
+                out[row + x] = 1;
+            else
+                out[row + x] = mask[row + x];
+        }
+    }
+}
+
+void dialateMask(unsigned char *mask, unsigned char *out, int mw, int mh) {
+    int y, x;
+    for (y = 1; y < mh - 1; y++) {
+        int row = mw * y;
+        for (x = 1; x < mw - 1; x++) {
+            out[row + x] = (mask[row + x] | mask[row + x + 1]
+                    | mask[row + x - 1] | mask[row + x - mw]
+                    | mask[row + x + mw]);
+        }
+    }
+}
+
+void stuff(int r, int g, int b, unsigned char *img, int off) {
+    img[off + 2] = b;
+    img[off + 1] = g;
+    img[off] = r;
+}
+
+void filterRedEye(unsigned char *src, unsigned char *dest, int iw, int ih, short *rect) {
+    int recX = rect[0], recY = rect[1], recW = rect[2], recH = rect[3];
+    unsigned char *mask1 = (unsigned char *) malloc(recW * recH);
+    unsigned char *mask2 = (unsigned char *)malloc(recW*recH);
+    int QUE_LEN = 100;
+    int y, x, i;
+
+    rect[0] = MAX(rect[0],0);
+    rect[1] = MAX(rect[1],0);
+    rect[2] = MIN(rect[2]+rect[0],iw)-rect[0];
+    rect[3] = MIN(rect[3]+rect[1],ih)-rect[1];
+
+    findReds(src, mask2, iw, ih, rect);
+    dialateMask(mask2, mask1, recW, recH);
+    dialateMask(mask1, mask2, recW, recH);
+    dialateMask(mask2, mask1, recW, recH);
+    dialateMask(mask1, mask2, recW, recH);
+    findPossible(src, mask2, iw, ih, rect);
+    dialateMask(mask2, mask1, recW, recH);
+
+    for (i = 0; i < 12; i++) {
+        dialateMaskIfRed(src, iw, ih, mask1, mask2, rect);
+        dialateMaskIfRed(src, iw, ih, mask2, mask1, rect);
+    }
+    dialateMask(mask1, mask2, recW, recH);
+    dialateMask(mask2, mask1, recW, recH);
+
+    for (y = 3; y < recH-3; y++) {
+        int sy = (recY + y) * iw;
+        for (x = 3; x < recW-3; x++) {
+            int p = (recX + x + sy) * 4;
+
+            int b = src[p + 2];
+            int g = src[p + 1];
+            int r = src[p];
+
+            if (mask1[x + y * recW] != 0) {
+                int m = MAX(g,b);
+                float rr = (r - m) / (float) m;
+                if (rr > .7f && g < 60 && b < 60) {
+                    dest[p + 2] = (0);
+                    dest[p + 1] = (0);
+                    dest[p] = (0);
+                } else {
+                    if (mask2[x + y * recW] != 0) {
+                        stuff(r / 2, g / 2, b / 2, dest, p);
+                    } else
+                        stuff((2 * r) / 3, (2 * g) / 3, (2 * b) / 3, dest, p);
+                }
+
+            } else
+                stuff(r, g, b, dest, p);
+
+            //dest[p + 2] = dest[p + 1] =dest[p]=src[p];
+        }
+
+    }
+
+    free(mask1);
+    free(mask2);
+}
+
+
diff --git a/jni/filters/redeye.c b/jni/filters/redeye.c
new file mode 100644
index 0000000..9a358dd
--- /dev/null
+++ b/jni/filters/redeye.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+#include "filters.h"
+
+ void JNIFUNCF(ImageFilterRedEye, nativeApplyFilter, jobject bitmap, jint width, jint height, jshortArray vrect)
+ {
+     char* destination = 0;
+     AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+     unsigned char * rgb = (unsigned char * )destination;
+     short* rect = (*env)->GetShortArrayElements(env, vrect,0);
+
+     filterRedEye(rgb,rgb,width,height,rect);
+
+     (*env)->ReleaseShortArrayElements(env, vrect, rect, 0);
+     AndroidBitmap_unlockPixels(env, bitmap);
+ }
diff --git a/jni/filters/shadows.c b/jni/filters/shadows.c
index f812b93..38d64c8 100644
--- a/jni/filters/shadows.c
+++ b/jni/filters/shadows.c
@@ -17,23 +17,41 @@
 #include <math.h>
 #include "filters.h"
 
- void JNIFUNCF(ImageFilterShadows, nativeApplyFilter, jobject bitmap, jint width, jint height, jshortArray vlut)
- {
-     char* destination = 0;
-     AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
-     unsigned char * rgb = (unsigned char * )destination;
-     int i;
-     int len = width * height * 4;
-     short* lut = (*env)->GetShortArrayElements(env, vlut,0);
-     unsigned short * hsv = (unsigned short *)malloc(3*sizeof(short));
-     for (i = 0; i < len; i+=4)
-     {
-       rgb2hsv(rgb,i,hsv,0);
-       hsv[0] = lut[hsv[0]];
-       hsv2rgb(hsv,0, rgb,i);
-     }
+void JNIFUNCF(ImageFilterShadows, nativeApplyFilter, jobject bitmap, jint width, jint height, float scale){
+    double shadowFilterMap[] = {
+            -0.00591,  0.0001,
+             1.16488,  0.01668,
+            -0.18027, -0.06791,
+            -0.12625,  0.09001,
+             0.15065, -0.03897
+    };
 
-     (*env)->ReleaseShortArrayElements(env, vlut, lut, 0);
-     free(hsv);
-     AndroidBitmap_unlockPixels(env, bitmap);
- }
+    char* destination = 0;
+    AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+    unsigned char * rgb = (unsigned char * )destination;
+    int i;
+    double s = (scale>=0)?scale:scale/5;
+    int len = width * height * 4;
+
+    double *poly = (double *) malloc(5*sizeof(double));
+    for (i = 0; i < 5; i++) {
+        poly[i] = fastevalPoly(shadowFilterMap+i*2,2 , s);
+    }
+
+    unsigned short * hsv = (unsigned short *)malloc(3*sizeof(short));
+
+    for (i = 0; i < len; i+=4)
+    {
+        rgb2hsv(rgb,i,hsv,0);
+
+        double v = (fastevalPoly(poly,5,hsv[0]/4080.)*4080);
+        if (v>4080) v = 4080;
+        hsv[0] = (unsigned short) ((v>0)?v:0);
+
+        hsv2rgb(hsv,0, rgb,i);
+    }
+
+    free(poly);
+    free(hsv);
+    AndroidBitmap_unlockPixels(env, bitmap);
+}