From 8921c28c7333ad2b4d34f013904ad4737044f366 Mon Sep 17 00:00:00 2001 From: John Hoford Date: Wed, 20 Feb 2013 17:50:42 -0800 Subject: [PATCH] add highlight filter Change-Id: I2e59e09fbc80172b9dfe27b3ce8ff2f1e24c5872 --- jni/Android.mk | 1 + jni/filters/contrast.c | 9 ++ jni/filters/filters.h | 1 + jni/filters/highlight.c | 40 +++++ res/values/filtershow_ids.xml | 1 + res/values/filtershow_strings.xml | 2 + .../filtershow/filters/BaseFiltersManager.java | 2 + .../filtershow/filters/ImageFilterHighlights.java | 73 +++++++++ .../gallery3d/filtershow/filters/SplineMath.java | 166 +++++++++++++++++++++ 9 files changed, 295 insertions(+) create mode 100644 jni/filters/highlight.c create mode 100644 src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java create mode 100644 src/com/android/gallery3d/filtershow/filters/SplineMath.java diff --git a/jni/Android.mk b/jni/Android.mk index 1843c77ea..e612486e1 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -32,6 +32,7 @@ LOCAL_SRC_FILES := filters/gradient.c \ filters/contrast.c \ filters/hue.c \ filters/shadows.c \ + filters/highlight.c \ filters/hsv.c \ filters/vibrance.c \ filters/geometry.c \ diff --git a/jni/filters/contrast.c b/jni/filters/contrast.c index 6c1b976cf..b04e9364e 100644 --- a/jni/filters/contrast.c +++ b/jni/filters/contrast.c @@ -27,6 +27,15 @@ unsigned char clamp(int c) 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; diff --git a/jni/filters/filters.h b/jni/filters/filters.h index d518b6398..14b69cdd4 100644 --- a/jni/filters/filters.h +++ b/jni/filters/filters.h @@ -44,6 +44,7 @@ typedef unsigned int Color; #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); 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 +#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/res/values/filtershow_ids.xml b/res/values/filtershow_ids.xml index ba318345b..28e78166d 100644 --- a/res/values/filtershow_ids.xml +++ b/res/values/filtershow_ids.xml @@ -28,6 +28,7 @@ + diff --git a/res/values/filtershow_strings.xml b/res/values/filtershow_strings.xml index 66fb3902d..a845c3482 100644 --- a/res/values/filtershow_strings.xml +++ b/res/values/filtershow_strings.xml @@ -115,6 +115,8 @@ Hue Shadows + + Highlights Curves diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java index 43660d6a0..377bd2b6f 100644 --- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java +++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java @@ -42,6 +42,7 @@ public class BaseFiltersManager { filters.add(new ImageFilterVignette()); filters.add(new ImageFilterContrast()); filters.add(new ImageFilterShadows()); + filters.add(new ImageFilterHighlights()); filters.add(new ImageFilterVibrance()); filters.add(new ImageFilterSharpen()); filters.add(new ImageFilterCurves()); @@ -89,6 +90,7 @@ public class BaseFiltersManager { representations.add(getRepresentation(ImageFilterVignette.class)); representations.add(getRepresentation(ImageFilterContrast.class)); representations.add(getRepresentation(ImageFilterShadows.class)); + representations.add(getRepresentation(ImageFilterHighlights.class)); representations.add(getRepresentation(ImageFilterVibrance.class)); representations.add(getRepresentation(ImageFilterSharpen.class)); representations.add(getRepresentation(ImageFilterCurves.class)); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java new file mode 100644 index 000000000..b9a14652a --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package com.android.gallery3d.filtershow.filters; + +import com.android.gallery3d.R; + +import android.graphics.Bitmap; +import android.util.Log; + +public class ImageFilterHighlights extends SimpleImageFilter { + private static final String LOGTAG = "ImageFilterVignette"; + + public ImageFilterHighlights() { + mName = "Highlights"; + } + + SplineMath mSpline = new SplineMath(5); + double[] mHighlightCurve = { 0.0, 0.32, 0.418, 0.476, 0.642 }; + + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Shadows"); + representation.setFilterClass(ImageFilterHighlights.class); + representation.setTextId(R.string.highlight_recovery); + representation.setButtonId(R.id.highlightRecoveryButton); + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + return representation; + } + + native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float[] luminanceMap); + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } + float p = getParameters().getValue(); + double t = p/100.; + for (int i = 0; i < 5; i++) { + double x = i / 4.; + double y = mHighlightCurve[i] *t+x*(1-t); + mSpline.setPoint(i, x, y); + } + + float[][] curve = mSpline.calculatetCurve(256); + float[] luminanceMap = new float[curve.length]; + for (int i = 0; i < luminanceMap.length; i++) { + luminanceMap[i] = curve[i][1]; + } + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + nativeApplyFilter(bitmap, w, h, luminanceMap); + return bitmap; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/SplineMath.java b/src/com/android/gallery3d/filtershow/filters/SplineMath.java new file mode 100644 index 000000000..5b12d0a61 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/SplineMath.java @@ -0,0 +1,166 @@ +package com.android.gallery3d.filtershow.filters; + + +public class SplineMath { + double[][] mPoints = new double[6][2]; + double[] mDerivatives; + SplineMath(int n) { + mPoints = new double[n][2]; + } + + public void setPoint(int index, double x, double y) { + mPoints[index][0] = x; + mPoints[index][1] = y; + mDerivatives = null; + } + + public float[][] calculatetCurve(int n) { + float[][] curve = new float[n][2]; + double[][] points = new double[mPoints.length][2]; + for (int i = 0; i < mPoints.length; i++) { + + points[i][0] = mPoints[i][0]; + points[i][1] = mPoints[i][1]; + + } + double[] derivatives = solveSystem(points); + float start = (float) points[0][0]; + float end = (float) (points[points.length - 1][0]); + + curve[0][0] = (float) (points[0][0]); + curve[0][1] = (float) (points[0][1]); + int last = curve.length - 1; + curve[last][0] = (float) (points[points.length - 1][0]); + curve[last][1] = (float) (points[points.length - 1][1]); + + for (int i = 0; i < curve.length; i++) { + + double[] cur = null; + double[] next = null; + double x = start + i * (end - start) / (curve.length - 1); + int pivot = 0; + for (int j = 0; j < points.length - 1; j++) { + if (x >= points[j][0] && x <= points[j + 1][0]) { + pivot = j; + } + } + cur = points[pivot]; + next = points[pivot + 1]; + if (x <= next[0]) { + double x1 = cur[0]; + double x2 = next[0]; + double y1 = cur[1]; + double y2 = next[1]; + + // Use the second derivatives to apply the cubic spline + // equation: + double delta = (x2 - x1); + double delta2 = delta * delta; + double b = (x - x1) / delta; + double a = 1 - b; + double ta = a * y1; + double tb = b * y2; + double tc = (a * a * a - a) * derivatives[pivot]; + double td = (b * b * b - b) * derivatives[pivot + 1]; + double y = ta + tb + (delta2 / 6) * (tc + td); + + curve[i][0] = (float) (x); + curve[i][1] = (float) (y); + } else { + curve[i][0] = (float) (next[0]); + curve[i][1] = (float) (next[1]); + } + } + return curve; + } + + public double getValue(double x) { + double[] cur = null; + double[] next = null; + if (mDerivatives == null) + mDerivatives = solveSystem(mPoints); + int pivot = 0; + for (int j = 0; j < mPoints.length - 1; j++) { + pivot = j; + if (x <= mPoints[j][0]) { + break; + } + } + cur = mPoints[pivot]; + next = mPoints[pivot + 1]; + double x1 = cur[0]; + double x2 = next[0]; + double y1 = cur[1]; + double y2 = next[1]; + + // Use the second derivatives to apply the cubic spline + // equation: + double delta = (x2 - x1); + double delta2 = delta * delta; + double b = (x - x1) / delta; + double a = 1 - b; + double ta = a * y1; + double tb = b * y2; + double tc = (a * a * a - a) * mDerivatives[pivot]; + double td = (b * b * b - b) * mDerivatives[pivot + 1]; + double y = ta + tb + (delta2 / 6) * (tc + td); + + return y; + + } + + double[] solveSystem(double[][] points) { + int n = points.length; + double[][] system = new double[n][3]; + double[] result = new double[n]; // d + double[] solution = new double[n]; // returned coefficients + system[0][1] = 1; + system[n - 1][1] = 1; + double d6 = 1.0 / 6.0; + double d3 = 1.0 / 3.0; + + // let's create a tridiagonal matrix representing the + // system, and apply the TDMA algorithm to solve it + // (see http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm) + for (int i = 1; i < n - 1; i++) { + double deltaPrevX = points[i][0] - points[i - 1][0]; + double deltaX = points[i + 1][0] - points[i - 1][0]; + double deltaNextX = points[i + 1][0] - points[i][0]; + double deltaNextY = points[i + 1][1] - points[i][1]; + double deltaPrevY = points[i][1] - points[i - 1][1]; + system[i][0] = d6 * deltaPrevX; // a_i + system[i][1] = d3 * deltaX; // b_i + system[i][2] = d6 * deltaNextX; // c_i + result[i] = (deltaNextY / deltaNextX) - (deltaPrevY / deltaPrevX); // d_i + } + + // Forward sweep + for (int i = 1; i < n; i++) { + // m = a_i/b_i-1 + double m = system[i][0] / system[i - 1][1]; + // b_i = b_i - m(c_i-1) + system[i][1] = system[i][1] - m * system[i - 1][2]; + // d_i = d_i - m(d_i-1) + result[i] = result[i] - m * result[i - 1]; + } + + // Back substitution + solution[n - 1] = result[n - 1] / system[n - 1][1]; + for (int i = n - 2; i >= 0; --i) { + solution[i] = (result[i] - system[i][2] * solution[i + 1]) / system[i][1]; + } + return solution; + } + + public static void main(String[] args) { + SplineMath s = new SplineMath(10); + for (int i = 0; i < 10; i++) { + s.setPoint(i, i, i); + } + float[][] curve = s.calculatetCurve(40); + + for (int j = 0; j < curve.length; j++) { + System.out.println(curve[j][0] + "," + curve[j][1]); + } + } +} -- 2.11.0