summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--jni/Android.mk52
-rw-r--r--jni/Application.mk1
-rw-r--r--jni/filters/bwfilter.c55
-rw-r--r--jni/filters/contrast.c56
-rw-r--r--jni/filters/edge.c126
-rw-r--r--jni/filters/exposure.c37
-rw-r--r--jni/filters/filters.h53
-rw-r--r--jni/filters/fx.c88
-rw-r--r--jni/filters/geometry.c184
-rw-r--r--jni/filters/gradient.c65
-rw-r--r--jni/filters/highlight.c40
-rw-r--r--jni/filters/hsv.c156
-rw-r--r--jni/filters/hue.c46
-rw-r--r--jni/filters/kmeans.cc81
-rw-r--r--jni/filters/kmeans.h232
-rw-r--r--jni/filters/negative.c33
-rw-r--r--jni/filters/redEyeMath.c172
-rw-r--r--jni/filters/redeye.c31
-rw-r--r--jni/filters/saturated.c53
-rw-r--r--jni/filters/shadows.c57
-rw-r--r--jni/filters/tinyplanet.cc150
-rw-r--r--jni/filters/vibrance.c62
-rw-r--r--jni/filters/vignette.c49
-rw-r--r--jni/filters/wbalance.c169
-rw-r--r--jni/jni_egl_fence.cpp78
-rw-r--r--jni/jni_egl_fence.h33
26 files changed, 2159 insertions, 0 deletions
diff --git a/jni/Android.mk b/jni/Android.mk
new file mode 100644
index 000000000..e612486e1
--- /dev/null
+++ b/jni/Android.mk
@@ -0,0 +1,52 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_SRC_FILES := jni_egl_fence.cpp
+
+LOCAL_SDK_VERSION := 9
+
+LOCAL_LDFLAGS := -llog -lEGL
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libjni_eglfence
+
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Filtershow
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_LDFLAGS := -llog -ljnigraphics
+LOCAL_SDK_VERSION := 9
+LOCAL_MODULE := libjni_filtershow_filters
+LOCAL_SRC_FILES := filters/gradient.c \
+ filters/saturated.c \
+ filters/exposure.c \
+ filters/edge.c \
+ filters/contrast.c \
+ filters/hue.c \
+ filters/shadows.c \
+ filters/highlight.c \
+ filters/hsv.c \
+ filters/vibrance.c \
+ filters/geometry.c \
+ filters/negative.c \
+ filters/vignette.c \
+ filters/redEyeMath.c \
+ filters/fx.c \
+ filters/wbalance.c \
+ filters/redeye.c \
+ filters/bwfilter.c \
+ filters/tinyplanet.cc \
+ filters/kmeans.cc
+
+LOCAL_CFLAGS += -ffast-math -O3 -funroll-loops
+LOCAL_ARM_MODE := arm
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/jni/Application.mk b/jni/Application.mk
new file mode 100644
index 000000000..22d188e59
--- /dev/null
+++ b/jni/Application.mk
@@ -0,0 +1 @@
+APP_PLATFORM := android-9
diff --git a/jni/filters/bwfilter.c b/jni/filters/bwfilter.c
new file mode 100644
index 000000000..f7fb31ad1
--- /dev/null
+++ b/jni/filters/bwfilter.c
@@ -0,0 +1,55 @@
+/*
+ * 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(ImageFilterBwFilter, nativeApplyFilter, jobject bitmap, jint width, jint height, jint rw, jint gw, jint bw)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ float sr = rw;
+ float sg = gw;
+ float sb = bw;
+
+ float min = MIN(sg,sb);
+ min = MIN(sr,min);
+ float max = MAX(sg,sb);
+ max = MAX(sr,max);
+ float avg = (min+max)/2;
+ sb /= avg;
+ sg /= avg;
+ sr /= avg;
+ int i;
+ int len = width * height * 4;
+
+ for (i = 0; i < len; i+=4)
+ {
+ float r = sr *rgb[RED];
+ float g = sg *rgb[GREEN];
+ float b = sb *rgb[BLUE];
+ min = MIN(g,b);
+ min = MIN(r,min);
+ max = MAX(g,b);
+ max = MAX(r,max);
+ avg =(min+max)/2;
+ rgb[RED] = CLAMP(avg);
+ rgb[GREEN] = rgb[RED];
+ rgb[BLUE] = rgb[RED];
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/contrast.c b/jni/filters/contrast.c
new file mode 100644
index 000000000..b04e9364e
--- /dev/null
+++ b/jni/filters/contrast.c
@@ -0,0 +1,56 @@
+/*
+ * 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"
+
+unsigned char clamp(int c)
+{
+ int N = 255;
+ c &= ~(c >> 31);
+ c -= N;
+ c &= (c >> 31);
+ c += N;
+ return (unsigned char) c;
+}
+
+int clampMax(int c,int max)
+{
+ c &= ~(c >> 31);
+ c -= max;
+ c &= (c >> 31);
+ c += max;
+ return c;
+}
+
+void JNIFUNCF(ImageFilterContrast, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloat bright)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ int i;
+ int len = width * height * 4;
+ float m = (float)pow(2, bright/100.);
+ float c = 127-m*127;
+
+ for (i = 0; i < len; i+=4) {
+ rgb[RED] = clamp((int)(m*rgb[RED]+c));
+ rgb[GREEN] = clamp((int)(m*rgb[GREEN]+c));
+ rgb[BLUE] = clamp((int)(m*rgb[BLUE]+c));
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
+
diff --git a/jni/filters/edge.c b/jni/filters/edge.c
new file mode 100644
index 000000000..9f5d88f77
--- /dev/null
+++ b/jni/filters/edge.c
@@ -0,0 +1,126 @@
+/*
+ * 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(ImageFilterEdge, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloat p)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+
+ // using contrast function:
+ // f(v) = exp(-alpha * v^beta)
+ // use beta ~ 1
+
+ float const alpha = 5.0f;
+ float const beta = p;
+ float const c_min = 100.0f;
+ float const c_max = 500.0f;
+
+ // pixels must be 4 bytes
+ char * dst = destination;
+
+ int j, k;
+ char * ptr = destination;
+ int row_stride = 4 * width;
+
+ // set 2 row buffer (avoids bitmap copy)
+ int buf_len = 2 * row_stride;
+ char buf[buf_len];
+ int buf_row_ring = 0;
+
+ // set initial buffer to black
+ memset(buf, 0, buf_len * sizeof(char));
+ for (j = 3; j < buf_len; j+=4) {
+ *(buf + j) = 255; // set initial alphas
+ }
+
+ // apply sobel filter
+ for (j = 1; j < height - 1; j++) {
+
+ for (k = 1; k < width - 1; k++){
+ int loc = j * row_stride + k * 4;
+
+ float bestx = 0.0f;
+ int l;
+ for (l = 0; l < 3; l++) {
+ float tmp = 0.0f;
+ tmp += *(ptr + (loc - row_stride + 4 + l));
+ tmp += *(ptr + (loc + 4 + l)) * 2.0f;
+ tmp += *(ptr + (loc + row_stride + 4 + l));
+ tmp -= *(ptr + (loc - row_stride - 4 + l));
+ tmp -= *(ptr + (loc - 4 + l)) * 2.0f;
+ tmp -= *(ptr + (loc + row_stride - 4 + l));
+ if (fabs(tmp) > fabs(bestx)) {
+ bestx = tmp;
+ }
+ }
+
+ float besty = 0.0f;
+ for (l = 0; l < 3; l++) {
+ float tmp = 0.0f;
+ tmp -= *(ptr + (loc - row_stride - 4 + l));
+ tmp -= *(ptr + (loc - row_stride + l)) * 2.0f;
+ tmp -= *(ptr + (loc - row_stride + 4 + l));
+ tmp += *(ptr + (loc + row_stride - 4 + l));
+ tmp += *(ptr + (loc + row_stride + l)) * 2.0f;
+ tmp += *(ptr + (loc + row_stride + 4 + l));
+ if (fabs(tmp) > fabs(besty)) {
+ besty = tmp;
+ }
+ }
+
+ // compute gradient magnitude
+ float mag = sqrt(bestx * bestx + besty * besty);
+
+ // clamp
+ mag = MIN(MAX(c_min, mag), c_max);
+
+ // scale to [0, 1]
+ mag = (mag - c_min) / (c_max - c_min);
+
+ float ret = 1.0f - exp (- alpha * pow(mag, beta));
+ ret = 255 * ret;
+
+ int off = k * 4;
+ *(buf + buf_row_ring + off) = ret;
+ *(buf + buf_row_ring + off + 1) = ret;
+ *(buf + buf_row_ring + off + 2) = ret;
+ *(buf + buf_row_ring + off + 3) = *(ptr + loc + 3);
+ }
+
+ buf_row_ring += row_stride;
+ buf_row_ring %= buf_len;
+
+ if (j - 1 >= 0) {
+ memcpy((dst + row_stride * (j - 1)), (buf + buf_row_ring), row_stride * sizeof(char));
+ }
+
+ }
+ buf_row_ring += row_stride;
+ buf_row_ring %= buf_len;
+ int second_last_row = row_stride * (height - 2);
+ memcpy((dst + second_last_row), (buf + buf_row_ring), row_stride * sizeof(char));
+
+ // set last row to black
+ int last_row = row_stride * (height - 1);
+ memset((dst + last_row), 0, row_stride * sizeof(char));
+ for (j = 3; j < row_stride; j+=4) {
+ *(dst + last_row + j) = 255; // set alphas
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/exposure.c b/jni/filters/exposure.c
new file mode 100644
index 000000000..6b32798c8
--- /dev/null
+++ b/jni/filters/exposure.c
@@ -0,0 +1,37 @@
+/*
+ * 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 "filters.h"
+
+void JNIFUNCF(ImageFilterExposure, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloat bright)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ int i;
+ int len = width * height * 4;
+
+ int m = (255-bright);
+
+ for (i = 0; i < len; i+=4)
+ {
+ rgb[RED] = clamp((255*(rgb[RED]))/m);
+ rgb[GREEN] = clamp((255*(rgb[GREEN]))/m);
+ rgb[BLUE] = clamp((255*(rgb[BLUE]))/m);
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
+
diff --git a/jni/filters/filters.h b/jni/filters/filters.h
new file mode 100644
index 000000000..14b69cdd4
--- /dev/null
+++ b/jni/filters/filters.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef FILTERS_H
+#define FILTERS_H
+
+#include <jni.h>
+#include <string.h>
+#include <android/log.h>
+#include <android/bitmap.h>
+
+typedef unsigned int Color;
+
+#define SetColor(a, r, g, b) ((a << 24) | (b << 16) | (g << 8) | (r << 0));
+#define GetA(color) (((color) >> 24) & 0xFF)
+#define GetB(color) (((color) >> 16) & 0xFF)
+#define GetG(color) (((color) >> 8) & 0xFF)
+#define GetR(color) (((color) >> 0) & 0xFF)
+
+#define MIN(a, b) (a < b ? a : b)
+#define MAX(a, b) (a > b ? a : b)
+
+#define LOG(msg...) __android_log_print(ANDROID_LOG_VERBOSE, "NativeFilters", msg)
+
+#define JNIFUNCF(cls, name, vars...) Java_com_android_gallery3d_filtershow_filters_ ## cls ## _ ## name(JNIEnv* env, jobject obj, vars)
+
+#define RED i
+#define GREEN i+1
+#define BLUE i+2
+#define ALPHA i+3
+#define CLAMP(c) (MAX(0, MIN(255, c)))
+
+__inline__ unsigned char clamp(int c);
+__inline__ int clampMax(int c,int max);
+
+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/fx.c b/jni/filters/fx.c
new file mode 100644
index 000000000..c3c9cbdc6
--- /dev/null
+++ b/jni/filters/fx.c
@@ -0,0 +1,88 @@
+/*
+ * 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 "filters.h"
+
+__inline__ int interp(unsigned char *src, int p , int *off ,float dr,float dg, float db){
+
+ float fr00 = (src[p+off[0]])*(1-dr)+(src[p+off[1]])*dr;
+ float fr01 = (src[p+off[2]])*(1-dr)+(src[p+off[3]])*dr;
+ float fr10 = (src[p+off[4]])*(1-dr)+(src[p+off[5]])*dr;
+ float fr11 = (src[p+off[6]])*(1-dr)+(src[p+off[7]])*dr;
+ float frb0 = fr00 * (1-db)+fr01*db;
+ float frb1 = fr10 * (1-db)+fr11*db;
+ float frbg = frb0 * (1-dg)+frb1*dg;
+
+ return (int)frbg ;
+}
+
+void JNIFUNCF(ImageFilterFx, nativeApplyFilter, jobject bitmap, jint width, jint height,
+ jobject lutbitmap, jint lutwidth, jint lutheight,
+ jint start, jint end)
+{
+ char* destination = 0;
+ char* lut = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ AndroidBitmap_lockPixels(env, lutbitmap, (void**) &lut);
+ unsigned char * rgb = (unsigned char * )destination;
+ unsigned char * lutrgb = (unsigned char * )lut;
+ int lutdim_r = lutheight;
+ int lutdim_g = lutheight;;
+ int lutdim_b = lutwidth/lutheight;;
+ int STEP = 4;
+
+ int off[8] = {
+ 0,
+ STEP*1,
+ STEP*lutdim_r,
+ STEP*(lutdim_r + 1),
+ STEP*(lutdim_r*lutdim_b),
+ STEP*(lutdim_r*lutdim_b+1),
+ STEP*(lutdim_r*lutdim_b+lutdim_r),
+ STEP*(lutdim_r*lutdim_b+lutdim_r + 1)
+ };
+
+ float scale_R = (lutdim_r-1.f)/256.f;
+ float scale_G = (lutdim_g-1.f)/256.f;
+ float scale_B = (lutdim_b-1.f)/256.f;
+
+ int i;
+ for (i = start; i < end; i+= STEP)
+ {
+ int r = rgb[RED];
+ int g = rgb[GREEN];
+ int b = rgb[BLUE];
+
+ float fb = b*scale_B;
+ float fg = g*scale_G;
+ float fr = r*scale_R;
+ int lut_b = (int)fb;
+ int lut_g = (int)fg;
+ int lut_r = (int)fr;
+ int p = lut_r+lut_b*lutdim_r+lut_g*lutdim_r*lutdim_b;
+ p*=STEP;
+ float dr = fr-lut_r;
+ float dg = fg-lut_g;
+ float db = fb-lut_b;
+ rgb[RED] = clamp(interp(lutrgb,p ,off,dr,dg,db));
+ rgb[GREEN] = clamp(interp(lutrgb,p+1,off,dr,dg,db));
+ rgb[BLUE] = clamp(interp(lutrgb,p+2,off,dr,dg,db));
+
+ }
+
+ AndroidBitmap_unlockPixels(env, bitmap);
+ AndroidBitmap_unlockPixels(env, lutbitmap);
+}
diff --git a/jni/filters/geometry.c b/jni/filters/geometry.c
new file mode 100644
index 000000000..a0b5aaacf
--- /dev/null
+++ b/jni/filters/geometry.c
@@ -0,0 +1,184 @@
+/*
+ * 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 "filters.h"
+#include <stdio.h>
+
+__inline__ void flipVertical(char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ //Vertical
+ size_t cpy_bytes = sizeof(char) * 4;
+ int width = cpy_bytes * srcWidth;
+ int length = srcHeight;
+ int total = length * width;
+ size_t bytes_to_copy = sizeof(char) * width;
+ int i = 0;
+ int temp = total - width;
+ for (i = 0; i < total; i += width) {
+ memcpy(destination + temp - i, source + i, bytes_to_copy);
+ }
+}
+
+__inline__ void flipHorizontal(char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ //Horizontal
+ size_t cpy_bytes = sizeof(char) * 4;
+ int width = cpy_bytes * srcWidth;
+ int length = srcHeight;
+ int total = length * width;
+ int i = 0;
+ int j = 0;
+ int temp = 0;
+ for (i = 0; i < total; i+= width) {
+ temp = width + i - cpy_bytes;
+ for (j = 0; j < width; j+=cpy_bytes) {
+ memcpy(destination + temp - j, source + i + j, cpy_bytes);
+ }
+ }
+}
+
+__inline__ void flip_fun(int flip, char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ int horiz = (flip & 1) != 0;
+ int vert = (flip & 2) != 0;
+ if (horiz && vert){
+ int arr_len = dstWidth * dstHeight * sizeof(char) * 4;
+ char* temp = (char *) malloc(arr_len);
+ flipHorizontal(source, srcWidth, srcHeight, temp, dstWidth, dstHeight);
+ flipVertical(temp, dstWidth, dstHeight, destination, dstWidth, dstHeight);
+ free(temp);
+ return;
+ }
+ if (horiz){
+ flipHorizontal(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ return;
+ }
+ if (vert){
+ flipVertical(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ return;
+ }
+}
+
+//90 CCW (opposite of what's used in UI?)
+__inline__ void rotate90(char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ size_t cpy_bytes = sizeof(char) * 4;
+ int width = cpy_bytes * srcWidth;
+ int length = srcHeight;
+ int total = length * width;
+ int i = 0;
+ int j = 0;
+ for (j = 0; j < length * cpy_bytes; j+= cpy_bytes){
+ for (i = 0; i < width; i+=cpy_bytes){
+ int column_disp = (width - cpy_bytes - i) * length;
+ int row_disp = j;
+ memcpy(destination + column_disp + row_disp , source + j * srcWidth + i, cpy_bytes);
+ }
+ }
+}
+
+__inline__ void rotate180(char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ flip_fun(3, source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+}
+
+__inline__ void rotate270(char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ rotate90(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ flip_fun(3, destination, dstWidth, dstHeight, destination, dstWidth, dstHeight);
+}
+
+// rotate == 1 is 90 degrees, 2 is 180, 3 is 270 (positive is CCW).
+__inline__ void rotate_fun(int rotate, char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
+ switch( rotate )
+ {
+ case 1:
+ rotate90(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ break;
+ case 2:
+ rotate180(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ break;
+ case 3:
+ rotate270(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ break;
+ default:
+ break;
+ }
+}
+
+__inline__ void crop(char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight, int offsetWidth, int offsetHeight){
+ size_t cpy_bytes = sizeof(char) * 4;
+ int row_width = cpy_bytes * srcWidth;
+ int new_row_width = cpy_bytes * dstWidth;
+ if ((srcWidth > dstWidth + offsetWidth) || (srcHeight > dstHeight + offsetHeight)){
+ return;
+ }
+ int i = 0;
+ int j = 0;
+ for (j = offsetHeight; j < offsetHeight + dstHeight; j++){
+ memcpy(destination + (j - offsetHeight) * new_row_width, source + j * row_width + offsetWidth * cpy_bytes, cpy_bytes * dstWidth );
+ }
+}
+
+void JNIFUNCF(ImageFilterGeometry, nativeApplyFilterFlip, jobject src, jint srcWidth, jint srcHeight, jobject dst, jint dstWidth, jint dstHeight, jint flip) {
+ char* destination = 0;
+ char* source = 0;
+ if (srcWidth != dstWidth || srcHeight != dstHeight) {
+ return;
+ }
+ AndroidBitmap_lockPixels(env, src, (void**) &source);
+ AndroidBitmap_lockPixels(env, dst, (void**) &destination);
+ flip_fun(flip, source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ AndroidBitmap_unlockPixels(env, dst);
+ AndroidBitmap_unlockPixels(env, src);
+}
+
+void JNIFUNCF(ImageFilterGeometry, nativeApplyFilterRotate, jobject src, jint srcWidth, jint srcHeight, jobject dst, jint dstWidth, jint dstHeight, jint rotate) {
+ char* destination = 0;
+ char* source = 0;
+ int len = dstWidth * dstHeight * 4;
+ AndroidBitmap_lockPixels(env, src, (void**) &source);
+ AndroidBitmap_lockPixels(env, dst, (void**) &destination);
+ rotate_fun(rotate, source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
+ AndroidBitmap_unlockPixels(env, dst);
+ AndroidBitmap_unlockPixels(env, src);
+}
+
+void JNIFUNCF(ImageFilterGeometry, nativeApplyFilterCrop, jobject src, jint srcWidth, jint srcHeight, jobject dst, jint dstWidth, jint dstHeight, jint offsetWidth, jint offsetHeight) {
+ char* destination = 0;
+ char* source = 0;
+ int len = dstWidth * dstHeight * 4;
+ AndroidBitmap_lockPixels(env, src, (void**) &source);
+ AndroidBitmap_lockPixels(env, dst, (void**) &destination);
+ crop(source, srcWidth, srcHeight, destination, dstWidth, dstHeight, offsetWidth, offsetHeight);
+ AndroidBitmap_unlockPixels(env, dst);
+ AndroidBitmap_unlockPixels(env, src);
+}
+
+void JNIFUNCF(ImageFilterGeometry, nativeApplyFilterStraighten, jobject src, jint srcWidth, jint srcHeight, jobject dst, jint dstWidth, jint dstHeight, jfloat straightenAngle) {
+ char* destination = 0;
+ char* source = 0;
+ int len = dstWidth * dstHeight * 4;
+ AndroidBitmap_lockPixels(env, src, (void**) &source);
+ AndroidBitmap_lockPixels(env, dst, (void**) &destination);
+ // TODO: implement straighten
+ int i = 0;
+ for (; i < len; i += 4) {
+ int r = source[RED];
+ int g = source[GREEN];
+ int b = source[BLUE];
+ destination[RED] = 128;
+ destination[GREEN] = g;
+ destination[BLUE] = 128;
+ }
+ AndroidBitmap_unlockPixels(env, dst);
+ AndroidBitmap_unlockPixels(env, src);
+}
+
diff --git a/jni/filters/gradient.c b/jni/filters/gradient.c
new file mode 100644
index 000000000..1a8569786
--- /dev/null
+++ b/jni/filters/gradient.c
@@ -0,0 +1,65 @@
+/*
+ * 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 "filters.h"
+
+void JNIFUNCF(ImageFilter, nativeApplyGradientFilter, jobject bitmap, jint width, jint height,
+ jintArray redGradient, jintArray greenGradient, jintArray blueGradient)
+{
+ char* destination = 0;
+ jint* redGradientArray = 0;
+ jint* greenGradientArray = 0;
+ jint* blueGradientArray = 0;
+ if (redGradient)
+ redGradientArray = (*env)->GetIntArrayElements(env, redGradient, NULL);
+ if (greenGradient)
+ greenGradientArray = (*env)->GetIntArrayElements(env, greenGradient, NULL);
+ if (blueGradient)
+ blueGradientArray = (*env)->GetIntArrayElements(env, blueGradient, NULL);
+
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ int i;
+ int len = width * height * 4;
+ for (i = 0; i < len; i+=4)
+ {
+ if (redGradient)
+ {
+ int r = destination[RED];
+ r = redGradientArray[r];
+ destination[RED] = r;
+ }
+ if (greenGradient)
+ {
+ int g = destination[GREEN];
+ g = greenGradientArray[g];
+ destination[GREEN] = g;
+ }
+ if (blueGradient)
+ {
+ int b = destination[BLUE];
+ b = blueGradientArray[b];
+ destination[BLUE] = b;
+ }
+ }
+ if (redGradient)
+ (*env)->ReleaseIntArrayElements(env, redGradient, redGradientArray, 0);
+ if (greenGradient)
+ (*env)->ReleaseIntArrayElements(env, greenGradient, greenGradientArray, 0);
+ if (blueGradient)
+ (*env)->ReleaseIntArrayElements(env, blueGradient, blueGradientArray, 0);
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
+
diff --git a/jni/filters/highlight.c b/jni/filters/highlight.c
new file mode 100644
index 000000000..fe9b88f94
--- /dev/null
+++ b/jni/filters/highlight.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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(ImageFilterHighlights, nativeApplyFilter, jobject bitmap,
+ jint width, jint height, jfloatArray luminanceMap){
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ int i;
+ int len = width * height * 4;
+ jfloat* lum = (*env)->GetFloatArrayElements(env, luminanceMap,0);
+ unsigned short * hsv = (unsigned short *)malloc(3*sizeof(short));
+
+ for (i = 0; i < len; i+=4)
+ {
+ rgb2hsv(rgb,i,hsv,0);
+ int v = clampMax(hsv[0],4080);
+ hsv[0] = (unsigned short) clampMax(lum[((255*v)/4080)]*4080,4080);
+ hsv2rgb(hsv,0, rgb,i);
+ }
+
+ free(hsv);
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/hsv.c b/jni/filters/hsv.c
new file mode 100644
index 000000000..aabd053fe
--- /dev/null
+++ b/jni/filters/hsv.c
@@ -0,0 +1,156 @@
+/*
+ * 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"
+
+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 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;
+
+ 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;
+}
+
+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];
+
+ // set chroma and min component value m
+ //chroma = ( cv * cs )/k1;
+ //m = cv - chroma;
+ m = ((int)cv*(k1 - (int)cs ))/k1;
+
+ // 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/hue.c b/jni/filters/hue.c
new file mode 100644
index 000000000..a4aef936d
--- /dev/null
+++ b/jni/filters/hue.c
@@ -0,0 +1,46 @@
+/*
+ * 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 "filters.h"
+
+void JNIFUNCF(ImageFilterHue, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloatArray matrix)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ unsigned char * rgb = (unsigned char * )destination;
+ int i;
+ int len = width * height * 4;
+ jfloat* mat = (*env)->GetFloatArrayElements(env, matrix,0);
+
+ for (i = 0; i < len; i+=4)
+ {
+ int r = rgb[RED];
+ int g = rgb[GREEN];
+ int b = rgb[BLUE];
+
+ float rf = r*mat[0] + g*mat[4] + b*mat[8] + mat[12];
+ float gf = r*mat[1] + g*mat[5] + b*mat[9] + mat[13];
+ float bf = r*mat[2] + g*mat[6] + b*mat[10] + mat[14];
+
+ rgb[RED] = clamp((int)rf);
+ rgb[GREEN] = clamp((int)gf);
+ rgb[BLUE] = clamp((int)bf);
+ }
+
+ (*env)->ReleaseFloatArrayElements(env, matrix, mat, 0);
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
+
diff --git a/jni/filters/kmeans.cc b/jni/filters/kmeans.cc
new file mode 100644
index 000000000..97cead7bc
--- /dev/null
+++ b/jni/filters/kmeans.cc
@@ -0,0 +1,81 @@
+/*
+ * 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 "filters.h"
+#include "kmeans.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * For reasonable speeds:
+ * k < 30
+ * small_ds_bitmap width/height < 64 pixels.
+ * large_ds_bitmap width/height < 512 pixels
+ *
+ * bad for high-frequency image noise
+ */
+
+void JNIFUNCF(ImageFilterKMeans, nativeApplyFilter, jobject bitmap, jint width, jint height,
+ jobject large_ds_bitmap, jint lwidth, jint lheight, jobject small_ds_bitmap,
+ jint swidth, jint sheight, jint p, jint seed)
+{
+ char* destination = 0;
+ char* larger_ds_dst = 0;
+ char* smaller_ds_dst = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ AndroidBitmap_lockPixels(env, large_ds_bitmap, (void**) &larger_ds_dst);
+ AndroidBitmap_lockPixels(env, small_ds_bitmap, (void**) &smaller_ds_dst);
+ unsigned char * dst = (unsigned char *) destination;
+
+ unsigned char * small_ds = (unsigned char *) smaller_ds_dst;
+ unsigned char * large_ds = (unsigned char *) larger_ds_dst;
+
+ // setting for small bitmap
+ int len = swidth * sheight * 4;
+ int dimension = 3;
+ int stride = 4;
+ int iterations = 20;
+ int k = p;
+ unsigned int s = seed;
+ unsigned char finalCentroids[k * stride];
+
+ // get initial picks from small downsampled image
+ runKMeans<unsigned char, int>(k, finalCentroids, small_ds, len, dimension,
+ stride, iterations, s);
+
+
+ len = lwidth * lheight * 4;
+ iterations = 8;
+ unsigned char nextCentroids[k * stride];
+
+ // run kmeans on large downsampled image
+ runKMeansWithPicks<unsigned char, int>(k, nextCentroids, large_ds, len,
+ dimension, stride, iterations, finalCentroids);
+
+ len = width * height * 4;
+
+ // apply to final image
+ applyCentroids<unsigned char, int>(k, nextCentroids, dst, len, dimension, stride);
+
+ AndroidBitmap_unlockPixels(env, small_ds_bitmap);
+ AndroidBitmap_unlockPixels(env, large_ds_bitmap);
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/jni/filters/kmeans.h b/jni/filters/kmeans.h
new file mode 100644
index 000000000..24506058a
--- /dev/null
+++ b/jni/filters/kmeans.h
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#ifndef KMEANS_H
+#define KMEANS_H
+
+#include <cstdlib>
+#include <math.h>
+
+// Helper functions
+
+template <typename T, typename N>
+inline void sum(T values[], int len, int dimension, int stride, N dst[]) {
+ int x, y;
+ // zero out dst vector
+ for (x = 0; x < dimension; x++) {
+ dst[x] = 0;
+ }
+ for (x = 0; x < len; x+= stride) {
+ for (y = 0; y < dimension; y++) {
+ dst[y] += values[x + y];
+ }
+ }
+}
+
+template <typename T, typename N>
+inline void set(T val1[], N val2[], int dimension) {
+ int x;
+ for (x = 0; x < dimension; x++) {
+ val1[x] = val2[x];
+ }
+}
+
+template <typename T, typename N>
+inline void add(T val[], N dst[], int dimension) {
+ int x;
+ for (x = 0; x < dimension; x++) {
+ dst[x] += val[x];
+ }
+}
+
+template <typename T, typename N>
+inline void divide(T dst[], N divisor, int dimension) {
+ int x;
+ if (divisor == 0) {
+ return;
+ }
+ for (x = 0; x < dimension; x++) {
+ dst[x] /= divisor;
+ }
+}
+
+/**
+ * Calculates euclidean distance.
+ */
+
+template <typename T, typename N>
+inline N euclideanDist(T val1[], T val2[], int dimension) {
+ int x;
+ N sum = 0;
+ for (x = 0; x < dimension; x++) {
+ N diff = (N) val1[x] - (N) val2[x];
+ sum += diff * diff;
+ }
+ return sqrt(sum);
+}
+
+// K-Means
+
+
+/**
+ * Picks k random starting points from the data set.
+ */
+template <typename T>
+void initialPickHeuristicRandom(int k, T values[], int len, int dimension, int stride, T dst[],
+ unsigned int seed) {
+ int x, z, num_vals, cntr;
+ num_vals = len / stride;
+ cntr = 0;
+ srand(seed);
+ unsigned int r_vals[k];
+ unsigned int r;
+
+ for (x = 0; x < k; x++) {
+
+ // ensure randomly chosen value is unique
+ int r_check = 0;
+ while (r_check == 0) {
+ r = (unsigned int) rand() % num_vals;
+ r_check = 1;
+ for (z = 0; z < x; z++) {
+ if (r == r_vals[z]) {
+ r_check = 0;
+ }
+ }
+ }
+ r_vals[x] = r;
+ r *= stride;
+
+ // set dst to be randomly chosen value
+ set<T,T>(dst + cntr, values + r, dimension);
+ cntr += stride;
+ }
+}
+
+/**
+ * Finds index of closet centroid to a value
+ */
+template <typename T, typename N>
+inline int findClosest(T values[], T oldCenters[], int dimension, int stride, int pop_size) {
+ int best_ind = 0;
+ N best_len = euclideanDist <T, N>(values, oldCenters, dimension);
+ int y;
+ for (y = stride; y < pop_size; y+=stride) {
+ N l = euclideanDist <T, N>(values, oldCenters + y, dimension);
+ if (l < best_len) {
+ best_len = l;
+ best_ind = y;
+ }
+ }
+ return best_ind;
+}
+
+/**
+ * Calculates new centroids by averaging value clusters for old centroids.
+ */
+template <typename T, typename N>
+int calculateNewCentroids(int k, T values[], int len, int dimension, int stride, T oldCenters[],
+ T dst[]) {
+ int x, pop_size;
+ pop_size = k * stride;
+ int popularities[k];
+ N tmp[pop_size];
+
+ //zero popularities
+ memset(popularities, 0, sizeof(int) * k);
+ // zero dst, and tmp
+ for (x = 0; x < pop_size; x++) {
+ tmp[x] = 0;
+ }
+
+ // put summation for each k in tmp
+ for (x = 0; x < len; x+=stride) {
+ int best = findClosest<T, N>(values + x, oldCenters, dimension, stride, pop_size);
+ add<T, N>(values + x, tmp + best, dimension);
+ popularities[best / stride]++;
+
+ }
+
+ int ret = 0;
+ int y;
+ // divide to get centroid and set dst to result
+ for (x = 0; x < pop_size; x+=stride) {
+ divide<N, int>(tmp + x, popularities[x / stride], dimension);
+ for (y = 0; y < dimension; y++) {
+ if ((dst + x)[y] != (T) ((tmp + x)[y])) {
+ ret = 1;
+ }
+ }
+ set(dst + x, tmp + x, dimension);
+ }
+ return ret;
+}
+
+template <typename T, typename N>
+void runKMeansWithPicks(int k, T finalCentroids[], T values[], int len, int dimension, int stride,
+ int iterations, T initialPicks[]){
+ int k_len = k * stride;
+ int x;
+
+ // zero newCenters
+ for (x = 0; x < k_len; x++) {
+ finalCentroids[x] = 0;
+ }
+
+ T * c1 = initialPicks;
+ T * c2 = finalCentroids;
+ T * temp;
+ int ret = 1;
+ for (x = 0; x < iterations; x++) {
+ ret = calculateNewCentroids<T, N>(k, values, len, dimension, stride, c1, c2);
+ temp = c1;
+ c1 = c2;
+ c2 = temp;
+ if (ret == 0) {
+ x = iterations;
+ }
+ }
+ set<T, T>(finalCentroids, c1, dimension);
+}
+
+/**
+ * Runs the k-means algorithm on dataset values with some initial centroids.
+ */
+template <typename T, typename N>
+void runKMeans(int k, T finalCentroids[], T values[], int len, int dimension, int stride,
+ int iterations, unsigned int seed){
+ int k_len = k * stride;
+ T initialPicks [k_len];
+ initialPickHeuristicRandom<T>(k, values, len, dimension, stride, initialPicks, seed);
+
+ runKMeansWithPicks<T, N>(k, finalCentroids, values, len, dimension, stride,
+ iterations, initialPicks);
+}
+
+/**
+ * Sets each value in values to the closest centroid.
+ */
+template <typename T, typename N>
+void applyCentroids(int k, T centroids[], T values[], int len, int dimension, int stride) {
+ int x, pop_size;
+ pop_size = k * stride;
+ for (x = 0; x < len; x+= stride) {
+ int best = findClosest<T, N>(values + x, centroids, dimension, stride, pop_size);
+ set<T, T>(values + x, centroids + best, dimension);
+ }
+}
+
+#endif // KMEANS_H
diff --git a/jni/filters/negative.c b/jni/filters/negative.c
new file mode 100644
index 000000000..735e583c9
--- /dev/null
+++ b/jni/filters/negative.c
@@ -0,0 +1,33 @@
+/*
+ * 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 "filters.h"
+
+void JNIFUNCF(ImageFilterNegative, nativeApplyFilter, jobject bitmap, jint width, jint height)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+
+ int tot_len = height * width * 4;
+ int i;
+ char * dst = destination;
+ for (i = 0; i < tot_len; i+=4) {
+ dst[RED] = 255 - dst[RED];
+ dst[GREEN] = 255 - dst[GREEN];
+ dst[BLUE] = 255 - dst[BLUE];
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/redEyeMath.c b/jni/filters/redEyeMath.c
new file mode 100644
index 000000000..26f3f76a4
--- /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 000000000..9a358dd3d
--- /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/saturated.c b/jni/filters/saturated.c
new file mode 100644
index 000000000..1bc0cc56b
--- /dev/null
+++ b/jni/filters/saturated.c
@@ -0,0 +1,53 @@
+/*
+ * 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 "filters.h"
+
+void JNIFUNCF(ImageFilterSaturated, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloat saturation)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ int i;
+ int len = width * height * 4;
+ float Rf = 0.2999f;
+ float Gf = 0.587f;
+ float Bf = 0.114f;
+ float S = saturation;;
+ float MS = 1.0f - S;
+ float Rt = Rf * MS;
+ float Gt = Gf * MS;
+ float Bt = Bf * MS;
+ float R, G, B;
+ for (i = 0; i < len; i+=4)
+ {
+ int r = destination[RED];
+ int g = destination[GREEN];
+ int b = destination[BLUE];
+ int t = (r + g) / 2;
+ R = r;
+ G = g;
+ B = b;
+
+ float Rc = R * (Rt + S) + G * Gt + B * Bt;
+ float Gc = R * Rt + G * (Gt + S) + B * Bt;
+ float Bc = R * Rt + G * Gt + B * (Bt + S);
+
+ destination[RED] = CLAMP(Rc);
+ destination[GREEN] = CLAMP(Gc);
+ destination[BLUE] = CLAMP(Bc);
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/shadows.c b/jni/filters/shadows.c
new file mode 100644
index 000000000..38d64c8b5
--- /dev/null
+++ b/jni/filters/shadows.c
@@ -0,0 +1,57 @@
+/*
+ * 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(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
+ };
+
+ 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);
+}
diff --git a/jni/filters/tinyplanet.cc b/jni/filters/tinyplanet.cc
new file mode 100644
index 000000000..beac0861a
--- /dev/null
+++ b/jni/filters/tinyplanet.cc
@@ -0,0 +1,150 @@
+/*
+ * 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 "filters.h"
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define PI_F 3.141592653589f
+
+class ImageRGBA {
+ public:
+ ImageRGBA(unsigned char* image, int width, int height)
+ : image_(image), width_(width), height_(height) {
+ width_step_ = width * 4;
+ }
+
+ int Width() const {
+ return width_;
+ }
+
+ int Height() const {
+ return height_;
+ }
+
+ // Pixel accessor.
+ unsigned char* operator()(int x, int y) {
+ return image_ + y * width_step_ + x * 4;
+ }
+ const unsigned char* operator()(int x, int y) const {
+ return image_ + y * width_step_ + x * 4;
+ }
+
+ private:
+ unsigned char* image_;
+ int width_;
+ int height_;
+ int width_step_;
+};
+
+// Interpolate a pixel in a 3 channel image.
+inline void InterpolatePixel(const ImageRGBA &image, float x, float y,
+ unsigned char* dest) {
+ // Get pointers and scale factors for the source pixels.
+ float ax = x - floor(x);
+ float ay = y - floor(y);
+ float axn = 1.0f - ax;
+ float ayn = 1.0f - ay;
+ const unsigned char *p = image(x, y);
+ const unsigned char *p2 = image(x, y + 1);
+
+ // Interpolate each image color plane.
+ dest[0] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] +
+ ax * ay * p2[4] + axn * ay * p2[0] + 0.5f);
+ p++;
+ p2++;
+
+ dest[1] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] +
+ ax * ay * p2[4] + axn * ay * p2[0] + 0.5f);
+ p++;
+ p2++;
+
+ dest[2] = static_cast<unsigned char>(axn * ayn * p[0] + ax * ayn * p[4] +
+ ax * ay * p2[4] + axn * ay * p2[0] + 0.5f);
+ p++;
+ p2++;
+ dest[3] = 0xFF;
+}
+
+// Wrap circular coordinates around the globe
+inline float wrap(float value, float dimension) {
+ return value - (dimension * floor(value/dimension));
+}
+
+void StereographicProjection(float scale, float angle, unsigned char* input_image,
+ int input_width, int input_height,
+ unsigned char* output_image, int output_width,
+ int output_height) {
+ ImageRGBA input(input_image, input_width, input_height);
+ ImageRGBA output(output_image, output_width, output_height);
+
+ const float image_scale = output_width * scale;
+
+ for (int x = 0; x < output_width; x++) {
+ // Center and scale x
+ float xf = (x - output_width / 2.0f) / image_scale;
+
+ for (int y = 0; y < output_height; y++) {
+ // Center and scale y
+ float yf = (y - output_height / 2.0f) / image_scale;
+
+ // Convert to polar
+ float r = hypotf(xf, yf);
+ float theta = angle+atan2(yf, xf);
+ if (theta>PI_F) theta-=2*PI_F;
+
+ // Project onto plane
+ float phi = 2 * atan(1 / r);
+ // (theta stays the same)
+
+ // Map to panorama image
+ float px = (theta / (2 * PI_F)) * input_width;
+ float py = (phi / PI_F) * input_height;
+
+ // Wrap around the globe
+ px = wrap(px, input_width);
+ py = wrap(py, input_height);
+
+ // Write the interpolated pixel
+ InterpolatePixel(input, px, py, output(x, y));
+ }
+ }
+}
+
+
+void JNIFUNCF(ImageFilterTinyPlanet, nativeApplyFilter, jobject bitmap_in, jint width, jint height, jobject bitmap_out, jint output_size, jfloat scale,jfloat angle)
+{
+ char* source = 0;
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap_in, (void**) &source);
+ AndroidBitmap_lockPixels(env, bitmap_out, (void**) &destination);
+ unsigned char * rgb_in = (unsigned char * )source;
+ unsigned char * rgb_out = (unsigned char * )destination;
+
+ StereographicProjection(scale,angle, rgb_in, width, height, rgb_out, output_size, output_size);
+ AndroidBitmap_unlockPixels(env, bitmap_in);
+ AndroidBitmap_unlockPixels(env, bitmap_out);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
diff --git a/jni/filters/vibrance.c b/jni/filters/vibrance.c
new file mode 100644
index 000000000..cb5c536e5
--- /dev/null
+++ b/jni/filters/vibrance.c
@@ -0,0 +1,62 @@
+/*
+ * 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(ImageFilterVibrance, nativeApplyFilter, jobject bitmap, jint width, jint height, jfloat vibrance)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ int i;
+ int len = width * height * 4;
+ float Rf = 0.2999f;
+ float Gf = 0.587f;
+ float Bf = 0.114f;
+ float Vib = vibrance/100.f;
+ float S = Vib+1;
+ float MS = 1.0f - S;
+ float Rt = Rf * MS;
+ float Gt = Gf * MS;
+ float Bt = Bf * MS;
+ float R, G, B;
+ for (i = 0; i < len; i+=4)
+ {
+ int r = destination[RED];
+ int g = destination[GREEN];
+ int b = destination[BLUE];
+ float red = (r-MAX(g, b))/256.f;
+ float sx = (float)(Vib/(1+exp(-red*3)));
+ S = sx+1;
+ MS = 1.0f - S;
+ Rt = Rf * MS;
+ Gt = Gf * MS;
+ Bt = Bf * MS;
+ int t = (r + g) / 2;
+ R = r;
+ G = g;
+ B = b;
+
+ float Rc = R * (Rt + S) + G * Gt + B * Bt;
+ float Gc = R * Rt + G * (Gt + S) + B * Bt;
+ float Bc = R * Rt + G * Gt + B * (Bt + S);
+
+ destination[RED] = CLAMP(Rc);
+ destination[GREEN] = CLAMP(Gc);
+ destination[BLUE] = CLAMP(Bc);
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/vignette.c b/jni/filters/vignette.c
new file mode 100644
index 000000000..b9ee3ff01
--- /dev/null
+++ b/jni/filters/vignette.c
@@ -0,0 +1,49 @@
+/*
+ * 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 "filters.h"
+#include <math.h>
+
+static int* gVignetteMap = 0;
+static int gVignetteWidth = 0;
+static int gVignetteHeight = 0;
+
+void JNIFUNCF(ImageFilterVignette, nativeApplyFilter, jobject bitmap, jint width, jint height, jint centerx, jint centery, jfloat radiusx, jfloat radiusy, jfloat strength)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ int i;
+ int len = width * height * 4;
+ int vignette = 0;
+ float d = centerx;
+ if (radiusx == 0) radiusx = 10;
+ if (radiusy == 0) radiusy = 10;
+ float scalex = 1/radiusx;
+ float scaley = 1/radiusy;
+
+ for (i = 0; i < len; i += 4)
+ {
+ int p = i/4;
+ float x = ((p%width)-centerx)*scalex;
+ float y = ((p/width)-centery)*scaley;
+ float dist = sqrt(x*x+y*y)-1;
+ vignette = (int) (strength*256*MAX(dist,0));
+ destination[RED] = CLAMP(destination[RED] - vignette);
+ destination[GREEN] = CLAMP(destination[GREEN] - vignette);
+ destination[BLUE] = CLAMP(destination[BLUE] - vignette);
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/filters/wbalance.c b/jni/filters/wbalance.c
new file mode 100644
index 000000000..2b92b9978
--- /dev/null
+++ b/jni/filters/wbalance.c
@@ -0,0 +1,169 @@
+/*
+ * 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 "filters.h"
+
+void estmateWhite(unsigned char *src, int len, int *wr, int *wb, int *wg){
+
+ int STEP = 4;
+ int RANGE = 256;
+ int *histR = (int *) malloc(256*sizeof(int));
+ int *histG = (int *) malloc(256*sizeof(int));
+ int *histB = (int *) malloc(256*sizeof(int));
+ int i;
+ for (i = 0; i < 255; i++) {
+ histR[i] = histG[i] = histB[i] =0;
+ }
+
+ for (i = 0; i < len; i+=STEP) {
+ histR[(src[RED])]++;
+ histG[(src[GREEN])]++;
+ histB[(src[BLUE])]++;
+ }
+ int min_r = -1, min_g = -1,min_b = -1;
+ int max_r = 0, max_g = 0,max_b = 0;
+ int sum_r = 0,sum_g=0,sum_b=0;
+
+ for (i = 1; i < RANGE-1; i++) {
+ int r = histR[i];
+ int g = histG[i];
+ int b = histB[i];
+ sum_r += r;
+ sum_g += g;
+ sum_b += b;
+
+ if (r>0){
+ if (min_r < 0) min_r = i;
+ max_r = i;
+ }
+ if (g>0){
+ if (min_g < 0) min_g = i;
+ max_g = i;
+ }
+ if (b>0){
+ if (min_b < 0) min_b = i;
+ max_b = i;
+ }
+ }
+
+ int sum15r = 0,sum15g=0,sum15b=0;
+ int count15r = 0,count15g=0,count15b=0;
+ int tmp_r = 0,tmp_g=0,tmp_b=0;
+
+ for (i = RANGE-2; i >0; i--) {
+ int r = histR[i];
+ int g = histG[i];
+ int b = histB[i];
+ tmp_r += r;
+ tmp_g += g;
+ tmp_b += b;
+
+ if ((tmp_r > sum_r/20) && (tmp_r < sum_r/5)) {
+ sum15r += r*i;
+ count15r += r;
+ }
+ if ((tmp_g > sum_g/20) && (tmp_g < sum_g/5)) {
+ sum15g += g*i;
+ count15g += g;
+ }
+ if ((tmp_b > sum_b/20) && (tmp_b < sum_b/5)) {
+ sum15b += b*i;
+ count15b += b;
+ }
+
+ }
+ free(histR);
+ free(histG);
+ free(histB);
+
+ if ((count15r>0) && (count15g>0) && (count15b>0) ){
+ *wr = sum15r/count15r;
+ *wb = sum15g/count15g;
+ *wg = sum15b/count15b;
+ }else {
+ *wg = *wb = *wr=255;
+ }
+}
+
+void estmateWhiteBox(unsigned char *src, int iw, int ih, int x,int y, int *wr, int *wb, int *wg){
+ int r;
+ int g;
+ int b;
+ int sum;
+ int xp,yp;
+ int bounds = 5;
+ if (x<0) x = bounds;
+ if (y<0) y = bounds;
+ if (x>=(iw-bounds)) x = (iw-bounds-1);
+ if (y>=(ih-bounds)) y = (ih-bounds-1);
+ int startx = x - bounds;
+ int starty = y - bounds;
+ int endx = x + bounds;
+ int endy = y + bounds;
+
+ for(yp= starty;yp<endy;yp++) {
+ for(xp= startx;xp<endx;xp++) {
+ int i = 4*(xp+yp*iw);
+ r += src[RED];
+ g += src[GREEN];
+ b += src[BLUE];
+ sum++;
+ }
+ }
+ *wr = r/sum;
+ *wg = g/sum;
+ *wb = b/sum;
+}
+
+void JNIFUNCF(ImageFilterWBalance, nativeApplyFilter, jobject bitmap, jint width, jint height, int locX,int locY)
+{
+ char* destination = 0;
+ AndroidBitmap_lockPixels(env, bitmap, (void**) &destination);
+ int i;
+ int len = width * height * 4;
+ unsigned char * rgb = (unsigned char * )destination;
+ int wr;
+ int wg;
+ int wb;
+
+ if (locX==-1)
+ estmateWhite(rgb,len,&wr,&wg,&wb);
+ else
+ estmateWhiteBox(rgb, width, height,locX,locY,&wr,&wg,&wb);
+
+ int min = MIN(wr, MIN(wg, wb));
+ int max = MAX(wr, MAX(wg, wb));
+ float avg = (min+max)/2.f;
+ float scaleR = avg/wr;
+ float scaleG = avg/wg;
+ float scaleB = avg/wb;
+
+ for (i = 0; i < len; i+=4)
+ {
+ int r = rgb[RED];
+ int g = rgb[GREEN];
+ int b = rgb[BLUE];
+
+ float Rc = r*scaleR;
+ float Gc = g*scaleG;
+ float Bc = b*scaleB;
+
+ rgb[RED] = clamp(Rc);
+ rgb[GREEN] = clamp(Gc);
+ rgb[BLUE] = clamp(Bc);
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
diff --git a/jni/jni_egl_fence.cpp b/jni/jni_egl_fence.cpp
new file mode 100644
index 000000000..cf15e2f5d
--- /dev/null
+++ b/jni/jni_egl_fence.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "jni_egl_fence.h"
+
+#include <android/log.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <string.h>
+
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR,"egl_fence",__VA_ARGS__)
+
+typedef EGLSyncKHR EGLAPIENTRY (*TypeEglCreateSyncKHR)(EGLDisplay dpy,
+ EGLenum type, const EGLint *attrib_list);
+typedef EGLBoolean EGLAPIENTRY (*TypeEglDestroySyncKHR)(EGLDisplay dpy,
+ EGLSyncKHR sync);
+typedef EGLint EGLAPIENTRY (*TypeEglClientWaitSyncKHR)(EGLDisplay dpy,
+ EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+static TypeEglCreateSyncKHR FuncEglCreateSyncKHR = NULL;
+static TypeEglClientWaitSyncKHR FuncEglClientWaitSyncKHR = NULL;
+static TypeEglDestroySyncKHR FuncEglDestroySyncKHR = NULL;
+static bool initialized = false;
+static bool egl_khr_fence_sync_supported = false;
+
+bool IsEglKHRFenceSyncSupported() {
+ if (!initialized) {
+ EGLDisplay display = eglGetCurrentDisplay();
+ const char* eglExtensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
+ if (eglExtensions && strstr(eglExtensions, "EGL_KHR_fence_sync")) {
+ FuncEglCreateSyncKHR = (TypeEglCreateSyncKHR) eglGetProcAddress("eglCreateSyncKHR");
+ FuncEglClientWaitSyncKHR = (TypeEglClientWaitSyncKHR) eglGetProcAddress("eglClientWaitSyncKHR");
+ FuncEglDestroySyncKHR = (TypeEglDestroySyncKHR) eglGetProcAddress("eglDestroySyncKHR");
+ if (FuncEglCreateSyncKHR != NULL && FuncEglClientWaitSyncKHR != NULL
+ && FuncEglDestroySyncKHR != NULL) {
+ egl_khr_fence_sync_supported = true;
+ }
+ }
+ initialized = true;
+ }
+ return egl_khr_fence_sync_supported;
+}
+
+void
+Java_com_android_gallery3d_photoeditor_FilterStack_nativeEglSetFenceAndWait(JNIEnv* env,
+ jobject thiz) {
+ if (!IsEglKHRFenceSyncSupported()) return;
+ EGLDisplay display = eglGetCurrentDisplay();
+
+ // Create a egl fence and wait for egl to return it.
+ // Additional reference on egl fence sync can be found in:
+ // http://www.khronos.org/registry/vg/extensions/KHR/EGL_KHR_fence_sync.txt
+ EGLSyncKHR fence = FuncEglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
+ if (fence == EGL_NO_SYNC_KHR) {
+ return;
+ }
+
+ EGLint result = FuncEglClientWaitSyncKHR(display,
+ fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+ EGL_FOREVER_KHR);
+ if (result == EGL_FALSE) {
+ ALOGE("EGL FENCE: error waiting for fence: %#x", eglGetError());
+ }
+ FuncEglDestroySyncKHR(display, fence);
+}
diff --git a/jni/jni_egl_fence.h b/jni/jni_egl_fence.h
new file mode 100644
index 000000000..6b2c20ab8
--- /dev/null
+++ b/jni/jni_egl_fence.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef COM_ANDROID_GALLERY3D_PHOTOEDITOR_JNI_EGL_FENSE_H
+#define COM_ANDROID_GALLERY3D_PHOTOEDITOR_JNI_EGL_FENSE_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL
+Java_com_android_gallery3d_photoeditor_FilterStack_nativeEglSetFenceAndWait(JNIEnv* env,
+ jobject thiz);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* COM_ANDROID_GALLERY3D_PHOTOEDITOR_JNI_EGL_FENSE_H */