summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk9
-rw-r--r--jni/Android.mk10
-rw-r--r--jni/image_util_jni.cpp153
-rw-r--r--proguard.flags12
-rw-r--r--res/values/camera2arrays.xml10
-rw-r--r--res/values/qcomstrings.xml1
-rw-r--r--res/xml/capture_preferences.xml7
-rw-r--r--rs/YuvToRgb.rs58
-rw-r--r--rs/rotator.rs51
-rw-r--r--src/com/android/camera/CaptureModule.java176
-rw-r--r--src/com/android/camera/CaptureUI.java3
-rw-r--r--src/com/android/camera/SettingsManager.java1
-rw-r--r--src/com/android/camera/imageprocessor/FrameProcessor.java335
-rw-r--r--src/com/android/camera/imageprocessor/PostProcessor.java16
-rw-r--r--src/com/android/camera/imageprocessor/filter/BeautificationFilter.java134
15 files changed, 944 insertions, 32 deletions
diff --git a/Android.mk b/Android.mk
index f230c0742..de980e692 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,15 +7,13 @@ LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13
LOCAL_STATIC_JAVA_LIBRARIES += xmp_toolkit
-
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, src_pd)
LOCAL_SRC_FILES += $(call all-java-files-under, src_pd_gcam)
+LOCAL_SRC_FILES += $(call all-renderscript-files-under, rs)
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
-LOCAL_CERTIFICATE := platform
-
include $(LOCAL_PATH)/version.mk
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
@@ -25,6 +23,7 @@ LOCAL_AAPT_FLAGS := \
LOCAL_PACKAGE_NAME := SnapdragonCamera
#LOCAL_SDK_VERSION := current
+LOCAL_RENDERSCRIPT_TARGET_API := 23
LOCAL_OVERRIDES_PACKAGES := Camera2
@@ -34,9 +33,9 @@ LOCAL_PROGUARD_FLAG_FILES := proguard.flags
# the libraries in the APK, otherwise just put them in /system/lib and
# leave them out of the APK
ifneq (,$(TARGET_BUILD_APPS))
- LOCAL_JNI_SHARED_LIBRARIES := libjni_snapcammosaic libjni_snapcamtinyplanet
+ LOCAL_JNI_SHARED_LIBRARIES := libjni_snapcammosaic libjni_snapcamtinyplanet libjni_imageutil
else
- LOCAL_REQUIRED_MODULES := libjni_snapcammosaic libjni_snapcamtinyplanet
+ LOCAL_REQUIRED_MODULES := libjni_snapcammosaic libjni_snapcamtinyplanet libjni_imageutil
endif
include $(BUILD_PACKAGE)
diff --git a/jni/Android.mk b/jni/Android.mk
index c94a8075d..de2abb6b8 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -63,3 +63,13 @@ LOCAL_ARM_MODE := arm
include $(BUILD_SHARED_LIBRARY)
+# ImageUtilForCamera2 with beautification
+include $(CLEAR_VARS)
+LOCAL_LDFLAGS := -llog
+LOCAL_SDK_VERSION := 9
+LOCAL_MODULE := libjni_imageutil
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := image_util_jni.cpp
+LOCAL_CFLAGS += -ffast-math -O3 -funroll-loops
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/jni/image_util_jni.cpp b/jni/image_util_jni.cpp
new file mode 100644
index 000000000..2297f9164
--- /dev/null
+++ b/jni/image_util_jni.cpp
@@ -0,0 +1,153 @@
+/*
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <jni.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#ifdef __ANDROID__
+#include "android/log.h"
+#define printf(...) __android_log_print( ANDROID_LOG_ERROR, "ImageUtil", __VA_ARGS__ )
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+JNIEXPORT jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeRotateNV21
+ (JNIEnv* env, jobject thiz, jobjectArray inBuf,
+ jint imageWidth, jint imageHeight, jint degree, jobjectArray outBuf);
+JNIEXPORT jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeNV21toRgb(
+ JNIEnv *env, jobject thiz, jobjectArray yvuBuf, jobjectArray rgbBuf, jint width, jint height);
+#ifdef __cplusplus
+}
+#endif
+
+typedef unsigned char uint8_t;
+
+void rotateBufAndMerge(uint8_t *in_buf, jint imageWidth, jint imageHeight, jint degree, uint8_t *out_buf)
+{
+ if(degree == 90) {
+ int i = 0;
+ for (int x = 0; x < imageWidth; x++) {
+ for (int y = imageHeight - 1; y >= 0; y--) {
+ int offset = y * imageWidth + x;
+ out_buf[i] = in_buf[offset];
+ i++;
+ }
+ }
+ i = imageWidth * imageHeight;
+ for (int x = 0; x < imageWidth; x += 2) {
+ for (int y = imageHeight / 2 - 1; y >= 0; y--) {
+ int offset = imageWidth*imageHeight + y * imageWidth + x;
+ out_buf[i] = in_buf[offset];
+ i++;
+ out_buf[i] = in_buf[offset + 1];
+ i++;
+ }
+ }
+ } else if(degree == 270) {
+ int i = 0;
+ for (int x = imageWidth - 1; x >= 0; x--) {
+ for (int y = 0; y < imageHeight; y++) {
+ int offset = y * imageWidth + x;
+ out_buf[i] = in_buf[offset];
+ i++;
+ }
+ }
+ i = imageWidth * imageHeight;
+ for (int x = imageWidth - 2; x >= 0; x-=2) {
+ for (int y = 0; y < imageHeight/2; y++) {
+ int offset = imageWidth*imageHeight + y * imageWidth + x;
+ out_buf[i] = in_buf[offset];
+ i++;
+ out_buf[i] = in_buf[offset + 1];
+ i++;
+ }
+ }
+ } else if(degree == 180) {
+ int i = 0;
+ for (int y = imageHeight - 1; y >= 0; y--) {
+ for (int x = imageWidth - 1; x >= 0 ; x--) {
+ int offset = y * imageWidth + x;
+ out_buf[i] = in_buf[offset];
+ i++;
+ }
+ }
+ i = imageWidth * imageHeight;
+ for (int y = imageHeight/2 - 1; y >= 0; y--) {
+ for (int x = imageWidth - 2; x >= 0 ; x-=2) {
+ int offset = imageWidth*imageHeight + y * imageWidth + x;
+ out_buf[i] = in_buf[offset];
+ i++;
+ out_buf[i] = in_buf[offset + 1];
+ i++;
+ }
+ }
+ }
+}
+
+jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeRotateNV21(
+ JNIEnv* env, jobject thiz, jobjectArray inBuf,
+ jint imageWidth, jint imageHeight, jint degree, jobjectArray outBuf)
+{
+ uint8_t *in_buf = (uint8_t *)env->GetDirectBufferAddress(inBuf);
+ uint8_t *out_buf = (uint8_t *)env->GetDirectBufferAddress(outBuf);
+ rotateBufAndMerge(in_buf, imageWidth, imageHeight, degree, out_buf);
+
+ return 0;
+}
+
+jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeNV21toRgb(
+ JNIEnv* env, jobject thiz, jobjectArray yvuBuf, jobjectArray rgbBuf, jint width, jint height)
+{
+ uint8_t *in_buf = (uint8_t *)env->GetDirectBufferAddress(yvuBuf);
+ uint8_t *rgb_buf = (uint8_t *)env->GetDirectBufferAddress(rgbBuf);
+ int ysize = width * height;
+ int y_value;
+ int i, v, u, r, g, b;
+ for(int x=0; x < width; x++) {
+ for(int y=0; y < height; y++) {
+ y_value = (in_buf[y*width+x] & 0xFF);
+ i = ysize + (x/2*2) + ((y/2) * width);
+ v = (in_buf[i] & 0xFF) - 128;
+ u = (in_buf[i + 1] & 0xFF) - 128;
+ r = (int)(1.164f * y_value + 1.596f * v);
+ g = (int)(1.164f * y_value - 0.813f * v - 0.391f * u);
+ b = (int)(1.164f * y_value + 2.018f * u);
+ r = r > 255 ? 255 : r < 0 ? 0 : r;
+ g = g > 255 ? 255 : g < 0 ? 0 : g;
+ b = b > 255 ? 255 : b < 0 ? 0 : b;
+ rgb_buf[(y*width + x) * 4 + 3] = (uint8_t)(0xFF);
+ rgb_buf[(y*width + x) * 4 + 2] = (uint8_t)(b & 0xFF);
+ rgb_buf[(y*width + x) * 4 + 1] = (uint8_t)(g & 0xFF);
+ rgb_buf[(y*width + x) * 4 + 0] = (uint8_t)(r & 0xFF);
+ }
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/proguard.flags b/proguard.flags
index 10fd77b6b..9d477262a 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -45,3 +45,15 @@
*** closeSilently(...);
}
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keep class * {
+ public <methods>;
+ public <fields>;
+ private <methods>;
+ private <fields>;
+}
+
+-keep class android.renderscript.** { *; }
diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml
index 59e06f260..49f37e139 100644
--- a/res/values/camera2arrays.xml
+++ b/res/values/camera2arrays.xml
@@ -60,6 +60,16 @@
<item>front</item>
</string-array>
+ <string-array name="pref_camera2_makeup_entries" translatable="true">
+ <item>On</item>
+ <item>Off</item>
+ </string-array>
+
+ <string-array name="pref_camera2_makeup_entryvalues" translatable="false">
+ <item>on</item>
+ <item>off</item>
+ </string-array>
+
<string-array name="pref_camera2_mono_preview_entries" translatable="true">
<item>@string/pref_camera2_mono_preview_entry_on</item>
<item>@string/pref_camera2_mono_preview_entry_off</item>
diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml
index 476d65efe..31ba4d49f 100644
--- a/res/values/qcomstrings.xml
+++ b/res/values/qcomstrings.xml
@@ -979,5 +979,6 @@
<string name="pref_camera2_whitebalance_default" translatable="false">1</string>
<string name="pref_camera2_coloreffect_default" translatable="false">0</string>
<string name="pref_camera2_flashmode_default" translatable="false">2</string>
+ <string name="pref_camera2_makeup_title" translatable="true">Makeup</string>
</resources>
diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml
index 3dad3a701..5e9235e34 100644
--- a/res/xml/capture_preferences.xml
+++ b/res/xml/capture_preferences.xml
@@ -183,4 +183,11 @@
camera:key="pref_camera2_initial_camera_key"
camera:entries="@array/pref_camera2_initial_camera_entries"
camera:entryValues="@array/pref_camera2_initial_camera_entryvalues"/>
+
+ <ListPreference
+ camera:defaultValue="off"
+ camera:key="pref_camera2_makeup_key"
+ camera:entries="@array/pref_camera2_makeup_entries"
+ camera:entryValues="@array/pref_camera2_makeup_entryvalues"
+ camera:title="@string/pref_camera2_makeup_title"/>
</PreferenceGroup>
diff --git a/rs/YuvToRgb.rs b/rs/YuvToRgb.rs
new file mode 100644
index 000000000..25771c5c7
--- /dev/null
+++ b/rs/YuvToRgb.rs
@@ -0,0 +1,58 @@
+/*
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#pragma version(1)
+#pragma rs java_package_name(com.android.camera.imageprocessor)
+#pragma rs_fp_relaxed
+
+rs_allocation gIn;
+uint32_t width;
+uint32_t height;
+
+uchar4 __attribute__((kernel)) nv21ToRgb(uint32_t x, uint32_t y) {
+ uint32_t ySize = width*height;
+ uint32_t index = ySize + (x/2*2) + ((y/2) * width);
+ int yV = (int)(rsGetElementAt_uchar(gIn, x + y*width) & 0xFF);
+ int vV = (int)(rsGetElementAt_uchar(gIn, index) & 0xFF ) -128;
+ int uV = (int)(rsGetElementAt_uchar(gIn, index+1) & 0xFF ) -128;
+
+ int r = (int) (1.164f * yV + 1.596f * vV );
+ int g = (int) (1.164f * yV - 0.813f * vV - 0.391f * uV);
+ int b = (int) (1.164f * yV + 2.018f * uV );
+
+ r = r>255? 255 : r<0 ? 0 : r;
+ g = g>255? 255 : g<0 ? 0 : g;
+ b = b>255? 255 : b<0 ? 0 : b;
+ uchar4 res4;
+ res4.r = (uchar)(r & 0xFF);
+ res4.g = (uchar)(g & 0xFF);
+ res4.b = (uchar)(b & 0xFF);
+ res4.a = 0xFF;
+
+ return res4;
+} \ No newline at end of file
diff --git a/rs/rotator.rs b/rs/rotator.rs
new file mode 100644
index 000000000..cd9da4396
--- /dev/null
+++ b/rs/rotator.rs
@@ -0,0 +1,51 @@
+/*
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#pragma version(1)
+#pragma rs java_package_name(com.android.camera.imageprocessor)
+#pragma rs_fp_relaxed
+
+rs_allocation gOut;
+rs_allocation gIn;
+uint32_t width;
+uint32_t height;
+
+uchar __attribute__((kernel)) rotate90andMerge(uint32_t x, uint32_t y) {
+ uchar yValue = rsGetElementAt_uchar(gIn, x + y*width);
+ rsSetElementAt_uchar(gOut, yValue, x*height + height - 1 - y);
+
+ if(x%2 == 0 && y%2==1) {
+ uint32_t ySize = width*height;
+ uint32_t index = ySize + x + ((y/2) * width);
+ uchar vValue = rsGetElementAt_uchar(gIn, index);
+ uchar uValue = rsGetElementAt_uchar(gIn, index + 1);
+ rsSetElementAt_uchar(gOut, vValue, ySize + x/2*height + height - 1 - y);
+ rsSetElementAt_uchar(gOut, uValue, ySize + x/2*height + height - 1 - y - 1);
+ }
+ return (uchar)0;
+} \ No newline at end of file
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 8810c6df4..0f65f21c3 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Camera;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -39,6 +40,7 @@ import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CameraProfile;
@@ -53,6 +55,7 @@ import android.os.Message;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
+import android.view.Display;
import android.view.KeyEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
@@ -60,7 +63,9 @@ import android.view.SurfaceHolder;
import android.view.View;
import android.widget.Toast;
+import com.android.camera.imageprocessor.filter.ImageFilter;
import com.android.camera.imageprocessor.PostProcessor;
+import com.android.camera.imageprocessor.FrameProcessor;
import com.android.camera.PhotoModule.NamedImages;
import com.android.camera.PhotoModule.NamedImages.NamedEntity;
import com.android.camera.ui.CountDownView;
@@ -75,6 +80,7 @@ import org.codeaurora.snapcam.filter.ClearSightImageProcessor;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
@@ -197,6 +203,11 @@ public class CaptureModule implements CameraModule, PhotoController,
* A {@link Handler} for running tasks in the background.
*/
private PostProcessor mPostProcessor;
+ private FrameProcessor mFrameProcessor;
+ private Size mFrameProcPreviewOutputSize;
+ private Face[] mPreviewFaces = null;
+ private Face[] mStickyFaces = null;
+ private Rect mBayerCameraRegion;
private Handler mCameraHandler;
private Handler mImageAvailableHandler;
private Handler mCaptureCallbackHandler;
@@ -302,6 +313,20 @@ public class CaptureModule implements CameraModule, PhotoController,
* camera.
*/
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
+
+
+ public Face[] getPreviewFaces() {
+ return mPreviewFaces;
+ }
+
+ public Face[] getStickyFaces() {
+ return mStickyFaces;
+ }
+
+ public Rect getCameraRegion() {
+ return mBayerCameraRegion;
+ }
+
/**
* A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
*/
@@ -310,6 +335,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private void process(CaptureResult result) {
int id = (int) result.getRequest().getTag();
+
if (!mFirstPreviewLoaded) {
mActivity.runOnUiThread(new Runnable() {
@Override
@@ -319,6 +345,13 @@ public class CaptureModule implements CameraModule, PhotoController,
});
mFirstPreviewLoaded = true;
}
+
+ Face[] faces = result.get(CaptureResult.STATISTICS_FACES);
+ mPreviewFaces = faces;
+ if(faces != null && faces.length != 0) {
+ mStickyFaces = faces;
+ }
+
switch (mState[id]) {
case STATE_PREVIEW: {
break;
@@ -540,17 +573,31 @@ public class CaptureModule implements CameraModule, PhotoController,
mNamedImages = new NamedImages();
}
+ public ArrayList<ImageFilter> getFrameFilters() {
+ if(mFrameProcessor == null) {
+ return new ArrayList<ImageFilter>();
+ } else {
+ return mFrameProcessor.getFrameFilters();
+ }
+ }
+
+ private void applyFaceDetect(CaptureRequest.Builder builder, int id) {
+ if(id == getMainCameraId()) {
+ builder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
+ CameraMetadata.STATISTICS_FACE_DETECT_MODE_SIMPLE);
+ }
+ }
+
private void createSession(final int id) {
if (mPaused || !mCameraOpened[id] || !mSurfaceReady) return;
Log.d(TAG, "createSession " + id);
List<Surface> list = new LinkedList<Surface>();
try {
- Surface surface = getPreviewSurface(id);
+ Surface surface = getPreviewSurfaceForSession(id);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder[id] = mCameraDevice[id].createCaptureRequest(CameraDevice
.TEMPLATE_PREVIEW);
mPreviewRequestBuilder[id].setTag(id);
- mPreviewRequestBuilder[id].addTarget(surface);
CameraCaptureSession.StateCallback captureSessionCallback =
new CameraCaptureSession.StateCallback() {
@@ -571,7 +618,6 @@ public class CaptureModule implements CameraModule, PhotoController,
// Finally, we start displaying the camera preview.
mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id]
.build(), mCaptureCallback, mCameraHandler);
-
if (isClearSightOn()) {
ClearSightImageProcessor.getInstance().onCaptureSessionConfigured(id == BAYER_ID, cameraCaptureSession);
}
@@ -603,12 +649,28 @@ public class CaptureModule implements CameraModule, PhotoController,
}
};
- list.add(surface);
-
if(isClearSightOn()) {
+ mPreviewRequestBuilder[id].addTarget(surface);
+ list.add(surface);
ClearSightImageProcessor.getInstance().createCaptureSession(
- id==BAYER_ID, mCameraDevice[id], list, captureSessionCallback);
+ id == BAYER_ID, mCameraDevice[id], list, captureSessionCallback);
+ } else if (id == getMainCameraId()) {
+ if(mFrameProcessor.isFrameFilterEnabled()) {
+ mFrameProcessor.init(mFrameProcPreviewOutputSize);
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ mUI.getSurfaceHolder().setFixedSize(mFrameProcPreviewOutputSize.getHeight(), mFrameProcPreviewOutputSize.getWidth());
+ }
+ });
+ }
+ mFrameProcessor.setOutputSurface(surface);
+ mPreviewRequestBuilder[id].addTarget(mFrameProcessor.getInputSurface());
+ list.add(mFrameProcessor.getInputSurface());
+ list.add(mImageReader[id].getSurface());
+ mCameraDevice[id].createCaptureSession(list, captureSessionCallback, null);
} else {
+ mPreviewRequestBuilder[id].addTarget(surface);
+ list.add(surface);
list.add(mImageReader[id].getSurface());
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice[id].createCaptureSession(list, captureSessionCallback, null);
@@ -659,6 +721,7 @@ public class CaptureModule implements CameraModule, PhotoController,
}
mPostProcessor = new PostProcessor(mActivity, this);
+ mFrameProcessor = new FrameProcessor(mActivity, this);
setCurrentMode();
mContentResolver = mActivity.getContentResolver();
@@ -742,7 +805,6 @@ public class CaptureModule implements CameraModule, PhotoController,
mControlAFMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
applySettingsForAutoFocus(builder, id);
-
mState[id] = STATE_WAITING_TOUCH_FOCUS;
mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler);
setAFModeToPreview(id, mControlAFMode);
@@ -775,10 +837,6 @@ public class CaptureModule implements CameraModule, PhotoController,
}
}
- /**
- * Capture a still picture. This method should be called when we get a response in
- * {@link #mCaptureCallback} from both {@link #lockFocus()}.
- */
private void captureStillPicture(final int id) {
Log.d(TAG, "captureStillPicture " + id);
try {
@@ -809,7 +867,7 @@ public class CaptureModule implements CameraModule, PhotoController,
if(csEnabled) {
ClearSightImageProcessor.getInstance().capture(
id==BAYER_ID, mCaptureSession[id], captureBuilder, mCaptureCallbackHandler);
- } else if(id == BAYER_ID && mPostProcessor.isFilterOn()) {
+ } else if(id == getMainCameraId() && mPostProcessor.isFilterOn()) {
captureBuilder.addTarget(mImageReader[id].getSurface());
List<CaptureRequest> captureList = mPostProcessor.setRequiredImages(captureBuilder);
mCaptureSession[id].captureBurst(captureList, new CameraCaptureSession.CaptureCallback() {
@@ -940,22 +998,56 @@ public class CaptureModule implements CameraModule, PhotoController,
}
}
+ private void determineFrameProcPreviewOutputSize(List<Size> sizeList, float targetRatio) {
+ Display display = mActivity.getWindowManager().getDefaultDisplay();
+ Point ds = new Point();
+ display.getSize(ds);
+ int i=0, j=0, width, height;
+ float ratio;
+ for(; i < sizeList.size(); i++) {
+ width = sizeList.get(i).getHeight();
+ height = sizeList.get(i).getWidth();
+ ratio = (float)height/width;
+ if(ds.x >= width || ds.y >= height) {
+ if(j == 0) {
+ j = i;
+ }
+ if(ratio < targetRatio + 0.2f && ratio > targetRatio - 0.2f) {
+ break;
+ }
+ }
+ }
+ if(i == sizeList.size()) {
+ if(j != 0) {
+ mFrameProcPreviewOutputSize = sizeList.get(j);
+ } else {
+ mFrameProcPreviewOutputSize = sizeList.get(sizeList.size()-1);
+ }
+ } else {
+ mFrameProcPreviewOutputSize = sizeList.get(i);
+ }
+ }
/**
* Sets up member variables related to camera.
*
* @param width The width of available size for camera preview
* @param height The height of available size for camera preview
*/
- private void setUpCameraOutputs(int imageFomat) {
+ private void setUpCameraOutputs(int imageFormat) {
Log.d(TAG, "setUpCameraOutputs");
CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
try {
String[] cameraIdList = manager.getCameraIdList();
for (int i = 0; i < cameraIdList.length; i++) {
String cameraId = cameraIdList[i];
+
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
if (isInMode(i))
mCameraIdList.add(i);
+ if(i == getMainCameraId()) {
+ mBayerCameraRegion = characteristics.get(CameraCharacteristics
+ .SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ }
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
@@ -971,10 +1063,10 @@ public class CaptureModule implements CameraModule, PhotoController,
if (i == getMainCameraId()) {
Point screenSize = new Point();
mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize);
- Size[] prevSizes = map.getOutputSizes(imageFomat);
- Size prevSize = getOptimalPreviewSize(size, prevSizes, screenSize.x,
+ Size[] prevSizes = map.getOutputSizes(imageFormat);
+ mFrameProcPreviewOutputSize = getOptimalPreviewSize(size, prevSizes, screenSize.x,
screenSize.y);
- mUI.setPreviewSize(prevSize.getWidth(), prevSize.getHeight());
+ mUI.setPreviewSize(mFrameProcPreviewOutputSize.getWidth(), mFrameProcPreviewOutputSize.getHeight());
}
if (isClearSightOn()) {
ClearSightImageProcessor.getInstance().init(size.getWidth(), size.getHeight(),
@@ -982,11 +1074,13 @@ public class CaptureModule implements CameraModule, PhotoController,
ClearSightImageProcessor.getInstance().setCallback(this);
} else {
// No Clearsight
- mImageReader[i] = ImageReader.newInstance(size.getWidth(), size.getHeight(),
- imageFomat, MAX_IMAGE_NUM);
-
- if(mPostProcessor.isFilterOn() && i == BAYER_ID) {
+ mImageReader[i] = ImageReader.newInstance(size.getWidth(), size.getHeight(), imageFormat, MAX_IMAGE_NUM);
+ if(mPostProcessor.isFilterOn() && i == getMainCameraId()) {
mImageReader[i].setOnImageAvailableListener(mPostProcessor, mImageAvailableHandler);
+// if(mFrameProcessor.isFrameFilterEnabled()) {
+// determineFrameProcPreviewOutputSize(Arrays.asList(map.getOutputSizes(imageFormat)),
+// (float) size.getWidth() / (float) size.getHeight());
+// }
} else {
mImageReader[i].setOnImageAvailableListener(new ImageAvailableListener(i) {
@Override
@@ -1033,7 +1127,6 @@ public class CaptureModule implements CameraModule, PhotoController,
builder.addTarget(getPreviewSurface(id));
applySettingsForUnlockFocus(builder, id);
-
mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler);
mState[id] = STATE_PREVIEW;
mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
@@ -1069,6 +1162,9 @@ public class CaptureModule implements CameraModule, PhotoController,
if(mPostProcessor != null) {
mPostProcessor.onClose();
}
+ if(mFrameProcessor != null) {
+ mFrameProcessor.onClose();
+ }
for (int i = 0; i < MAX_NUM_CAM; i++) {
if (null != mCaptureSession[i]) {
if (mIsLinked) {
@@ -1113,6 +1209,7 @@ public class CaptureModule implements CameraModule, PhotoController,
builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
applyAFRegions(builder, id);
applyCommonSettings(builder, id);
+ applyFaceDetect(builder, id);
}
private void applySettingsForCapture(CaptureRequest.Builder builder, int id) {
@@ -1125,12 +1222,14 @@ public class CaptureModule implements CameraModule, PhotoController,
builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
applyCommonSettings(builder, id);
+ applyFaceDetect(builder, id);
}
private void applySettingsForUnlockFocus(CaptureRequest.Builder builder, int id) {
builder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
applyCommonSettings(builder, id);
+ applyFaceDetect(builder, id);
}
private void applySettingsForAutoFocus(CaptureRequest.Builder builder, int id) {
@@ -1138,6 +1237,7 @@ public class CaptureModule implements CameraModule, PhotoController,
.CONTROL_AF_TRIGGER_START);
applyAFRegions(builder, id);
applyCommonSettings(builder, id);
+ applyFaceDetect(builder, id);
}
private void applyCommonSettings(CaptureRequest.Builder builder, int id) {
@@ -1254,6 +1354,16 @@ public class CaptureModule implements CameraModule, PhotoController,
mCurrentMode = isBackCamera() ? getCameraMode() : FRONT_MODE;
}
+ private ArrayList<Integer> getFrameProcFilterId() {
+ String scene = mSettingsManager.getValue(SettingsManager.KEY_MAKEUP);
+ ArrayList<Integer> filters = new ArrayList<Integer>();
+ if(scene != null && scene.equalsIgnoreCase("on")) {
+ filters.add(FrameProcessor.FILTER_MAKEUP);
+ }
+
+ return filters;
+ }
+
private int getPostProcFilterId() {
String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE);
if (scene != null) {
@@ -1281,6 +1391,9 @@ public class CaptureModule implements CameraModule, PhotoController,
Log.d(TAG, "Chosen postproc filter id : "+getPostProcFilterId());
mPostProcessor.onOpen(getPostProcFilterId());
}
+ if(mFrameProcessor != null) {
+ mFrameProcessor.onOpen(getFrameProcFilterId());
+ }
if(mPostProcessor.isFilterOn()) {
setUpCameraOutputs(ImageFormat.YUV_420_888);
} else {
@@ -1551,7 +1664,6 @@ public class CaptureModule implements CameraModule, PhotoController,
@Override
public void onPreviewUIDestroyed() {
-
}
@Override
@@ -1764,6 +1876,7 @@ public class CaptureModule implements CameraModule, PhotoController,
mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest
.CONTROL_AF_TRIGGER_IDLE);
applyCommonSettings(mPreviewRequestBuilder[id], id);
+ applyFaceDetect(mPreviewRequestBuilder[id], id);
}
public float getZoomValue() {
@@ -1816,6 +1929,7 @@ public class CaptureModule implements CameraModule, PhotoController,
applyIso(mPreviewRequestBuilder[cameraId]);
break;
}
+ applyFaceDetect(mPreviewRequestBuilder[cameraId], cameraId);
return updatePreview;
}
@@ -1919,6 +2033,18 @@ public class CaptureModule implements CameraModule, PhotoController,
if (getCameraMode() == DUAL_MODE && id == MONO_ID) {
return mUI.getSurfaceHolder2().getSurface();
} else {
+ return mFrameProcessor.getInputSurface();
+ }
+ } else {
+ return mFrameProcessor.getInputSurface();
+ }
+ }
+
+ private Surface getPreviewSurfaceForSession(int id) {
+ if (isBackCamera()) {
+ if (getCameraMode() == DUAL_MODE && id == MONO_ID) {
+ return mUI.getSurfaceHolder2().getSurface();
+ } else {
return mUI.getSurfaceHolder().getSurface();
}
} else {
@@ -2018,6 +2144,9 @@ public class CaptureModule implements CameraModule, PhotoController,
case SettingsManager.KEY_MONO_PREVIEW:
if (count == 0) restart();
return;
+ case SettingsManager.KEY_MAKEUP:
+ restart();
+ return;
case SettingsManager.KEY_SCENE_MODE:
if (count == 0 && checkNeedToRestart(value)) {
restart();
@@ -2107,7 +2236,8 @@ public class CaptureModule implements CameraModule, PhotoController,
float prevRatio = (float) prevSize.getWidth() / prevSize.getHeight();
if (Math.abs(prevRatio - ratio) < 0.01) {
// flip w and h
- if (prevSize.getWidth() <= screenH && prevSize.getHeight() <= screenW) {
+ if (prevSize.getWidth() <= screenH && prevSize.getHeight() <= screenW &&
+ prevSize.getWidth() <= pictureSize.getWidth() && prevSize.getHeight() <= pictureSize.getHeight()) {
return prevSize;
} else {
optimal = prevSize;
diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java
index 50104570d..5b8ecad6e 100644
--- a/src/com/android/camera/CaptureUI.java
+++ b/src/com/android/camera/CaptureUI.java
@@ -92,7 +92,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
SettingsManager.KEY_LONGSHOT,
SettingsManager.KEY_EXPOSURE,
SettingsManager.KEY_WHITE_BALANCE,
- SettingsManager.KEY_CAMERA2
+ SettingsManager.KEY_CAMERA2,
+ SettingsManager.KEY_MAKEUP
};
String[] mDeveloperKeys = new String[]{
SettingsManager.KEY_MONO_ONLY,
diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java
index d19cab2a6..215ab8436 100644
--- a/src/com/android/camera/SettingsManager.java
+++ b/src/com/android/camera/SettingsManager.java
@@ -68,6 +68,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
public static final String KEY_FOCUS_MODE = "pref_camera2_focusmode_key";
public static final String KEY_FLASH_MODE = "pref_camera2_flashmode_key";
public static final String KEY_WHITE_BALANCE = "pref_camera2_whitebalance_key";
+ public static final String KEY_MAKEUP = "pref_camera2_makeup_key";
public static final String KEY_CAMERA2 = "pref_camera2_camera2_key";
public static final String KEY_MONO_ONLY = "pref_camera2_mono_only_key";
public static final String KEY_MONO_PREVIEW = "pref_camera2_mono_preview_key";
diff --git a/src/com/android/camera/imageprocessor/FrameProcessor.java b/src/com/android/camera/imageprocessor/FrameProcessor.java
new file mode 100644
index 000000000..951479de9
--- /dev/null
+++ b/src/com/android/camera/imageprocessor/FrameProcessor.java
@@ -0,0 +1,335 @@
+/*
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.camera.imageprocessor;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicYuvToRGB;
+import android.renderscript.Type;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import com.android.camera.CaptureModule;
+import com.android.camera.PhotoModule;
+import com.android.camera.imageprocessor.filter.BeautificationFilter;
+import com.android.camera.imageprocessor.filter.ImageFilter;
+import com.android.camera.util.CameraUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+public class FrameProcessor {
+
+ private ImageReader mInputImageReader;
+ private Allocation mInputAllocation;
+ private Allocation mProcessAllocation;
+ private Allocation mOutputAllocation;
+
+ private HandlerThread mProcessingThread;
+ private Handler mProcessingHandler;
+ private HandlerThread mOutingThread;
+ private Handler mOutingHandler;
+
+ public ProcessingTask mTask;
+ private RenderScript mRs;
+ private Activity mActivity;
+ ScriptC_YuvToRgb mRsYuvToRGB;
+ ScriptC_rotator mRsRotator;
+ private Size mSize;
+ private Object mAllocationLock = new Object();
+ private boolean mIsAllocationEverUsed;
+ private ArrayList<ImageFilter> mPreviewFilters;
+ private ArrayList<ImageFilter> mFinalFilters;
+ private Surface mSurfaceAsItIs;
+ private boolean mIsActive = false;
+ public static final int FILTER_NONE = 0;
+ public static final int FILTER_MAKEUP = 1;
+ private CaptureModule mModule;
+
+ public FrameProcessor(Activity activity, CaptureModule module) {
+ mActivity = activity;
+ mModule = module;
+ mPreviewFilters = new ArrayList<ImageFilter>();
+ mFinalFilters = new ArrayList<ImageFilter>();
+ }
+
+ public void init(Size previewDim) {
+ mSize = previewDim;
+ synchronized (mAllocationLock) {
+ mRs = RenderScript.create(mActivity);
+ mRsYuvToRGB = new ScriptC_YuvToRgb(mRs);
+ mRsRotator = new ScriptC_rotator(mRs);
+ mInputImageReader = ImageReader.newInstance(mSize.getWidth(), mSize.getHeight(), ImageFormat.YUV_420_888, 8);
+
+ Type.Builder rgbTypeBuilder = new Type.Builder(mRs, Element.RGBA_8888(mRs));
+ rgbTypeBuilder.setX(mSize.getHeight());
+ rgbTypeBuilder.setY(mSize.getWidth());
+ mOutputAllocation = Allocation.createTyped(mRs, rgbTypeBuilder.create(),
+ Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT);
+
+ if (mProcessingThread == null) {
+ mProcessingThread = new HandlerThread("FrameProcessor");
+ mProcessingThread.start();
+ mProcessingHandler = new Handler(mProcessingThread.getLooper());
+ }
+
+ if (mOutingThread == null) {
+ mOutingThread = new HandlerThread("FrameOutingThread");
+ mOutingThread.start();
+ mOutingHandler = new Handler(mOutingThread.getLooper());
+ }
+
+ mTask = new ProcessingTask();
+ mInputImageReader.setOnImageAvailableListener(mTask, mProcessingHandler);
+ mIsAllocationEverUsed = false;
+ }
+ }
+
+ private void createAllocation(int width, int height) {
+ Type.Builder yuvTypeBuilder = new Type.Builder(mRs, Element.YUV(mRs));
+ yuvTypeBuilder.setX(width);
+ yuvTypeBuilder.setY(height);
+ yuvTypeBuilder.setYuvFormat(ImageFormat.NV21);
+ mInputAllocation = Allocation.createTyped(mRs, yuvTypeBuilder.create(), Allocation.USAGE_SCRIPT);
+ Type.Builder nv21TypeBuilder = new Type.Builder(mRs, Element.U8(mRs));
+ nv21TypeBuilder.setX(width * height * 3 / 2);
+ mProcessAllocation = Allocation.createTyped(mRs, nv21TypeBuilder.create(), Allocation.USAGE_SCRIPT);
+ mRsRotator.set_gIn(mInputAllocation);
+ mRsRotator.set_gOut(mProcessAllocation);
+ mRsRotator.set_width(width);
+ mRsRotator.set_height(height);
+ mRsYuvToRGB.set_gIn(mProcessAllocation);
+ mRsYuvToRGB.set_width(height);
+ mRsYuvToRGB.set_height(width);
+ }
+
+ public ArrayList<ImageFilter> getFrameFilters() {
+ return mFinalFilters;
+ }
+
+ private void cleanFilterSet() {
+ if(mPreviewFilters != null) {
+ for (ImageFilter filter : mPreviewFilters) {
+ filter.deinit();
+ }
+ }
+ if(mFinalFilters != null) {
+ for (ImageFilter filter : mFinalFilters) {
+ filter.deinit();
+ }
+ }
+ mPreviewFilters = new ArrayList<ImageFilter>();
+ mFinalFilters = new ArrayList<ImageFilter>();
+ }
+
+ public void onOpen(ArrayList<Integer> filterIds) {
+ mIsActive = true;
+ synchronized (mAllocationLock) {
+ cleanFilterSet();
+ if (filterIds != null) {
+ for (Integer i : filterIds) {
+ addFilter(i.intValue());
+ }
+ }
+ }
+ }
+
+ private void addFilter(int filterId) {
+ if(filterId == FILTER_MAKEUP) {
+ ImageFilter filter = new BeautificationFilter(mModule);
+ if(filter.isSupported()) {
+ mPreviewFilters.add(filter);
+ mFinalFilters.add(filter);
+ }
+ }
+ }
+
+ public void onClose() {
+ mIsActive = false;
+ synchronized (mAllocationLock) {
+ if (mIsAllocationEverUsed) {
+ if (mInputAllocation != null) {
+ mInputAllocation.destroy();
+ }
+ if (mOutputAllocation != null) {
+ mOutputAllocation.destroy();
+ }
+ if (mProcessAllocation != null) {
+ mProcessAllocation.destroy();
+ }
+ }
+ if (mRs != null) {
+ mRs.destroy();
+ }
+ mRs = null;
+ mProcessAllocation = null;
+ mOutputAllocation = null;
+ mInputAllocation = null;
+ }
+ if (mProcessingThread != null) {
+ mProcessingThread.quitSafely();
+ try {
+ mProcessingThread.join();
+ mProcessingThread = null;
+ mProcessingHandler = null;
+ } catch (InterruptedException e) {
+ }
+ }
+ if (mOutingThread != null) {
+ mOutingThread.quitSafely();
+ try {
+ mOutingThread.join();
+ mOutingThread = null;
+ mOutingHandler = null;
+ } catch (InterruptedException e) {
+ }
+ }
+ for(ImageFilter filter : mPreviewFilters) {
+ filter.deinit();
+ }
+ for(ImageFilter filter : mFinalFilters) {
+ filter.deinit();
+ }
+ }
+
+ public Surface getInputSurface() {
+ if(mPreviewFilters.size() == 0) {
+ return mSurfaceAsItIs;
+ }
+ synchronized (mAllocationLock) {
+ if (mInputImageReader == null)
+ return null;
+ return mInputImageReader.getSurface();
+ }
+ }
+
+ public boolean isFrameFilterEnabled() {
+ if(mPreviewFilters.size() == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public void setOutputSurface(Surface surface) {
+ if(mPreviewFilters.size() == 0) {
+ mSurfaceAsItIs = surface;
+ } else {
+ mOutputAllocation.setSurface(surface);
+ }
+ }
+
+ class ProcessingTask implements Runnable, ImageReader.OnImageAvailableListener {
+ byte[] yvuBytes = null;
+ int ySize;
+ int stride;
+ int height;
+
+ public ProcessingTask() {
+ }
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ synchronized (mAllocationLock) {
+ if(mOutputAllocation == null)
+ return;
+ try {
+ Image image = reader.acquireLatestImage();
+ if(image == null)
+ return;
+ if(!mIsActive) {
+ image.close();
+ return;
+ }
+ mIsAllocationEverUsed = true;
+ ByteBuffer bY = image.getPlanes()[0].getBuffer();
+ ByteBuffer bVU = image.getPlanes()[2].getBuffer();
+ if(yvuBytes == null) {
+ stride = image.getPlanes()[0].getRowStride();
+ height = mSize.getHeight();
+ ySize = stride * mSize.getHeight();
+ yvuBytes = new byte[ySize*3/2];
+ }
+ //Start processing yvu buf
+ for (ImageFilter filter : mPreviewFilters) {
+ filter.init(mSize.getWidth(), mSize.getHeight(), stride, stride);
+ filter.addImage(bY, bVU, 0, new Boolean(true));
+ }
+ //End processing yvu buf
+ bY.get(yvuBytes, 0, bY.remaining());
+ bVU.get(yvuBytes, ySize, bVU.remaining());
+ image.close();
+ mOutingHandler.post(this);
+ } catch (IllegalStateException e) {
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ synchronized (mAllocationLock) {
+ if(!mIsActive) {
+ return;
+ }
+ if(mInputAllocation == null) {
+ createAllocation(stride, height);
+ }
+ mInputAllocation.copyFrom(yvuBytes);
+ mRsRotator.forEach_rotate90andMerge(mInputAllocation);
+ mRsYuvToRGB.forEach_nv21ToRgb(mOutputAllocation);
+ mOutputAllocation.ioSend();
+ }
+ }
+ }
+
+ private native int nativeRotateNV21(ByteBuffer inBuf, int imageWidth, int imageHeight, int degree, ByteBuffer outBuf);
+
+ private native int nativeNV21toRgb(ByteBuffer yvuBuf, ByteBuffer rgbBuf, int width, int height);
+
+ static {
+ System.loadLibrary("jni_imageutil");
+ }
+}
+
diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java
index 7f0e63990..a126e8817 100644
--- a/src/com/android/camera/imageprocessor/PostProcessor.java
+++ b/src/com/android/camera/imageprocessor/PostProcessor.java
@@ -132,9 +132,13 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
}
public boolean isFilterOn() {
- if(mFilter == null)
- return false;
- return true;
+ if(mFilter != null) {
+ return true;
+ }
+ if(mController.getFrameFilters().size() != 0) {
+ return true;
+ }
+ return false;
}
public void onOpen(int postFilterId) {
@@ -389,6 +393,12 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
}
}
}
+ //Start processing FrameProcessor filter as well
+ for (ImageFilter filter : mController.getFrameFilters()) {
+ filter.init(resultImage.width, resultImage.height, resultImage.stride, resultImage.stride);
+ filter.addImage(resultImage.outBuffer, null, 0, new Boolean(false));
+ }
+ //End processing FrameProessor filter
clear();
mStatus = STATUS.INIT;
if(mWatchdog != null) {
diff --git a/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java b/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java
new file mode 100644
index 000000000..6ec9376d0
--- /dev/null
+++ b/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java
@@ -0,0 +1,134 @@
+/*
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.camera.imageprocessor.filter;
+
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.Face;
+import android.util.Log;
+import android.util.Size;
+
+import com.android.camera.CaptureModule;
+import com.android.camera.ui.FilmstripBottomControls;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class BeautificationFilter implements ImageFilter {
+
+ int mWidth;
+ int mHeight;
+ int mStrideY;
+ int mStrideVU;
+ private CaptureModule mModule;
+ private static boolean DEBUG = false;
+ private static String TAG = "BeautificationFilter";
+ private static boolean mIsSupported = false;
+
+ public BeautificationFilter(CaptureModule module) {
+ mModule = module;
+ }
+
+ @Override
+ public List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder) {
+ return null;
+ }
+
+ @Override
+ public String getStringName() {
+ return "BeautificationFilter";
+ }
+
+ @Override
+ public int getNumRequiredImage() {
+ return 0;
+ }
+
+ @Override
+ public void init(int width, int height, int strideY, int strideVU) {
+ mWidth = width;
+ mHeight = height;
+ mStrideY = strideY;
+ mStrideVU = strideVU;
+ }
+
+ @Override
+ public void deinit() {
+
+ }
+
+ @Override
+ public void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object isPreview) {
+ Rect back = mModule.getCameraRegion();
+ Face[] faces;
+ if(((Boolean)isPreview).booleanValue()) {
+ faces = mModule.getPreviewFaces();
+ } else {
+ faces = mModule.getStickyFaces();
+ }
+ float widthRatio = (float)mWidth/back.width();
+ float heightRatio = (float)mHeight/back.height();
+ if(faces == null || faces.length == 0)
+ return;
+ Rect rect = faces[0].getBounds();
+ int value = nativeBeautificationProcess(bY, bVU, mWidth, mHeight, mStrideY,
+ (int)(rect.left*widthRatio), (int)(rect.top*heightRatio),
+ (int)(rect.right*widthRatio), (int)(rect.bottom*heightRatio));
+ if(DEBUG && value < 0) {
+ if(value == -1) {
+ Log.d(TAG, "library initialization is failed.");
+ } else if(value == -2) {
+ Log.d(TAG, "No face is recognized");
+ }
+ }
+ }
+
+ @Override
+ public ResultImage processImage() {
+ return null;
+ }
+
+ @Override
+ public boolean isSupported() {
+ return mIsSupported;
+ }
+
+ private native int nativeBeautificationProcess(ByteBuffer yB, ByteBuffer vuB,
+ int width, int height, int stride, int fleft, int ftop, int fright, int fbottom);
+
+ static {
+ try {
+ System.loadLibrary("jni_makeup");
+ mIsSupported = true;
+ }catch(UnsatisfiedLinkError e) {
+ mIsSupported = false;
+ }
+ }
+}