summaryrefslogtreecommitdiffstats
path: root/media/mca/filterpacks
diff options
context:
space:
mode:
Diffstat (limited to 'media/mca/filterpacks')
-rw-r--r--media/mca/filterpacks/Android.mk55
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java62
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java63
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java51
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java106
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java96
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java44
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java93
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java66
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java76
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java66
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java309
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java153
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java118
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java176
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java65
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java59
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java128
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java147
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java141
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java129
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java175
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java92
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java132
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java125
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java140
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java178
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java112
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java120
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java187
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java139
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java142
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java144
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java60
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java218
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java106
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java211
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java124
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java156
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java170
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java111
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java153
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java116
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java154
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java133
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java90
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java143
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java153
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java66
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java61
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/performance/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java49
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/StringSource.java59
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java55
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java275
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java257
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/ui/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java976
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java469
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java44
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java358
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java567
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java265
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java282
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java4
-rw-r--r--media/mca/filterpacks/native/base/geometry.cpp160
-rw-r--r--media/mca/filterpacks/native/base/geometry.h111
-rw-r--r--media/mca/filterpacks/native/base/time_util.cpp90
-rw-r--r--media/mca/filterpacks/native/base/time_util.h69
-rw-r--r--media/mca/filterpacks/native/base/utilities.h160
-rw-r--r--media/mca/filterpacks/native/base/vec_types.h177
-rw-r--r--media/mca/filterpacks/native/imageproc/brightness.c97
-rw-r--r--media/mca/filterpacks/native/imageproc/contrast.c85
-rw-r--r--media/mca/filterpacks/native/imageproc/invert.c46
-rw-r--r--media/mca/filterpacks/native/imageproc/to_rgba.c145
87 files changed, 11705 insertions, 0 deletions
diff --git a/media/mca/filterpacks/Android.mk b/media/mca/filterpacks/Android.mk
new file mode 100644
index 0000000..6166b1e
--- /dev/null
+++ b/media/mca/filterpacks/Android.mk
@@ -0,0 +1,55 @@
+# Copyright (C) 2011 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+##
+# base
+##
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libfilterpack_base
+LOCAL_SRC_FILES := native/base/geometry.cpp \
+ native/base/time_util.cpp
+
+LOCAL_CFLAGS := -DANDROID
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_STATIC_LIBRARY)
+
+##
+# filterpack_imageproc
+##
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libfilterpack_imageproc
+
+LOCAL_SRC_FILES += native/imageproc/brightness.c \
+ native/imageproc/contrast.c \
+ native/imageproc/invert.c \
+ native/imageproc/to_rgba.c
+
+LOCAL_SHARED_LIBRARIES := libutils libfilterfw
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java
new file mode 100644
index 0000000..4185343
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.lang.Runnable;
+
+/**
+ * @hide
+ */
+public class CallbackFilter extends Filter {
+
+ @GenerateFieldPort(name = "listener", hasDefault = true)
+ private FilterContext.OnFrameReceivedListener mListener;
+
+ @GenerateFieldPort(name = "userData", hasDefault = true)
+ private Object mUserData;
+
+ @GenerateFinalPort(name = "callUiThread", hasDefault = true)
+ private boolean mCallbacksOnUiThread = true;
+
+ private Handler mUiThreadHandler;
+
+ private class CallbackRunnable implements Runnable {
+ private Filter mFilter;
+ private Frame mFrame;
+ private Object mUserData;
+ private FilterContext.OnFrameReceivedListener mListener;
+
+ public CallbackRunnable(FilterContext.OnFrameReceivedListener listener, Filter filter, Frame frame, Object userData) {
+ mListener = listener;
+ mFilter = filter;
+ mFrame = frame;
+ mUserData = userData;
+ }
+
+ public void run() {
+ mListener.onFrameReceived(mFilter, mFrame, mUserData);
+ mFrame.release();
+ }
+ }
+
+ public CallbackFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("frame");
+ }
+
+ public void prepare(FilterContext context) {
+ if (mCallbacksOnUiThread) {
+ mUiThreadHandler = new Handler(Looper.getMainLooper());
+ }
+ }
+
+ public void process(FilterContext context) {
+ // Get frame and forward to listener
+ final Frame input = pullInput("frame");
+ if (mListener != null) {
+ if (mCallbacksOnUiThread) {
+ input.retain();
+ CallbackRunnable uiRunnable = new CallbackRunnable(mListener, this, input, mUserData);
+ if (!mUiThreadHandler.post(uiRunnable)) {
+ throw new RuntimeException("Unable to send callback to UI thread!");
+ }
+ } else {
+ mListener.onFrameReceived(this, input, mUserData);
+ }
+ } else {
+ throw new RuntimeException("CallbackFilter received frame, but no listener set!");
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java
new file mode 100644
index 0000000..6b8cbc7
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+
+/**
+ * @hide
+ */
+public class FrameBranch extends Filter {
+
+ @GenerateFinalPort(name = "outputs", hasDefault = true)
+ private int mNumberOfOutputs = 2;
+
+ public FrameBranch(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("in");
+ for (int i = 0; i < mNumberOfOutputs; ++i) {
+ addOutputBasedOnInput("out" + i, "in");
+ }
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("in");
+
+ // Push output
+ for (int i = 0; i < mNumberOfOutputs; ++i) {
+ pushOutput("out" + i, input);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java
new file mode 100644
index 0000000..518b837
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class FrameFetch extends Filter {
+
+ @GenerateFinalPort(name = "format", hasDefault = true)
+ private FrameFormat mFormat;
+
+ @GenerateFieldPort(name = "key")
+ private String mKey;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ private boolean mRepeatFrame = false;
+
+ public FrameFetch(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", mFormat == null ? FrameFormat.unspecified() : mFormat);
+ }
+
+ public void process(FilterContext context) {
+ Frame output = context.fetchFrame(mKey);
+ if (output != null) {
+ pushOutput("frame", output);
+ if (!mRepeatFrame) {
+ closeOutputPort("frame");
+ }
+ } else {
+ delayNextProcess(250);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java
new file mode 100644
index 0000000..1218d1a
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+
+/**
+ * @hide
+ */
+public class FrameSource extends Filter {
+
+ @GenerateFinalPort(name = "format")
+ private FrameFormat mFormat;
+
+ @GenerateFieldPort(name = "frame", hasDefault = true)
+ private Frame mFrame = null;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ private boolean mRepeatFrame = false;
+
+ public FrameSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", mFormat);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mFrame != null) {
+ // Push output
+ pushOutput("frame", mFrame);
+ }
+
+ if (!mRepeatFrame) {
+ // Close output port as we are done here
+ closeOutputPort("frame");
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java
new file mode 100644
index 0000000..3aadaac
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+
+/**
+ * @hide
+ */
+public class FrameStore extends Filter {
+
+ @GenerateFieldPort(name = "key")
+ private String mKey;
+
+ public FrameStore(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("frame");
+ }
+
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("frame");
+
+ // Store frame
+ context.storeFrame(mKey, input);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java
new file mode 100644
index 0000000..1776820
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class GLTextureSource extends Filter {
+
+ @GenerateFieldPort(name = "texId")
+ private int mTexId;
+
+ @GenerateFieldPort(name = "width")
+ private int mWidth;
+
+ @GenerateFieldPort(name = "height")
+ private int mHeight;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ private boolean mRepeatFrame = false;
+
+ /* This timestamp will be used for all output frames from this source. They
+ * represent nanoseconds, and should be positive and monotonically
+ * increasing. Set to Frame.TIMESTAMP_UNKNOWN if timestamps are not
+ * meaningful for these textures.
+ */
+ @GenerateFieldPort(name = "timestamp", hasDefault = true)
+ private long mTimestamp = Frame.TIMESTAMP_UNKNOWN;
+
+ private Frame mFrame;
+
+ public GLTextureSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // Release frame, so that it is recreated during the next process call
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Generate frame if not generated already
+ if (mFrame == null) {
+ FrameFormat outputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mFrame = context.getFrameManager().newBoundFrame(outputFormat,
+ GLFrame.EXISTING_TEXTURE_BINDING,
+ mTexId);
+ mFrame.setTimestamp(mTimestamp);
+ }
+
+ // Push output
+ pushOutput("frame", mFrame);
+
+ if (!mRepeatFrame) {
+ // Close output port as we are done here
+ closeOutputPort("frame");
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mFrame != null) {
+ mFrame.release();
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java
new file mode 100644
index 0000000..b2285cd
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class GLTextureTarget extends Filter {
+
+ @GenerateFieldPort(name = "texId")
+ private int mTexId;
+
+ public GLTextureTarget(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("frame");
+
+ FrameFormat format = ImageFormat.create(input.getFormat().getWidth(),
+ input.getFormat().getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+
+ Frame frame = context.getFrameManager().newBoundFrame(format, GLFrame.EXISTING_TEXTURE_BINDING, mTexId);
+
+ // Copy to our texture frame
+ frame.setDataFromFrame(input);
+ frame.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java b/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java
new file mode 100644
index 0000000..6c22ee7
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.PrimitiveFormat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class InputStreamSource extends Filter {
+
+ @GenerateFinalPort(name = "target")
+ private String mTarget;
+
+ @GenerateFieldPort(name = "stream")
+ private InputStream mInputStream;
+
+ @GenerateFinalPort(name = "format", hasDefault = true)
+ private MutableFrameFormat mOutputFormat = null;
+
+ public InputStreamSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ int target = FrameFormat.readTargetString(mTarget);
+ if (mOutputFormat == null) {
+ mOutputFormat = PrimitiveFormat.createByteFormat(target);
+ }
+ addOutputPort("data", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ int fileSize = 0;
+ ByteBuffer byteBuffer = null;
+
+ // Read the file
+ try {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = mInputStream.read(buffer)) > 0) {
+ byteStream.write(buffer, 0, bytesRead);
+ fileSize += bytesRead;
+ }
+ byteBuffer = ByteBuffer.wrap(byteStream.toByteArray());
+ } catch (IOException exception) {
+ throw new RuntimeException(
+ "InputStreamSource: Could not read stream: " + exception.getMessage() + "!");
+ }
+
+ // Put it into a frame
+ mOutputFormat.setDimensions(fileSize);
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ output.setData(byteBuffer);
+
+ // Push output
+ pushOutput("data", output);
+
+ // Release pushed frame
+ output.release();
+
+ // Close output port as we are done here
+ closeOutputPort("data");
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java
new file mode 100644
index 0000000..f3e08e4
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+
+/**
+ * @hide
+ */
+public class NullFilter extends Filter {
+
+ public NullFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("frame");
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ pullInput("frame");
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java b/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java
new file mode 100644
index 0000000..d511e44
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import java.util.Set;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ObjectFormat;
+
+/**
+ * @hide
+ */
+public class ObjectSource extends Filter {
+
+ @GenerateFieldPort(name = "object")
+ private Object mObject;
+
+ @GenerateFinalPort(name = "format", hasDefault = true)
+ private FrameFormat mOutputFormat = FrameFormat.unspecified();
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ boolean mRepeatFrame = false;
+
+ private Frame mFrame;
+
+ public ObjectSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // If no frame has been created, create one now.
+ if (mFrame == null) {
+ if (mObject == null) {
+ throw new NullPointerException("ObjectSource producing frame with no object set!");
+ }
+ FrameFormat outputFormat = ObjectFormat.fromObject(mObject, FrameFormat.TARGET_SIMPLE);
+ mFrame = context.getFrameManager().newFrame(outputFormat);
+ mFrame.setObjectValue(mObject);
+ mFrame.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+ }
+
+ // Push output
+ pushOutput("frame", mFrame);
+
+ // Wait for free output
+ if (!mRepeatFrame) {
+ closeOutputPort("frame");
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ mFrame.release();
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // Release our internal frame, so that it is regenerated on the next call to process().
+ if (name.equals("object")) {
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java b/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java
new file mode 100644
index 0000000..3d3d0f1
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class OutputStreamTarget extends Filter {
+
+ @GenerateFieldPort(name = "stream")
+ private OutputStream mOutputStream;
+
+ public OutputStreamTarget(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("data");
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ Frame input = pullInput("data");
+ ByteBuffer data;
+
+ if (input.getFormat().getObjectClass() == String.class) {
+ String stringVal = (String)input.getObjectValue();
+ data = ByteBuffer.wrap(stringVal.getBytes());
+ } else {
+ data = input.getData();
+ }
+ try {
+ mOutputStream.write(data.array(), 0, data.limit());
+ mOutputStream.flush();
+ } catch (IOException exception) {
+ throw new RuntimeException(
+ "OutputStreamTarget: Could not write to stream: " + exception.getMessage() + "!");
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java
new file mode 100644
index 0000000..254167a
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+
+/**
+ * @hide
+ */
+public class RetargetFilter extends Filter {
+
+ @GenerateFinalPort(name = "target", hasDefault = false)
+ private String mTargetString;
+
+ private MutableFrameFormat mOutputFormat;
+ private int mTarget = -1;
+
+ public RetargetFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Setup target
+ mTarget = FrameFormat.readTargetString(mTargetString);
+
+ // Add ports
+ addInputPort("frame");
+ addOutputBasedOnInput("frame", "frame");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ MutableFrameFormat retargeted = inputFormat.mutableCopy();
+ retargeted.setTarget(mTarget);
+ return retargeted;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("frame");
+
+ // Create output frame
+ Frame output = context.getFrameManager().duplicateFrameToTarget(input, mTarget);
+
+ // Push output
+ pushOutput("frame", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java
new file mode 100644
index 0000000..473369c
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class AlphaBlendFilter extends ImageCombineFilter {
+
+ private final String mAlphaBlendShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float weight;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 colorL = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 colorR = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " float blend = texture2D(tex_sampler_2, v_texcoord).r * weight;\n" +
+ " gl_FragColor = colorL * (1.0 - blend) + colorR * blend;\n" +
+ "}\n";
+
+ public AlphaBlendFilter(String name) {
+ super(name, new String[] { "source", "overlay", "mask" }, "blended", "weight");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ throw new RuntimeException("TODO: Write native implementation for AlphaBlend!");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mAlphaBlendShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java
new file mode 100644
index 0000000..c71c1c9
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class AutoFixFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ @GenerateFieldPort(name = "scale")
+ private float mScale;
+
+ private static final int normal_cdf[] = {
+ 9, 33, 50, 64, 75, 84, 92, 99, 106, 112, 117, 122, 126, 130, 134, 138, 142,
+ 145, 148, 150, 154, 157, 159, 162, 164, 166, 169, 170, 173, 175, 177, 179,
+ 180, 182, 184, 186, 188, 189, 190, 192, 194, 195, 197, 198, 199, 200, 202,
+ 203, 205, 206, 207, 208, 209, 210, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 229, 230, 231, 232, 233,
+ 234, 235, 236, 236, 237, 238, 239, 239, 240, 240, 242, 242, 243, 244, 245,
+ 245, 246, 247, 247, 248, 249, 249, 250, 250, 251, 252, 253, 253, 254, 255,
+ 255, 256, 256, 257, 258, 258, 259, 259, 259, 260, 261, 262, 262, 263, 263,
+ 264, 264, 265, 265, 266, 267, 267, 268, 268, 269, 269, 269, 270, 270, 271,
+ 272, 272, 273, 273, 274, 274, 275, 275, 276, 276, 277, 277, 277, 278, 278,
+ 279, 279, 279, 280, 280, 281, 282, 282, 282, 283, 283, 284, 284, 285, 285,
+ 285, 286, 286, 287, 287, 288, 288, 288, 289, 289, 289, 290, 290, 290, 291,
+ 292, 292, 292, 293, 293, 294, 294, 294, 295, 295, 296, 296, 296, 297, 297,
+ 297, 298, 298, 298, 299, 299, 299, 299, 300, 300, 301, 301, 302, 302, 302,
+ 303, 303, 304, 304, 304, 305, 305, 305, 306, 306, 306, 307, 307, 307, 308,
+ 308, 308, 309, 309, 309, 309, 310, 310, 310, 310, 311, 312, 312, 312, 313,
+ 313, 313, 314, 314, 314, 315, 315, 315, 315, 316, 316, 316, 317, 317, 317,
+ 318, 318, 318, 319, 319, 319, 319, 319, 320, 320, 320, 321, 321, 322, 322,
+ 322, 323, 323, 323, 323, 324, 324, 324, 325, 325, 325, 325, 326, 326, 326,
+ 327, 327, 327, 327, 328, 328, 328, 329, 329, 329, 329, 329, 330, 330, 330,
+ 330, 331, 331, 332, 332, 332, 333, 333, 333, 333, 334, 334, 334, 334, 335,
+ 335, 335, 336, 336, 336, 336, 337, 337, 337, 337, 338, 338, 338, 339, 339,
+ 339, 339, 339, 339, 340, 340, 340, 340, 341, 341, 342, 342, 342, 342, 343,
+ 343, 343, 344, 344, 344, 344, 345, 345, 345, 345, 346, 346, 346, 346, 347,
+ 347, 347, 347, 348, 348, 348, 348, 349, 349, 349, 349, 349, 349, 350, 350,
+ 350, 350, 351, 351, 352, 352, 352, 352, 353, 353, 353, 353, 354, 354, 354,
+ 354, 355, 355, 355, 355, 356, 356, 356, 356, 357, 357, 357, 357, 358, 358,
+ 358, 358, 359, 359, 359, 359, 359, 359, 359, 360, 360, 360, 360, 361, 361,
+ 362, 362, 362, 362, 363, 363, 363, 363, 364, 364, 364, 364, 365, 365, 365,
+ 365, 366, 366, 366, 366, 366, 367, 367, 367, 367, 368, 368, 368, 368, 369,
+ 369, 369, 369, 369, 369, 370, 370, 370, 370, 370, 371, 371, 372, 372, 372,
+ 372, 373, 373, 373, 373, 374, 374, 374, 374, 374, 375, 375, 375, 375, 376,
+ 376, 376, 376, 377, 377, 377, 377, 378, 378, 378, 378, 378, 379, 379, 379,
+ 379, 379, 379, 380, 380, 380, 380, 381, 381, 381, 382, 382, 382, 382, 383,
+ 383, 383, 383, 384, 384, 384, 384, 385, 385, 385, 385, 385, 386, 386, 386,
+ 386, 387, 387, 387, 387, 388, 388, 388, 388, 388, 389, 389, 389, 389, 389,
+ 389, 390, 390, 390, 390, 391, 391, 392, 392, 392, 392, 392, 393, 393, 393,
+ 393, 394, 394, 394, 394, 395, 395, 395, 395, 396, 396, 396, 396, 396, 397,
+ 397, 397, 397, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, 400, 400,
+ 400, 400, 400, 401, 401, 402, 402, 402, 402, 403, 403, 403, 403, 404, 404,
+ 404, 404, 405, 405, 405, 405, 406, 406, 406, 406, 406, 407, 407, 407, 407,
+ 408, 408, 408, 408, 409, 409, 409, 409, 409, 409, 410, 410, 410, 410, 411,
+ 411, 412, 412, 412, 412, 413, 413, 413, 413, 414, 414, 414, 414, 415, 415,
+ 415, 415, 416, 416, 416, 416, 417, 417, 417, 417, 418, 418, 418, 418, 419,
+ 419, 419, 419, 419, 419, 420, 420, 420, 420, 421, 421, 422, 422, 422, 422,
+ 423, 423, 423, 423, 424, 424, 424, 425, 425, 425, 425, 426, 426, 426, 426,
+ 427, 427, 427, 427, 428, 428, 428, 429, 429, 429, 429, 429, 429, 430, 430,
+ 430, 430, 431, 431, 432, 432, 432, 433, 433, 433, 433, 434, 434, 434, 435,
+ 435, 435, 435, 436, 436, 436, 436, 437, 437, 437, 438, 438, 438, 438, 439,
+ 439, 439, 439, 439, 440, 440, 440, 441, 441, 442, 442, 442, 443, 443, 443,
+ 443, 444, 444, 444, 445, 445, 445, 446, 446, 446, 446, 447, 447, 447, 448,
+ 448, 448, 449, 449, 449, 449, 449, 450, 450, 450, 451, 451, 452, 452, 452,
+ 453, 453, 453, 454, 454, 454, 455, 455, 455, 456, 456, 456, 457, 457, 457,
+ 458, 458, 458, 459, 459, 459, 459, 460, 460, 460, 461, 461, 462, 462, 462,
+ 463, 463, 463, 464, 464, 465, 465, 465, 466, 466, 466, 467, 467, 467, 468,
+ 468, 469, 469, 469, 469, 470, 470, 470, 471, 472, 472, 472, 473, 473, 474,
+ 474, 474, 475, 475, 476, 476, 476, 477, 477, 478, 478, 478, 479, 479, 479,
+ 480, 480, 480, 481, 482, 482, 483, 483, 484, 484, 484, 485, 485, 486, 486,
+ 487, 487, 488, 488, 488, 489, 489, 489, 490, 490, 491, 492, 492, 493, 493,
+ 494, 494, 495, 495, 496, 496, 497, 497, 498, 498, 499, 499, 499, 500, 501,
+ 502, 502, 503, 503, 504, 504, 505, 505, 506, 507, 507, 508, 508, 509, 509,
+ 510, 510, 511, 512, 513, 513, 514, 515, 515, 516, 517, 517, 518, 519, 519,
+ 519, 520, 521, 522, 523, 524, 524, 525, 526, 526, 527, 528, 529, 529, 530,
+ 531, 532, 533, 534, 535, 535, 536, 537, 538, 539, 539, 540, 542, 543, 544,
+ 545, 546, 547, 548, 549, 549, 550, 552, 553, 554, 555, 556, 558, 559, 559,
+ 561, 562, 564, 565, 566, 568, 569, 570, 572, 574, 575, 577, 578, 579, 582,
+ 583, 585, 587, 589, 590, 593, 595, 597, 599, 602, 604, 607, 609, 612, 615,
+ 618, 620, 624, 628, 631, 635, 639, 644, 649, 654, 659, 666, 673, 680, 690,
+ 700, 714 };
+
+ private final String mAutoFixShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float scale;\n" +
+ "uniform float shift_scale;\n" +
+ "uniform float hist_offset;\n" +
+ "uniform float hist_scale;\n" +
+ "uniform float density_offset;\n" +
+ "uniform float density_scale;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const vec3 weights = vec3(0.33333, 0.33333, 0.33333);\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float energy = dot(color.rgb, weights);\n" +
+ " float mask_value = energy - 0.5;\n" +
+ " float alpha;\n" +
+ " if (mask_value > 0.0) {\n" +
+ " alpha = (pow(2.0 * mask_value, 1.5) - 1.0) * scale + 1.0;\n" +
+ " } else { \n" +
+ " alpha = (pow(2.0 * mask_value, 2.0) - 1.0) * scale + 1.0;\n" +
+ " }\n" +
+ " float index = energy * hist_scale + hist_offset;\n" +
+ " vec4 temp = texture2D(tex_sampler_1, vec2(index, 0.5));\n" +
+ " float value = temp.g + temp.r * shift_scale;\n" +
+ " index = value * density_scale + density_offset;\n" +
+ " temp = texture2D(tex_sampler_2, vec2(index, 0.5));\n" +
+ " value = temp.g + temp.r * shift_scale;\n" +
+ " float dst_energy = energy * alpha + value * (1.0 - alpha);\n" +
+ " float max_energy = energy / max(color.r, max(color.g, color.b));\n" +
+ " if (dst_energy > max_energy) {\n" +
+ " dst_energy = max_energy;\n" +
+ " }\n" +
+ " if (energy == 0.0) {\n" +
+ " gl_FragColor = color;\n" +
+ " } else {\n" +
+ " gl_FragColor = vec4(color.rgb * dst_energy / energy, color.a);\n" +
+ " }\n" +
+ "}\n";
+
+ private Program mShaderProgram;
+ private Program mNativeProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mHistFrame;
+ private Frame mDensityFrame;
+
+ public AutoFixFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mAutoFixShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mShaderProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void initParameters() {
+ mShaderProgram.setHostValue("shift_scale", 1.0f / 256f);
+ mShaderProgram.setHostValue("hist_offset", 0.5f / 766f);
+ mShaderProgram.setHostValue("hist_scale", 765f / 766f);
+ mShaderProgram.setHostValue("density_offset", 0.5f / 1024f);
+ mShaderProgram.setHostValue("density_scale", 1023f / 1024f);
+ mShaderProgram.setHostValue("scale", mScale);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ int densityDim = 1024;
+ int histDim = 255 * 3 + 1;
+ long precision = (256l * 256l - 1l);
+
+ int[] densityTable = new int[densityDim];
+ for (int i = 0; i < densityDim; ++i) {
+ long temp = normal_cdf[i] * precision / histDim;
+ densityTable[i] = (int) temp;
+ }
+
+ FrameFormat densityFormat = ImageFormat.create(densityDim, 1,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mDensityFrame = context.getFrameManager().newFrame(densityFormat);
+ mDensityFrame.setInts(densityTable);
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mDensityFrame != null) {
+ mDensityFrame.release();
+ mDensityFrame = null;
+ }
+
+ if (mHistFrame != null) {
+ mHistFrame.release();
+ mHistFrame = null;
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mShaderProgram != null) {
+ mShaderProgram.setHostValue("scale", mScale);
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mShaderProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ createHistogramFrame(context, mWidth, mHeight, input.getInts());
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mHistFrame, mDensityFrame};
+ mShaderProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void createHistogramFrame(FilterContext context, int width, int height, int[] data) {
+ int histDims = 255 * 3 + 1;
+ int[] histArray = new int[histDims];
+
+ float border_thickness_ratio = 0.05f;
+ int y_border_thickness = (int) (height * border_thickness_ratio);
+ int x_border_thickness = (int) (width * border_thickness_ratio);
+ int pixels = (width - 2 * x_border_thickness) * (height - 2 * y_border_thickness);
+
+ float count = 0f;
+ for (int y = y_border_thickness; y < height - y_border_thickness; ++y) {
+ for (int x = x_border_thickness; x < width - x_border_thickness; ++x) {
+ int index = y * width + x;
+ int energy = (data[index] & 0xFF) + ((data[index] >> 8) & 0xFF) +
+ ((data[index] >> 16) & 0xFF);
+ histArray[energy] ++;
+ }
+ }
+
+ for (int i = 1; i < histDims; i++) {
+ histArray[i] += histArray[i-1];
+ }
+
+ for (int i = 0; i < histDims; i++) {
+ long temp = (256 * 256 - 1l) * histArray[i] / pixels;
+ histArray[i] = (int) temp;
+ }
+
+ FrameFormat shaderHistFormat = ImageFormat.create(histDims, 1,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mHistFrame != null)
+ mHistFrame.release();
+
+ mHistFrame = context.getFrameManager().newFrame(shaderHistFormat);
+ mHistFrame.setInts(histArray);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
new file mode 100644
index 0000000..d4c901f
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterpacks.imageproc.ImageCombineFilter;
+import android.graphics.Bitmap;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class BitmapOverlayFilter extends Filter {
+
+ @GenerateFieldPort(name = "bitmap")
+ private Bitmap mBitmap;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private Frame mFrame;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mOverlayShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 original = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " gl_FragColor = vec4(original.rgb * (1.0 - mask.a) + mask.rgb, 1.0);\n" +
+ "}\n";
+
+ public BitmapOverlayFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mOverlayShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter FisheyeFilter does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ createBitmapFrame(context);
+ }
+
+ // Process
+ Frame[] inputs = {input, mFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void createBitmapFrame(FilterContext context) {
+ if (mBitmap != null) {
+ FrameFormat format = ImageFormat.create(mBitmap.getWidth(),
+ mBitmap.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+
+ if (mFrame != null) {
+ mFrame.release();
+ }
+
+ mFrame = context.getFrameManager().newFrame(format);
+ mFrame.setBitmap(mBitmap);
+
+ mBitmap.recycle();
+ mBitmap = null;
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java
new file mode 100644
index 0000000..978fc94
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Bitmap;
+
+/**
+ * @hide
+ */
+public class BitmapSource extends Filter {
+
+ @GenerateFieldPort(name = "target")
+ String mTargetString;
+
+ @GenerateFieldPort(name = "bitmap")
+ private Bitmap mBitmap;
+
+ @GenerateFieldPort(name = "recycleBitmap", hasDefault = true)
+ private boolean mRecycleBitmap = true;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ boolean mRepeatFrame = false;
+
+ private int mTarget;
+ private Frame mImageFrame;
+
+ public BitmapSource(String name) {
+ super(name);
+ }
+
+
+ @Override
+ public void setupPorts() {
+ // Setup output format
+ FrameFormat outputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_UNSPECIFIED);
+
+ // Add output port
+ addOutputPort("image", outputFormat);
+ }
+
+ public void loadImage(FilterContext filterContext) {
+ // Create frame with bitmap
+ mTarget = FrameFormat.readTargetString(mTargetString);
+ FrameFormat outputFormat = ImageFormat.create(mBitmap.getWidth(),
+ mBitmap.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ mTarget);
+ mImageFrame = filterContext.getFrameManager().newFrame(outputFormat);
+ mImageFrame.setBitmap(mBitmap);
+ mImageFrame.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+
+ // Free up space used by bitmap
+ if (mRecycleBitmap) {
+ mBitmap.recycle();
+ }
+ mBitmap = null;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // Clear image (to trigger reload) in case parameters have been changed
+ if (name.equals("bitmap") || name.equals("target")) {
+ if (mImageFrame != null) {
+ mImageFrame.release();
+ mImageFrame = null;
+ }
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mImageFrame == null) {
+ loadImage(context);
+ }
+
+ pushOutput("image", mImageFrame);
+
+ if (!mRepeatFrame) {
+ closeOutputPort("image");
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext env) {
+ if (mImageFrame != null) {
+ mImageFrame.release();
+ mImageFrame = null;
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
new file mode 100644
index 0000000..a1cec01
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Random;
+
+public class BlackWhiteFilter extends Filter {
+
+ @GenerateFieldPort(name = "black", hasDefault = true)
+ private float mBlack = 0f;
+
+ @GenerateFieldPort(name = "white", hasDefault = true)
+ private float mWhite = 1f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame = null;
+ private Random mRandom;
+
+ private final String mBlackWhiteShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float black;\n" +
+ "uniform float scale;\n" +
+ "uniform float stepsize;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float dither = texture2D(tex_sampler_1, v_texcoord).r;\n" +
+ " vec3 xform = clamp((color.rgb - black) * scale, 0.0, 1.0);\n" +
+ " vec3 temp = clamp((color.rgb + stepsize - black) * scale, 0.0, 1.0);\n" +
+ " vec3 new_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ public BlackWhiteFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mBlackWhiteShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ updateParameters();
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void updateParameters() {
+ float scale = (mBlack != mWhite) ? 1.0f / (mWhite - mBlack) : 2000f;
+ float stepsize = 1.0f / 255.0f;
+
+ mProgram.setHostValue("black", mBlack);
+ mProgram.setHostValue("scale", scale);
+ mProgram.setHostValue("stepsize", stepsize);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = mRandom.nextInt(255);
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+ }
+
+ if (mNoiseFrame != null && (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight)) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java
new file mode 100644
index 0000000..29bc8a3
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * The filter linearly blends "left" and "right" frames. The blending weight is
+ * the multiplication of parameter "blend" and the alpha value in "right" frame.
+ * @hide
+ */
+public class BlendFilter extends ImageCombineFilter {
+
+ private final String mBlendShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float blend;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 colorL = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 colorR = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " float weight = colorR.a * blend;\n" +
+ " gl_FragColor = mix(colorL, colorR, weight);\n" +
+ "}\n";
+
+ public BlendFilter(String name) {
+ super(name, new String[] { "left", "right" }, "blended", "blend");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ throw new RuntimeException("TODO: Write native implementation for Blend!");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mBlendShader);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java
new file mode 100644
index 0000000..046e69d
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+
+/**
+ * @hide
+ */
+public class BrightnessFilter extends SimpleImageFilter {
+
+ private static final String mBrightnessShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float brightness;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor = brightness * color;\n" +
+ "}\n";
+
+ public BrightnessFilter(String name) {
+ super(name, "brightness");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ return new NativeProgram("filterpack_imageproc", "brightness");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mBrightnessShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java
new file mode 100644
index 0000000..19da006
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.util.Log;
+
+public class ColorTemperatureFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0.5f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mColorTemperatureShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float scale;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 new_color = color.rgb;\n" +
+ " new_color.r = color.r + color.r * ( 1.0 - color.r) * scale;\n" +
+ " new_color.b = color.b - color.b * ( 1.0 - color.b) * scale;\n" +
+ " if (scale > 0.0) { \n" +
+ " color.g = color.g + color.g * ( 1.0 - color.g) * scale * 0.25;\n" +
+ " }\n" +
+ " float max_value = max(new_color.r, max(new_color.g, new_color.b));\n" +
+ " if (max_value > 1.0) { \n" +
+ " new_color /= max_value;\n" +
+ " } \n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ public ColorTemperatureFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mColorTemperatureShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ updateParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ mProgram.setHostValue("scale", 2.0f * mScale - 1.0f);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java
new file mode 100644
index 0000000..70e987f
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class ContrastFilter extends SimpleImageFilter {
+
+ private static final String mContrastShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float contrast;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " color -= 0.5;\n" +
+ " color *= contrast;\n" +
+ " color += 0.5;\n" +
+ " gl_FragColor = color;\n" + // this will clamp
+ "}\n";
+
+ public ContrastFilter(String name) {
+ super(name, "contrast");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ return new NativeProgram("filterpack_imageproc", "contrast");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mContrastShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java
new file mode 100644
index 0000000..5222d9c
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.format.ObjectFormat;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CropFilter extends Filter {
+
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ @GenerateFieldPort(name = "owidth")
+ private int mOutputWidth = -1;
+
+ @GenerateFieldPort(name = "oheight")
+ private int mOutputHeight = -1;
+
+ @GenerateFieldPort(name = "fillblack")
+ private boolean mFillBlack = false;
+
+ public CropFilter(String name) {
+ super(name);
+ }
+
+ private final String mFragShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const vec2 lo = vec2(0.0, 0.0);\n" +
+ " const vec2 hi = vec2(1.0, 1.0);\n" +
+ " const vec4 black = vec4(0.0, 0.0, 0.0, 1.0);\n" +
+ " bool out_of_bounds =\n" +
+ " any(lessThan(v_texcoord, lo)) ||\n" +
+ " any(greaterThan(v_texcoord, hi));\n" +
+ " if (out_of_bounds) {\n" +
+ " gl_FragColor = black;\n" +
+ " } else {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " }\n" +
+ "}\n";
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ // Make sure output size is set to unspecified, as we do not know what we will be resizing
+ // to.
+ MutableFrameFormat outputFormat = inputFormat.mutableCopy();
+ outputFormat.setDimensions(FrameFormat.SIZE_UNSPECIFIED, FrameFormat.SIZE_UNSPECIFIED);
+ return outputFormat;
+ }
+
+ protected void createProgram(FilterContext context, FrameFormat format) {
+ // TODO: Add CPU version
+ if (mLastFormat != null && mLastFormat.getTarget() == format.getTarget()) return;
+ mLastFormat = format;
+ mProgram = null;
+ switch (format.getTarget()) {
+ case FrameFormat.TARGET_GPU:
+ if(mFillBlack)
+ mProgram = new ShaderProgram(context, mFragShader);
+ else
+ mProgram = ShaderProgram.createIdentity(context);
+
+ break;
+ }
+ if (mProgram == null) {
+ throw new RuntimeException("Could not create a program for crop filter " + this + "!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame imageFrame = pullInput("image");
+ Frame boxFrame = pullInput("box");
+
+ createProgram(env, imageFrame.getFormat());
+
+ // Get the box
+ Quad box = (Quad)boxFrame.getObjectValue();
+
+ // Create output format
+ MutableFrameFormat outputFormat = imageFrame.getFormat().mutableCopy();
+ outputFormat.setDimensions(mOutputWidth == -1 ? outputFormat.getWidth() : mOutputWidth,
+ mOutputHeight == -1 ? outputFormat.getHeight() : mOutputHeight);
+
+ // Create output frame
+ Frame output = env.getFrameManager().newFrame(outputFormat);
+
+ // Set the program parameters
+ if (mProgram instanceof ShaderProgram) {
+ ShaderProgram shaderProgram = (ShaderProgram)mProgram;
+ shaderProgram.setSourceRegion(box);
+ }
+
+ mProgram.process(imageFrame, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java
new file mode 100644
index 0000000..d423d06
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CropRectFilter extends Filter {
+
+ @GenerateFieldPort(name = "xorigin")
+ private int mXorigin;
+
+ @GenerateFieldPort(name = "yorigin")
+ private int mYorigin;
+
+ @GenerateFieldPort(name = "width")
+ private int mOutputWidth;
+
+ @GenerateFieldPort(name = "height")
+ private int mOutputHeight;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ public CropRectFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateSourceRect(mWidth, mHeight);
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ FrameFormat outputFormat = ImageFormat.create(mOutputWidth, mOutputHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateSourceRect(inputFormat.getWidth(), inputFormat.getHeight());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ void updateSourceRect(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ /*
+ Log.e("CropFilter", mWidth + ", " + mHeight + ", " +
+ (float) mXorigin / mWidth + ", " +
+ (float) mYorigin / mHeight + ", " +
+ (float) mOutputWidth / mWidth + ", " +
+ (float) mOutputHeight / mHeight);
+ */
+
+ ((ShaderProgram) mProgram).setSourceRect((float) mXorigin / mWidth,
+ (float) mYorigin / mHeight,
+ (float) mOutputWidth / mWidth,
+ (float) mOutputHeight / mHeight);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java
new file mode 100644
index 0000000..e0514f8
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class CrossProcessFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mCrossProcessShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 ncolor = vec3(0.0, 0.0, 0.0);\n" +
+ " float value;\n" +
+ " if (color.r < 0.5) {\n" +
+ " value = color.r;\n" +
+ " } else {\n" +
+ " value = 1.0 - color.r;\n" +
+ " }\n" +
+ " float red = 4.0 * value * value * value;\n" +
+ " if (color.r < 0.5) {\n" +
+ " ncolor.r = red;\n" +
+ " } else {\n" +
+ " ncolor.r = 1.0 - red;\n" +
+ " }\n" +
+ " if (color.g < 0.5) {\n" +
+ " value = color.g;\n" +
+ " } else {\n" +
+ " value = 1.0 - color.g;\n" +
+ " }\n" +
+ " float green = 2.0 * value * value;\n" +
+ " if (color.g < 0.5) {\n" +
+ " ncolor.g = green;\n" +
+ " } else {\n" +
+ " ncolor.g = 1.0 - green;\n" +
+ " }\n" +
+ " ncolor.b = color.b * 0.5 + 0.25;\n" +
+ " gl_FragColor = vec4(ncolor.rgb, color.a);\n" +
+ "}\n";
+
+ public CrossProcessFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mCrossProcessShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter CrossProcess does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
new file mode 100644
index 0000000..3c7b846
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Random;
+
+public class DocumentaryFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame;
+ private Random mRandom;
+
+ private final String mDocumentaryShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float stepsize;\n" +
+ "uniform float inv_max_dist;\n" +
+ "uniform vec2 center;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ // black white
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float dither = texture2D(tex_sampler_1, v_texcoord).r;\n" +
+ " vec3 xform = clamp(2.0 * color.rgb, 0.0, 1.0);\n" +
+ " vec3 temp = clamp(2.0 * (color.rgb + stepsize), 0.0, 1.0);\n" +
+ " vec3 new_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
+ // grayscale
+ " float gray = dot(new_color, vec3(0.299, 0.587, 0.114));\n" +
+ " new_color = vec3(gray, gray, gray);\n" +
+ // vignette
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float lumen = 0.85 / (1.0 + exp((dist * inv_max_dist - 0.83) * 20.0)) + 0.15;\n" +
+ " gl_FragColor = vec4(new_color * lumen, color.a);\n" +
+ "}\n";
+
+ public DocumentaryFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mDocumentaryShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = mRandom.nextInt(255);
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+
+ initParameters();
+ }
+
+ if (mNoiseFrame != null && (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight)) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ if (mProgram != null) {
+ float centerX = (float) (0.5 * mWidth);
+ float centerY = (float) (0.5 * mHeight);
+ float center[] = {centerX, centerY};
+ float max_dist = (float) Math.sqrt(centerX * centerX + centerY * centerY);
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
+ mProgram.setHostValue("stepsize", 1.0f / 255.0f);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java
new file mode 100644
index 0000000..3f1711e
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.geometry.Quad;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.format.ObjectFormat;
+
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class DrawOverlayFilter extends Filter {
+
+ private ShaderProgram mProgram;
+
+ public DrawOverlayFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ FrameFormat imageFormatMask = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ addMaskedInputPort("source", imageFormatMask);
+ addMaskedInputPort("overlay", imageFormatMask);
+ addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
+ addOutputBasedOnInput("image", "source");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame sourceFrame = pullInput("source");
+ Frame overlayFrame = pullInput("overlay");
+ Frame boxFrame = pullInput("box");
+
+ // Get the box
+ Quad box = (Quad)boxFrame.getObjectValue();
+ box = box.translated(1.0f, 1.0f).scaled(2.0f);
+
+ mProgram.setTargetRegion(box);
+
+ // Create output frame with copy of input
+ Frame output = env.getFrameManager().newFrame(sourceFrame.getFormat());
+ output.setDataFromFrame(sourceFrame);
+
+ // Draw onto output
+ mProgram.process(overlayFrame, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java
new file mode 100644
index 0000000..83c9348
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.geometry.Quad;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.format.ObjectFormat;
+
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class DrawRectFilter extends Filter {
+
+ @GenerateFieldPort(name = "colorRed", hasDefault = true)
+ private float mColorRed = 0.8f;
+
+ @GenerateFieldPort(name = "colorGreen", hasDefault = true)
+ private float mColorGreen = 0.8f;
+
+ @GenerateFieldPort(name = "colorBlue", hasDefault = true)
+ private float mColorBlue = 0.0f;
+
+ private final String mVertexShader =
+ "attribute vec4 aPosition;\n" +
+ "void main() {\n" +
+ " gl_Position = aPosition;\n" +
+ "}\n";
+
+ private final String mFixedColorFragmentShader =
+ "precision mediump float;\n" +
+ "uniform vec4 color;\n" +
+ "void main() {\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+
+ private ShaderProgram mProgram;
+
+
+ public DrawRectFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mProgram = new ShaderProgram(context, mVertexShader, mFixedColorFragmentShader);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame imageFrame = pullInput("image");
+ Frame boxFrame = pullInput("box");
+
+ // Get the box
+ Quad box = (Quad)boxFrame.getObjectValue();
+ box = box.scaled(2.0f).translated(-1.0f, -1.0f);
+
+ // Create output frame with copy of input
+ GLFrame output = (GLFrame)env.getFrameManager().duplicateFrame(imageFrame);
+
+ // Draw onto output
+ output.focus();
+ renderBox(box);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void renderBox(Quad box) {
+ final int FLOAT_SIZE = 4;
+
+ // Get current values
+ float[] color = {mColorRed, mColorGreen, mColorBlue, 1f};
+ float[] vertexValues = { box.p0.x, box.p0.y,
+ box.p1.x, box.p1.y,
+ box.p3.x, box.p3.y,
+ box.p2.x, box.p2.y };
+
+ // Set the program variables
+ mProgram.setHostValue("color", color);
+ mProgram.setAttributeValues("aPosition", vertexValues, 2);
+ mProgram.setVertexCount(4);
+
+ // Draw
+ mProgram.beginDrawing();
+ GLES20.glLineWidth(1.0f);
+ GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, 4);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java
new file mode 100644
index 0000000..d8c88ee
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Color;
+
+public class DuotoneFilter extends Filter {
+
+ @GenerateFieldPort(name = "first_color", hasDefault = true)
+ private int mFirstColor = 0xFFFF0000;
+
+ @GenerateFieldPort(name = "second_color", hasDefault = true)
+ private int mSecondColor = 0xFFFFFF00;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mDuotoneShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec3 first;\n" +
+ "uniform vec3 second;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float energy = (color.r + color.g + color.b) * 0.3333;\n" +
+ " vec3 new_color = (1.0 - energy) * first + energy * second;\n" +
+ " gl_FragColor = vec4(new_color.rgb, color.a);\n" +
+ "}\n";
+
+ public DuotoneFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mDuotoneShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Duotone does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+ updateParameters();
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float first[] = { Color.red(mFirstColor)/255f,
+ Color.green(mFirstColor)/255f,
+ Color.blue(mFirstColor)/255f };
+ float second[] = { Color.red(mSecondColor)/255f,
+ Color.green(mSecondColor)/255f,
+ Color.blue(mSecondColor)/255f };
+
+ mProgram.setHostValue("first", first);
+ mProgram.setHostValue("second", second);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java
new file mode 100644
index 0000000..fc917a1
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class FillLightFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ @GenerateFieldPort(name = "strength", hasDefault = true)
+ private float mBacklight = 0f;
+
+ private Program mProgram;
+
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mFillLightShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float mult;\n" +
+ "uniform float igamma;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main()\n" +
+ "{\n" +
+ " const vec3 color_weights = vec3(0.25, 0.5, 0.25);\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float lightmask = dot(color.rgb, color_weights);\n" +
+ " float backmask = (1.0 - lightmask);\n" +
+ " vec3 ones = vec3(1.0, 1.0, 1.0);\n" +
+ " vec3 diff = pow(mult * color.rgb, igamma * ones) - color.rgb;\n" +
+ " diff = min(diff, 1.0);\n" +
+ " vec3 new_color = min(color.rgb + diff * backmask, 1.0);\n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ public FillLightFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mFillLightShader);
+ Log.e("FillLight", "tile size: " + mTileSize);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter FillLight does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ updateParameters();
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ private void updateParameters() {
+ float fade_gamma = 0.3f;
+ float amt = 1.0f - mBacklight;
+ float mult = 1.0f / (amt * 0.7f + 0.3f);
+ float faded = fade_gamma + (1.0f -fade_gamma) *mult;
+ float igamma = 1.0f / faded;
+
+ mProgram.setHostValue("mult", mult);
+ mProgram.setHostValue("igamma", igamma);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java
new file mode 100644
index 0000000..8d38f98
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+import java.lang.Math;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class FisheyeFilter extends Filter {
+ private static final String TAG = "FisheyeFilter";
+
+ // This parameter has range between 0 and 1. It controls the effect of radial distortion.
+ // The larger the value, the more prominent the distortion effect becomes (a straight line
+ // becomes a curve).
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private static final String mFisheyeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec2 center;\n" +
+ "uniform float alpha;\n" +
+ "uniform float bound;\n" +
+ "uniform float radius2;\n" +
+ "uniform float factor;\n" +
+ "uniform float inv_height;\n" +
+ "uniform float inv_width;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const float m_pi_2 = 1.570963;\n" +
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float radian = m_pi_2 - atan(alpha * sqrt(radius2 - dist * dist), dist);\n" +
+ " float scale = radian * factor / dist;\n" +
+ " vec2 new_coord = gl_FragCoord.xy * scale + (1.0 - scale) * center;\n" +
+ " new_coord.x *= inv_width;\n" +
+ " new_coord.y *= inv_height;\n" +
+ " vec4 color = texture2D(tex_sampler_0, new_coord);\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+
+ public FisheyeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mFisheyeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter FisheyeFilter does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateProgramParams();
+ }
+ }
+
+ private void updateFrameSize(int width, int height) {
+ float center[] = {0.5f * width, 0.5f * height};
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_width", 1.0f / width);
+ mProgram.setHostValue("inv_height", 1.0f / height);
+
+ mWidth = width;
+ mHeight = height;
+
+ updateProgramParams();
+ }
+
+ private void updateProgramParams() {
+ final float pi = 3.14159265f;
+
+ float alpha = mScale * 2.0f + 0.75f;
+ float bound2 = 0.25f * (mWidth * mWidth + mHeight * mHeight);
+ float bound = (float) Math.sqrt(bound2);
+ float radius = 1.15f * bound;
+ float radius2 = radius * radius;
+ float max_radian = 0.5f * pi -
+ (float) Math.atan(alpha / bound * (float) Math.sqrt(radius2 - bound2));
+ float factor = bound / max_radian;
+
+ mProgram.setHostValue("radius2",radius2);
+ mProgram.setHostValue("factor", factor);
+ mProgram.setHostValue("alpha", (float) (mScale * 2.0 + 0.75));
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java
new file mode 100644
index 0000000..3d319ea
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+
+/**
+ * The FixedRotationFilter rotates the input image clockwise, it only accepts
+ * 4 rotation angles: 0, 90, 180, 270
+ * @hide
+ */
+public class FixedRotationFilter extends Filter {
+
+ @GenerateFieldPort(name = "rotation", hasDefault = true)
+ private int mRotation = 0;
+
+ private ShaderProgram mProgram = null;
+
+ public FixedRotationFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ Frame input = pullInput("image");
+ if (mRotation == 0) {
+ pushOutput("image", input);
+ return;
+ }
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+ MutableFrameFormat outputFormat = inputFormat.mutableCopy();
+ int width = inputFormat.getWidth();
+ int height = inputFormat.getHeight();
+ Point p1 = new Point(0.0f, 0.0f);
+ Point p2 = new Point(1.0f, 0.0f);
+ Point p3 = new Point(0.0f, 1.0f);
+ Point p4 = new Point(1.0f, 1.0f);
+ Quad sourceRegion;
+ switch (((int)Math.round(mRotation / 90f)) % 4) {
+ case 1:
+ sourceRegion = new Quad(p3,p1,p4,p2);
+ outputFormat.setDimensions(height, width);
+ break;
+ case 2:
+ sourceRegion = new Quad(p4,p3,p2,p1);
+ break;
+ case 3:
+ sourceRegion = new Quad(p2,p4,p1,p3);
+ outputFormat.setDimensions(height, width);
+ break;
+ case 0:
+ default:
+ sourceRegion = new Quad(p1,p2,p3,p4);
+ break;
+ }
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Set the source region
+ mProgram.setSourceRegion(sourceRegion);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java
new file mode 100644
index 0000000..f8b857b
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+/**
+ * @hide
+ */
+public class FlipFilter extends Filter {
+
+ @GenerateFieldPort(name = "vertical", hasDefault = true)
+ private boolean mVertical = false;
+
+ @GenerateFieldPort(name = "horizontal", hasDefault = true)
+ private boolean mHorizontal = false;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ public FlipFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ updateParameters();
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float x_origin = (mHorizontal) ? 1.0f : 0.0f;
+ float y_origin = (mVertical) ? 1.0f : 0.0f;
+
+ float width = (mHorizontal) ? -1.0f : 1.0f;
+ float height = (mVertical) ? -1.0f : 1.0f;
+
+ ((ShaderProgram) mProgram).setSourceRect(x_origin, y_origin, width, height);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
new file mode 100644
index 0000000..168a9c6
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+
+import java.util.Random;
+
+public class GrainFilter extends Filter {
+
+ private static final int RAND_THRESHOLD = 128;
+
+ @GenerateFieldPort(name = "strength", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame = null;
+ private Random mRandom;
+
+ private final String mGrainShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float scale;\n" +
+ "uniform float stepX;\n" +
+ "uniform float stepY;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " float noise = texture2D(tex_sampler_1, v_texcoord + vec2(-stepX, -stepY)).r * 0.224;\n" +
+ " noise += texture2D(tex_sampler_1, v_texcoord + vec2(-stepX, stepY)).r * 0.224;\n" +
+ " noise += texture2D(tex_sampler_1, v_texcoord + vec2(stepX, -stepY)).r * 0.224;\n" +
+ " noise += texture2D(tex_sampler_1, v_texcoord + vec2(stepX, stepY)).r * 0.224;\n" +
+ " noise += 0.4448;\n" +
+ " noise *= scale;\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float energy = 0.33333 * color.r + 0.33333 * color.g + 0.33333 * color.b;\n" +
+ " float mask = (1.0 - sqrt(energy));\n" +
+ " float weight = 1.0 - 1.333 * mask * noise;\n" +
+ " gl_FragColor = vec4(color.rgb * weight, color.a);\n" +
+ "}\n";
+
+ public GrainFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mGrainShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void updateParameters() {
+ mProgram.setHostValue("scale", mScale);
+ }
+
+ private void updateFrameSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ if (mProgram != null) {
+ mProgram.setHostValue("stepX", 0.5f / mWidth);
+ mProgram.setHostValue("stepY", 0.5f / mHeight);
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ updateParameters();
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = (mRandom.nextInt(256) < RAND_THRESHOLD) ?
+ mRandom.nextInt(256) : 0;
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+ }
+
+ if (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java
new file mode 100644
index 0000000..858489b
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public abstract class ImageCombineFilter extends Filter {
+
+ protected Program mProgram;
+ protected String[] mInputNames;
+ protected String mOutputName;
+ protected String mParameterName;
+ protected int mCurrentTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ public ImageCombineFilter(String name,
+ String[] inputNames,
+ String outputName,
+ String parameterName) {
+ super(name);
+ mInputNames = inputNames;
+ mOutputName = outputName;
+ mParameterName = parameterName;
+ }
+
+ @Override
+ public void setupPorts() {
+ if (mParameterName != null) {
+ try {
+ Field programField = ImageCombineFilter.class.getDeclaredField("mProgram");
+ addProgramPort(mParameterName, mParameterName, programField, float.class, false);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Internal Error: mProgram field not found!");
+ }
+ }
+ for (String inputName : mInputNames) {
+ addMaskedInputPort(inputName, ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+ addOutputBasedOnInput(mOutputName, mInputNames[0]);
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ private void assertAllInputTargetsMatch() {
+ int target = getInputFormat(mInputNames[0]).getTarget();
+ for (String inputName : mInputNames) {
+ if (target != getInputFormat(inputName).getTarget()) {
+ throw new RuntimeException("Type mismatch of input formats in filter " + this
+ + ". All input frames must have the same target!");
+ }
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Pull input frames
+ int i = 0;
+ Frame[] inputs = new Frame[mInputNames.length];
+ for (String inputName : mInputNames) {
+ inputs[i++] = pullInput(inputName);
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputs[0].getFormat());
+
+ // Make sure we have a program
+ updateProgramWithTarget(inputs[0].getFormat().getTarget(), context);
+
+ // Process
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput(mOutputName, output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ protected void updateProgramWithTarget(int target, FilterContext context) {
+ if (target != mCurrentTarget) {
+ switch (target) {
+ case FrameFormat.TARGET_NATIVE:
+ mProgram = getNativeProgram(context);
+ break;
+
+ case FrameFormat.TARGET_GPU:
+ mProgram = getShaderProgram(context);
+ break;
+
+ default:
+ mProgram = null;
+ break;
+ }
+ if (mProgram == null) {
+ throw new RuntimeException("Could not create a program for image filter "
+ + this + "!");
+ }
+ initProgramInputs(mProgram, context);
+ mCurrentTarget = target;
+ }
+ }
+
+ protected abstract Program getNativeProgram(FilterContext context);
+
+ protected abstract Program getShaderProgram(FilterContext context);
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java
new file mode 100644
index 0000000..a5405cb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+
+import android.util.Log;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * @hide
+ */
+public class ImageEncoder extends Filter {
+
+ @GenerateFieldPort(name = "stream")
+ private OutputStream mOutputStream;
+
+ @GenerateFieldPort(name = "quality", hasDefault = true)
+ private int mQuality = 80;
+
+ public ImageEncoder(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_UNSPECIFIED));
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame input = pullInput("image");
+ Bitmap bitmap = input.getBitmap();
+ bitmap.compress(CompressFormat.JPEG, mQuality, mOutputStream);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java
new file mode 100644
index 0000000..b996eb8
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class ImageSlicer extends Filter {
+
+ @GenerateFieldPort(name = "xSlices")
+ private int mXSlices;
+
+ @GenerateFieldPort(name = "ySlices")
+ private int mYSlices;
+
+ @GenerateFieldPort(name = "padSize")
+ private int mPadSize;
+
+ // The current slice index from 0 to xSlices * ySlices
+ private int mSliceIndex;
+
+ private Frame mOriginalFrame;
+
+ private Program mProgram;
+
+ private int mInputWidth;
+ private int mInputHeight;
+
+ private int mSliceWidth;
+ private int mSliceHeight;
+
+ private int mOutputWidth;
+ private int mOutputHeight;
+
+ public ImageSlicer(String name) {
+ super(name);
+ mSliceIndex = 0;
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ private void calcOutputFormatForInput(Frame frame) {
+
+ // calculate the output size based on input size, xSlices, and ySlices
+ mInputWidth = frame.getFormat().getWidth();
+ mInputHeight = frame.getFormat().getHeight();
+
+ mSliceWidth = (mInputWidth + mXSlices - 1) / mXSlices;
+ mSliceHeight = (mInputHeight + mYSlices - 1)/ mYSlices;
+
+ mOutputWidth = mSliceWidth + mPadSize * 2;
+ mOutputHeight = mSliceHeight + mPadSize * 2;
+ }
+
+
+ @Override
+ public void process(FilterContext context) {
+
+ // Get input frame
+ if (mSliceIndex == 0) {
+ mOriginalFrame = pullInput("image");
+ calcOutputFormatForInput(mOriginalFrame);
+ }
+
+ FrameFormat inputFormat = mOriginalFrame.getFormat();
+ MutableFrameFormat outputFormat = inputFormat.mutableCopy();
+ outputFormat.setDimensions(mOutputWidth, mOutputHeight);
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Create the program if not created already
+ if (mProgram == null) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+
+ // Calculate the four corner of the source region
+ int xSliceIndex = mSliceIndex % mXSlices;
+ int ySliceIndex = mSliceIndex / mXSlices;
+
+ // TODO(rslin) : not sure shifting by 0.5 is needed.
+ float x0 = (xSliceIndex * mSliceWidth - mPadSize) / ((float) mInputWidth);
+ float y0 = (ySliceIndex * mSliceHeight - mPadSize) / ((float) mInputHeight);
+
+ ((ShaderProgram) mProgram).setSourceRect(x0, y0,
+ ((float) mOutputWidth) / mInputWidth,
+ ((float) mOutputHeight) / mInputHeight);
+
+ // Process
+ mProgram.process(mOriginalFrame, output);
+ mSliceIndex++;
+
+ if (mSliceIndex == mXSlices * mYSlices) {
+ mSliceIndex = 0;
+ mOriginalFrame.release();
+ setWaitsOnInputPort("image", true);
+ } else {
+ // Retain the original frame so it can be used next time.
+ mOriginalFrame.retain();
+ setWaitsOnInputPort("image", false);
+ }
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java
new file mode 100644
index 0000000..20aba91
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class ImageStitcher extends Filter {
+
+ @GenerateFieldPort(name = "xSlices")
+ private int mXSlices;
+
+ @GenerateFieldPort(name = "ySlices")
+ private int mYSlices;
+
+ @GenerateFieldPort(name = "padSize")
+ private int mPadSize;
+
+ private Program mProgram;
+ private Frame mOutputFrame;
+
+ private int mInputWidth;
+ private int mInputHeight;
+
+ private int mImageWidth;
+ private int mImageHeight;
+
+ private int mSliceWidth;
+ private int mSliceHeight;
+
+ private int mSliceIndex;
+
+ public ImageStitcher(String name) {
+ super(name);
+ mSliceIndex = 0;
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ private FrameFormat calcOutputFormatForInput(FrameFormat format) {
+ MutableFrameFormat outputFormat = format.mutableCopy();
+
+ mInputWidth = format.getWidth();
+ mInputHeight = format.getHeight();
+
+ mSliceWidth = mInputWidth - 2 * mPadSize;
+ mSliceHeight = mInputHeight - 2 * mPadSize;
+
+ mImageWidth = mSliceWidth * mXSlices;
+ mImageHeight = mSliceHeight * mYSlices;
+
+ outputFormat.setDimensions(mImageWidth, mImageHeight);
+ return outputFormat;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat format = input.getFormat();
+
+ // Create output frame
+ if (mSliceIndex == 0) {
+ mOutputFrame = context.getFrameManager().newFrame(calcOutputFormatForInput(format));
+ } else {
+ if ((format.getWidth() != mInputWidth) ||
+ (format.getHeight() != mInputHeight)) {
+ // CHECK input format here
+ throw new RuntimeException("Image size should not change.");
+ }
+ }
+
+ // Create the program if not created already
+ if (mProgram == null) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+
+ // TODO(rslin) : not sure shifting by 0.5 is needed.
+ float x0 = ((float) mPadSize) / mInputWidth;
+ float y0 = ((float) mPadSize) / mInputHeight;
+
+ int outputOffsetX = (mSliceIndex % mXSlices) * mSliceWidth;
+ int outputOffsetY = (mSliceIndex / mXSlices) * mSliceHeight;
+
+ float outputWidth = (float) Math.min(mSliceWidth, mImageWidth - outputOffsetX);
+ float outputHeight = (float) Math.min(mSliceHeight, mImageHeight - outputOffsetY);
+
+ // We need to set the source rect as well because the input are padded images.
+ ((ShaderProgram) mProgram).setSourceRect(x0, y0,
+ outputWidth / mInputWidth,
+ outputHeight / mInputHeight);
+
+ ((ShaderProgram) mProgram).setTargetRect(((float) outputOffsetX)/ mImageWidth,
+ ((float) outputOffsetY) / mImageHeight,
+ outputWidth / mImageWidth,
+ outputHeight / mImageHeight);
+
+ // Process this tile
+ mProgram.process(input, mOutputFrame);
+ mSliceIndex++;
+
+ // Push output
+ if (mSliceIndex == mXSlices * mYSlices) {
+ pushOutput("image", mOutputFrame);
+ mOutputFrame.release();
+ mSliceIndex = 0;
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java
new file mode 100644
index 0000000..400fd5d
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+
+/**
+ * @hide
+ */
+public class Invert extends SimpleImageFilter {
+
+ private static final String mInvertShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor.r = 1.0 - color.r;\n" +
+ " gl_FragColor.g = 1.0 - color.g;\n" +
+ " gl_FragColor.b = 1.0 - color.b;\n" +
+ " gl_FragColor.a = color.a;\n" +
+ "}\n";
+
+ public Invert(String name) {
+ super(name, null);
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ return new NativeProgram("filterpack_imageproc", "invert");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mInvertShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
new file mode 100644
index 0000000..452a833
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Random;
+
+public class LomoishFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame;
+ private Random mRandom;
+
+ private final String mLomoishShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float stepsizeX;\n" +
+ "uniform float stepsizeY;\n" +
+ "uniform float stepsize;\n" +
+ "uniform vec2 center;\n" +
+ "uniform float inv_max_dist;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ // sharpen
+ " vec3 nbr_color = vec3(0.0, 0.0, 0.0);\n" +
+ " vec2 coord;\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " coord.x = v_texcoord.x - 0.5 * stepsizeX;\n" +
+ " coord.y = v_texcoord.y - stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x - stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y - 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " vec3 s_color = vec3(color.rgb + 0.3 * nbr_color);\n" +
+ // cross process
+ " vec3 c_color = vec3(0.0, 0.0, 0.0);\n" +
+ " float value;\n" +
+ " if (s_color.r < 0.5) {\n" +
+ " value = s_color.r;\n" +
+ " } else {\n" +
+ " value = 1.0 - s_color.r;\n" +
+ " }\n" +
+ " float red = 4.0 * value * value * value;\n" +
+ " if (s_color.r < 0.5) {\n" +
+ " c_color.r = red;\n" +
+ " } else {\n" +
+ " c_color.r = 1.0 - red;\n" +
+ " }\n" +
+ " if (s_color.g < 0.5) {\n" +
+ " value = s_color.g;\n" +
+ " } else {\n" +
+ " value = 1.0 - s_color.g;\n" +
+ " }\n" +
+ " float green = 2.0 * value * value;\n" +
+ " if (s_color.g < 0.5) {\n" +
+ " c_color.g = green;\n" +
+ " } else {\n" +
+ " c_color.g = 1.0 - green;\n" +
+ " }\n" +
+ " c_color.b = s_color.b * 0.5 + 0.25;\n" +
+ // blackwhite
+ " float dither = texture2D(tex_sampler_1, v_texcoord).r;\n" +
+ " vec3 xform = clamp((c_color.rgb - 0.15) * 1.53846, 0.0, 1.0);\n" +
+ " vec3 temp = clamp((color.rgb + stepsize - 0.15) * 1.53846, 0.0, 1.0);\n" +
+ " vec3 bw_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
+ // vignette
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float lumen = 0.85 / (1.0 + exp((dist * inv_max_dist - 0.73) * 20.0)) + 0.15;\n" +
+ " gl_FragColor = vec4(bw_color * lumen, color.a);\n" +
+ "}\n";
+
+ public LomoishFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mLomoishShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void initParameters() {
+ if (mProgram !=null) {
+ float centerX = (float) (0.5 * mWidth);
+ float centerY = (float) (0.5 * mHeight);
+ float center[] = {centerX, centerY};
+ float max_dist = (float) Math.sqrt(centerX * centerX + centerY * centerY);
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
+
+ mProgram.setHostValue("stepsize", 1.0f / 255.0f);
+ mProgram.setHostValue("stepsizeX", 1.0f / mWidth);
+ mProgram.setHostValue("stepsizeY", 1.0f / mHeight);
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = mRandom.nextInt(255);
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+
+ initParameters();
+ }
+
+ if (mNoiseFrame != null && (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight)) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java
new file mode 100644
index 0000000..440d6a6
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+
+public class NegativeFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mNegativeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor = vec4(1.0 - color.rgb, color.a);\n" +
+ "}\n";
+
+ public NegativeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mNegativeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java
new file mode 100644
index 0000000..bc2e553
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class PosterizeFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mPosterizeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 pcolor;\n" +
+ " pcolor.r = (color.r >= 0.5) ? 0.75 : 0.25;\n" +
+ " pcolor.g = (color.g >= 0.5) ? 0.75 : 0.25;\n" +
+ " pcolor.b = (color.b >= 0.5) ? 0.75 : 0.25;\n" +
+ " gl_FragColor = vec4(pcolor, color.a);\n" +
+ "}\n";
+
+ public PosterizeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mPosterizeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
new file mode 100644
index 0000000..5632a5e
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RedEyeFilter extends Filter {
+
+ private static final float RADIUS_RATIO = 0.06f;
+ private static final float MIN_RADIUS = 10.0f;
+ private static final float DEFAULT_RED_INTENSITY = 1.30f;
+
+ @GenerateFieldPort(name = "centers")
+ private float[] mCenters;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Frame mRedEyeFrame;
+ private Bitmap mRedEyeBitmap;
+
+ private final Canvas mCanvas = new Canvas();
+ private final Paint mPaint = new Paint();
+
+ private float mRadius;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mRedEyeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float intensity;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " gl_FragColor = vec4(mask.a, mask.a, mask.a, 1.0) * intensity + color * (1.0 - intensity);\n" +
+ " if (mask.a > 0.0) {\n" +
+ " gl_FragColor.r = 0.0;\n" +
+ " float green_blue = color.g + color.b;\n" +
+ " float red_intensity = color.r / green_blue;\n" +
+ " if (red_intensity > intensity) {\n" +
+ " color.r = 0.5 * green_blue;\n" +
+ " }\n" +
+ " }\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+
+ public RedEyeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mRedEyeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter RedEye does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mRedEyeBitmap != null) {
+ mRedEyeBitmap.recycle();
+ mRedEyeBitmap = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ createRedEyeBitmap();
+ }
+
+ createRedEyeFrame(context);
+
+ // Process
+ Frame[] inputs = {input, mRedEyeFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+
+ // Release unused frame
+ mRedEyeFrame.release();
+ mRedEyeFrame = null;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateProgramParams();
+ }
+ }
+
+ private void createRedEyeBitmap() {
+ if (mRedEyeBitmap != null) {
+ mRedEyeBitmap.recycle();
+ }
+
+ int bitmapWidth = mWidth / 2;
+ int bitmapHeight = mHeight / 2;
+
+ mRedEyeBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+ mCanvas.setBitmap(mRedEyeBitmap);
+ mPaint.setColor(Color.WHITE);
+ mRadius = Math.max(MIN_RADIUS, RADIUS_RATIO * Math.min(bitmapWidth, bitmapHeight));
+
+ updateProgramParams();
+ }
+
+ private void createRedEyeFrame(FilterContext context) {
+ FrameFormat format = ImageFormat.create(mRedEyeBitmap.getWidth() ,
+ mRedEyeBitmap.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mRedEyeFrame = context.getFrameManager().newFrame(format);
+ mRedEyeFrame.setBitmap(mRedEyeBitmap);
+ }
+
+ private void updateProgramParams() {
+ mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
+
+ if ( mCenters.length % 2 == 1) {
+ throw new RuntimeException("The size of center array must be even.");
+ }
+
+ if (mRedEyeBitmap != null) {
+ for (int i = 0; i < mCenters.length; i += 2) {
+ mCanvas.drawCircle(mCenters[i] * mRedEyeBitmap.getWidth(),
+ mCenters[i + 1] * mRedEyeBitmap.getHeight(),
+ mRadius, mPaint);
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java
new file mode 100644
index 0000000..411e061
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class ResizeFilter extends Filter {
+
+ @GenerateFieldPort(name = "owidth")
+ private int mOWidth;
+ @GenerateFieldPort(name = "oheight")
+ private int mOHeight;
+ @GenerateFieldPort(name = "keepAspectRatio", hasDefault = true)
+ private boolean mKeepAspectRatio = false;
+ @GenerateFieldPort(name = "generateMipMap", hasDefault = true)
+ private boolean mGenerateMipMap = false;
+
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ private MutableFrameFormat mOutputFormat;
+ private int mInputChannels;
+
+ public ResizeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ protected void createProgram(FilterContext context, FrameFormat format) {
+ if (mLastFormat != null && mLastFormat.getTarget() == format.getTarget()) return;
+ mLastFormat = format;
+ switch (format.getTarget()) {
+ case FrameFormat.TARGET_NATIVE:
+ throw new RuntimeException("Native ResizeFilter not implemented yet!");
+
+
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram prog = ShaderProgram.createIdentity(context);
+ mProgram = prog;
+ break;
+
+ default:
+ throw new RuntimeException("ResizeFilter could not create suitable program!");
+ }
+ }
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame input = pullInput("image");
+ createProgram(env, input.getFormat());
+
+ // Create output frame
+ MutableFrameFormat outputFormat = input.getFormat().mutableCopy();
+ if (mKeepAspectRatio) {
+ FrameFormat inputFormat = input.getFormat();
+ mOHeight = mOWidth * inputFormat.getHeight() / inputFormat.getWidth();
+ }
+ outputFormat.setDimensions(mOWidth, mOHeight);
+ Frame output = env.getFrameManager().newFrame(outputFormat);
+
+ // Process
+ if (mGenerateMipMap) {
+ GLFrame mipmapped = (GLFrame)env.getFrameManager().newFrame(input.getFormat());
+ mipmapped.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ mipmapped.setDataFromFrame(input);
+ mipmapped.generateMipMap();
+ mProgram.process(mipmapped, output);
+ mipmapped.release();
+ } else {
+ mProgram.process(input, output);
+ }
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java
new file mode 100644
index 0000000..3da7939
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RotateFilter extends Filter {
+
+ @GenerateFieldPort(name = "angle")
+ private int mAngle;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private int mOutputWidth;
+ private int mOutputHeight;
+
+ public RotateFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ shaderProgram.setClearsOutput(true);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ mOutputWidth = mWidth;
+ mOutputHeight = mHeight;
+
+ updateParameters();
+ }
+
+ // Create output frame
+ FrameFormat outputFormat = ImageFormat.create(mOutputWidth, mOutputHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float sinTheta;
+ float cosTheta;
+
+ if (mAngle % 90 == 0) {
+ if (mAngle % 180 == 0) {
+ sinTheta = 0f;
+ cosTheta = (mAngle % 360 == 0) ? 1f:-1f;
+ } else {
+ cosTheta = 0f;
+ sinTheta = ((mAngle + 90) % 360 == 0) ? -1f:1f;
+
+ mOutputWidth = mHeight;
+ mOutputHeight = mWidth;
+ }
+ } else {
+ throw new RuntimeException("degree has to be multiply of 90.");
+ }
+
+ Point x0 = new Point(0.5f * (-cosTheta + sinTheta + 1f),
+ 0.5f * (-sinTheta - cosTheta + 1f));
+ Point x1 = new Point(0.5f * (cosTheta + sinTheta + 1f),
+ 0.5f * (sinTheta - cosTheta + 1f));
+ Point x2 = new Point(0.5f * (-cosTheta - sinTheta + 1f),
+ 0.5f * (-sinTheta + cosTheta + 1f));
+ Point x3 = new Point(0.5f * (cosTheta - sinTheta + 1f),
+ 0.5f * (sinTheta + cosTheta + 1f));
+ Quad quad = new Quad(x0, x1, x2, x3);
+ ((ShaderProgram) mProgram).setTargetRegion(quad);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java
new file mode 100644
index 0000000..b83af39
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class SaturateFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mBenProgram;
+ private Program mHerfProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mBenSaturateShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float scale;\n" +
+ "uniform float shift;\n" +
+ "uniform vec3 weights;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float kv = dot(color.rgb, weights) + shift;\n" +
+ " vec3 new_color = scale * color.rgb + (1.0 - scale) * kv;\n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ private final String mHerfSaturateShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec3 weights;\n" +
+ "uniform vec3 exponents;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float de = dot(color.rgb, weights);\n" +
+ " float inv_de = 1.0 / de;\n" +
+ " vec3 new_color = de * pow(color.rgb * inv_de, exponents);\n" +
+ " float max_color = max(max(max(new_color.r, new_color.g), new_color.b), 1.0);\n" +
+ " gl_FragColor = vec4(new_color / max_color, color.a);\n" +
+ "}\n";
+
+
+ public SaturateFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mBenSaturateShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mBenProgram = shaderProgram;
+
+ shaderProgram = new ShaderProgram(context, mHerfSaturateShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mHerfProgram = shaderProgram;
+
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mBenProgram != null && mHerfProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mBenProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ if (mScale > 0.0f) {
+ mHerfProgram.process(input, output);
+ } else {
+ mBenProgram.process(input, output);
+ }
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ float shift = 1.0f / 255.0f;
+ float weights[] = { 2f/8f, 5f/8f, 1f/8f};
+
+ mBenProgram.setHostValue("weights", weights);
+ mBenProgram.setHostValue("shift", shift);
+
+ mHerfProgram.setHostValue("weights", weights);
+
+ updateParameters();
+ }
+
+ private void updateParameters() {
+
+ if (mScale > 0.0f) {
+ float exponents[] = new float[3];
+
+ exponents[0] = (0.9f * mScale) + 1.0f;
+ exponents[1] = (2.1f * mScale) + 1.0f;
+ exponents[2] = (2.7f * mScale) + 1.0f;
+
+ mHerfProgram.setHostValue("exponents", exponents);
+ } else {
+ mBenProgram.setHostValue("scale", 1.0f + mScale);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java
new file mode 100644
index 0000000..7a83fdf
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class SepiaFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mSepiaShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform mat3 matrix;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 new_color = min(matrix * color.rgb, 1.0);\n" +
+ " gl_FragColor = vec4(new_color.rgb, color.a);\n" +
+ "}\n";
+
+ public SepiaFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mSepiaShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ float weights[] = { 805.0f / 2048.0f, 715.0f / 2048.0f, 557.0f / 2048.0f,
+ 1575.0f / 2048.0f, 1405.0f / 2048.0f, 1097.0f / 2048.0f,
+ 387.0f / 2048.0f, 344.0f / 2048.0f, 268.0f / 2048.0f };
+ mProgram.setHostValue("matrix", weights);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java
new file mode 100644
index 0000000..256b769
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+public class SharpenFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mSharpenShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float scale;\n" +
+ "uniform float stepsizeX;\n" +
+ "uniform float stepsizeY;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec3 nbr_color = vec3(0.0, 0.0, 0.0);\n" +
+ " vec2 coord;\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " coord.x = v_texcoord.x - 0.5 * stepsizeX;\n" +
+ " coord.y = v_texcoord.y - stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x - stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y - 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " gl_FragColor = vec4(color.rgb - 2.0 * scale * nbr_color, color.a);\n" +
+ "}\n";
+
+ public SharpenFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mSharpenShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateFrameSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ if (mProgram != null) {
+ mProgram.setHostValue("stepsizeX", 1.0f / mWidth);
+ mProgram.setHostValue("stepsizeY", 1.0f / mHeight);
+ updateParameters();
+ }
+ }
+
+ private void updateParameters() {
+ mProgram.setHostValue("scale", mScale);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java
new file mode 100644
index 0000000..f4fc271
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public abstract class SimpleImageFilter extends Filter {
+
+ protected int mCurrentTarget = FrameFormat.TARGET_UNSPECIFIED;
+ protected Program mProgram;
+ protected String mParameterName;
+
+ public SimpleImageFilter(String name, String parameterName) {
+ super(name);
+ mParameterName = parameterName;
+ }
+
+ @Override
+ public void setupPorts() {
+ if (mParameterName != null) {
+ try {
+ Field programField = SimpleImageFilter.class.getDeclaredField("mProgram");
+ addProgramPort(mParameterName, mParameterName, programField, float.class, false);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Internal Error: mProgram field not found!");
+ }
+ }
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ updateProgramWithTarget(inputFormat.getTarget(), context);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ protected void updateProgramWithTarget(int target, FilterContext context) {
+ if (target != mCurrentTarget) {
+ switch (target) {
+ case FrameFormat.TARGET_NATIVE:
+ mProgram = getNativeProgram(context);
+ break;
+
+ case FrameFormat.TARGET_GPU:
+ mProgram = getShaderProgram(context);
+ break;
+
+ default:
+ mProgram = null;
+ break;
+ }
+ if (mProgram == null) {
+ throw new RuntimeException("Could not create a program for image filter " + this + "!");
+ }
+ initProgramInputs(mProgram, context);
+ mCurrentTarget = target;
+ }
+ }
+
+ protected abstract Program getNativeProgram(FilterContext context);
+
+ protected abstract Program getShaderProgram(FilterContext context);
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java
new file mode 100644
index 0000000..c9f097d
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class StraightenFilter extends Filter {
+
+ @GenerateFieldPort(name = "angle", hasDefault = true)
+ private float mAngle = 0f;
+
+ @GenerateFieldPort(name = "maxAngle", hasDefault = true)
+ private float mMaxAngle = 45f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private static final float DEGREE_TO_RADIAN = (float) Math.PI / 180.0f;
+
+ public StraightenFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Create output frame
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ updateParameters();
+ }
+
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float cosTheta = (float) Math.cos(mAngle * DEGREE_TO_RADIAN);
+ float sinTheta = (float) Math.sin(mAngle * DEGREE_TO_RADIAN);
+
+ if (mMaxAngle <= 0)
+ throw new RuntimeException("Max angle is out of range (0-180).");
+ mMaxAngle = (mMaxAngle > 90) ? 90 : mMaxAngle;
+
+ Point p0 = new Point(-cosTheta * mWidth + sinTheta * mHeight,
+ -sinTheta * mWidth - cosTheta * mHeight);
+
+ Point p1 = new Point(cosTheta * mWidth + sinTheta * mHeight,
+ sinTheta * mWidth - cosTheta * mHeight);
+
+ Point p2 = new Point(-cosTheta * mWidth - sinTheta * mHeight,
+ -sinTheta * mWidth + cosTheta * mHeight);
+
+ Point p3 = new Point(cosTheta * mWidth - sinTheta * mHeight,
+ sinTheta * mWidth + cosTheta * mHeight);
+
+ float maxWidth = (float) Math.max(Math.abs(p0.x), Math.abs(p1.x));
+ float maxHeight = (float) Math.max(Math.abs(p0.y), Math.abs(p1.y));
+
+ float scale = 0.5f * Math.min( mWidth / maxWidth,
+ mHeight / maxHeight);
+
+ p0.set(scale * p0.x / mWidth + 0.5f, scale * p0.y / mHeight + 0.5f);
+ p1.set(scale * p1.x / mWidth + 0.5f, scale * p1.y / mHeight + 0.5f);
+ p2.set(scale * p2.x / mWidth + 0.5f, scale * p2.y / mHeight + 0.5f);
+ p3.set(scale * p3.x / mWidth + 0.5f, scale * p3.y / mHeight + 0.5f);
+
+ Quad quad = new Quad(p0, p1, p2, p3);
+ ((ShaderProgram) mProgram).setSourceRegion(quad);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java
new file mode 100644
index 0000000..0da54a5
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Color;
+
+public class TintFilter extends Filter {
+
+ @GenerateFieldPort(name = "tint", hasDefault = true)
+ private int mTint = 0xFF0000FF;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mTintShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec3 tint;\n" +
+ "uniform vec3 color_ratio;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float avg_color = dot(color_ratio, color.rgb);\n" +
+ " vec3 new_color = min(0.8 * avg_color + 0.2 * tint, 1.0);\n" +
+ " gl_FragColor = vec4(new_color.rgb, color.a);\n" +
+ "}\n";
+
+ public TintFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mTintShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ float color_ratio[] = {0.21f, 0.71f, 0.07f};
+ mProgram.setHostValue("color_ratio", color_ratio);
+
+ updateParameters();
+ }
+
+ private void updateParameters() {
+ float tint_color[] = {Color.red(mTint) / 255f,
+ Color.green(mTint) / 255f,
+ Color.blue(mTint) / 255f };
+
+ mProgram.setHostValue("tint", tint_color);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java
new file mode 100644
index 0000000..00e7bf4
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ */
+public class ToGrayFilter extends SimpleImageFilter {
+
+ @GenerateFieldPort(name = "invertSource", hasDefault = true)
+ private boolean mInvertSource = false;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private MutableFrameFormat mOutputFormat;
+
+ private static final String mColorToGray4Shader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float y = dot(color, vec4(0.299, 0.587, 0.114, 0));\n" +
+ " gl_FragColor = vec4(y, y, y, color.a);\n" +
+ "}\n";
+
+ public ToGrayFilter(String name) {
+ super(name, null);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ throw new RuntimeException("Native toGray not implemented yet!");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ int inputChannels = getInputFormat("image").getBytesPerSample();
+ if (inputChannels != 4) {
+ throw new RuntimeException("Unsupported GL input channels: " +
+ inputChannels + "! Channels must be 4!");
+ }
+ ShaderProgram program = new ShaderProgram(context, mColorToGray4Shader);
+ program.setMaximumTileSize(mTileSize);
+ if (mInvertSource)
+ program.setSourceRect(0.0f, 1.0f, 1.0f, -1.0f);
+ return program;
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java
new file mode 100644
index 0000000..bc4a65e
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+import java.lang.Math;
+/**
+ * @hide
+ */
+public class ToPackedGrayFilter extends Filter {
+
+ @GenerateFieldPort(name = "owidth", hasDefault = true)
+ private int mOWidth = FrameFormat.SIZE_UNSPECIFIED;
+ @GenerateFieldPort(name = "oheight", hasDefault = true)
+ private int mOHeight = FrameFormat.SIZE_UNSPECIFIED;
+ @GenerateFieldPort(name = "keepAspectRatio", hasDefault = true)
+ private boolean mKeepAspectRatio = false;
+
+ private Program mProgram;
+
+ private final String mColorToPackedGrayShader =
+ "precision mediump float;\n" +
+ "const vec4 coeff_y = vec4(0.299, 0.587, 0.114, 0);\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float pix_stride;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " for (int i = 0; i < 4; ++i) {\n" +
+ " vec4 p = texture2D(tex_sampler_0,\n" +
+ " v_texcoord + vec2(pix_stride * float(i), 0.0));\n" +
+ " gl_FragColor[i] = dot(p, coeff_y);\n" +
+ " }\n" +
+ "}\n";
+
+ public ToPackedGrayFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return convertInputFormat(inputFormat);
+ }
+
+ private void checkOutputDimensions(int outputWidth, int outputHeight) {
+ if (outputWidth <= 0 || outputHeight <= 0) {
+ throw new RuntimeException("Invalid output dimensions: " +
+ outputWidth + " " + outputHeight);
+ }
+ }
+
+ private FrameFormat convertInputFormat(FrameFormat inputFormat) {
+ int ow = mOWidth;
+ int oh = mOHeight;
+ int w = inputFormat.getWidth();
+ int h = inputFormat.getHeight();
+ if (mOWidth == FrameFormat.SIZE_UNSPECIFIED) {
+ ow = w;
+ }
+ if (mOHeight == FrameFormat.SIZE_UNSPECIFIED) {
+ oh = h;
+ }
+ if (mKeepAspectRatio) {
+ // if keep aspect ratio, use the bigger dimension to determine the
+ // final output size
+ if (w > h) {
+ ow = Math.max(ow, oh);
+ oh = ow * h / w;
+ } else {
+ oh = Math.max(ow, oh);
+ ow = oh * w / h;
+ }
+ }
+ ow = (ow > 0 && ow < 4) ? 4 : (ow / 4) * 4; // ensure width is multiple of 4
+ return ImageFormat.create(ow, oh,
+ ImageFormat.COLORSPACE_GRAY,
+ FrameFormat.TARGET_NATIVE);
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mProgram = new ShaderProgram(context, mColorToPackedGrayShader);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+ FrameFormat outputFormat = convertInputFormat(inputFormat);
+ int ow = outputFormat.getWidth();
+ int oh = outputFormat.getHeight();
+ checkOutputDimensions(ow, oh);
+ mProgram.setHostValue("pix_stride", 1.0f / ow);
+
+ // Do the RGBA to luminance conversion.
+ MutableFrameFormat tempFrameFormat = inputFormat.mutableCopy();
+ tempFrameFormat.setDimensions(ow / 4, oh);
+ Frame temp = context.getFrameManager().newFrame(tempFrameFormat);
+ mProgram.process(input, temp);
+
+ // Read frame from GPU to CPU.
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+ output.setDataFromFrame(temp);
+ temp.release();
+
+ // Push output and yield ownership.
+ pushOutput("image", output);
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java
new file mode 100644
index 0000000..ab4814f
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class ToRGBAFilter extends Filter {
+
+ private int mInputBPP;
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ public ToRGBAFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ MutableFrameFormat mask = new MutableFrameFormat(FrameFormat.TYPE_BYTE,
+ FrameFormat.TARGET_NATIVE);
+ mask.setDimensionCount(2);
+ addMaskedInputPort("image", mask);
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return getConvertedFormat(inputFormat);
+ }
+
+ public FrameFormat getConvertedFormat(FrameFormat format) {
+ MutableFrameFormat result = format.mutableCopy();
+ result.setMetaValue(ImageFormat.COLORSPACE_KEY, ImageFormat.COLORSPACE_RGBA);
+ result.setBytesPerSample(4);
+ return result;
+ }
+
+ public void createProgram(FilterContext context, FrameFormat format) {
+ mInputBPP = format.getBytesPerSample();
+ if (mLastFormat != null && mLastFormat.getBytesPerSample() == mInputBPP) return;
+ mLastFormat = format;
+ switch (mInputBPP) {
+ case 1:
+ mProgram = new NativeProgram("filterpack_imageproc", "gray_to_rgba");
+ break;
+ case 3:
+ mProgram = new NativeProgram("filterpack_imageproc", "rgb_to_rgba");
+ break;
+ default:
+ throw new RuntimeException("Unsupported BytesPerPixel: " + mInputBPP + "!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ createProgram(context, input.getFormat());
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(getConvertedFormat(input.getFormat()));
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java
new file mode 100644
index 0000000..9258502
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class ToRGBFilter extends Filter {
+
+ private int mInputBPP;
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ public ToRGBFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ MutableFrameFormat mask = new MutableFrameFormat(FrameFormat.TYPE_BYTE,
+ FrameFormat.TARGET_NATIVE);
+ mask.setDimensionCount(2);
+ addMaskedInputPort("image", mask);
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return getConvertedFormat(inputFormat);
+ }
+
+ public FrameFormat getConvertedFormat(FrameFormat format) {
+ MutableFrameFormat result = format.mutableCopy();
+ result.setMetaValue(ImageFormat.COLORSPACE_KEY, ImageFormat.COLORSPACE_RGB);
+ result.setBytesPerSample(3);
+ return result;
+ }
+
+ public void createProgram(FilterContext context, FrameFormat format) {
+ mInputBPP = format.getBytesPerSample();
+ if (mLastFormat != null && mLastFormat.getBytesPerSample() == mInputBPP) return;
+ mLastFormat = format;
+ switch (mInputBPP) {
+ case 1:
+ mProgram = new NativeProgram("filterpack_imageproc", "gray_to_rgb");
+ break;
+ case 4:
+ mProgram = new NativeProgram("filterpack_imageproc", "rgba_to_rgb");
+ break;
+ default:
+ throw new RuntimeException("Unsupported BytesPerPixel: " + mInputBPP + "!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ createProgram(context, input.getFormat());
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(getConvertedFormat(input.getFormat()));
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java
new file mode 100644
index 0000000..2d78fff
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class VignetteFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final float mSlope = 20.0f;
+ private final float mShade = 0.85f;
+
+ private final String mVignetteShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float range;\n" +
+ "uniform float inv_max_dist;\n" +
+ "uniform float shade;\n" +
+ "uniform vec2 center;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const float slope = 20.0;\n" +
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float lumen = shade / (1.0 + exp((dist * inv_max_dist - range) * slope)) + (1.0 - shade);\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor = vec4(color.rgb * lumen, color.a);\n" +
+ "}\n";
+
+ public VignetteFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mVignetteShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void initParameters() {
+ if (mProgram != null) {
+ float centerX = (float) (0.5 * mWidth);
+ float centerY = (float) (0.5 * mHeight);
+ float center[] = {centerX, centerY};
+ float max_dist = (float) Math.sqrt(centerX * centerX + centerY * centerY);
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
+ mProgram.setHostValue("shade", mShade);
+
+ updateParameters();
+ }
+ }
+
+ private void updateParameters() {
+ // The 'range' is between 1.3 to 0.6. When scale is zero then range is 1.3
+ // which means no vignette at all because the luminousity difference is
+ // less than 1/256 and will cause nothing.
+ mProgram.setHostValue("range", 1.30f - (float) Math.sqrt(mScale) * 0.7f);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ initParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java
new file mode 100644
index 0000000..1cf48b0
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.imageproc;
diff --git a/media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java b/media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java
new file mode 100644
index 0000000..7e2b4cf
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.numeric;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.format.ObjectFormat;
+
+import java.lang.Math;
+
+/**
+ * @hide
+ */
+public class SinWaveFilter extends Filter {
+
+ @GenerateFieldPort(name = "stepSize", hasDefault = true)
+ private float mStepSize = 0.05f;
+
+ private float mValue = 0.0f;
+
+ private FrameFormat mOutputFormat;
+
+ public SinWaveFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ mOutputFormat = ObjectFormat.fromClass(Float.class, FrameFormat.TARGET_SIMPLE);
+ addOutputPort("value", mOutputFormat);
+ }
+
+ @Override
+ public void open(FilterContext env) {
+ mValue = 0.0f;
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame output = env.getFrameManager().newFrame(mOutputFormat);
+ output.setObjectValue(((float)Math.sin(mValue) + 1.0f) / 2.0f);
+ pushOutput("value", output);
+ mValue += mStepSize;
+ output.release();
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java b/media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java
new file mode 100644
index 0000000..55088eb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.numeric;
diff --git a/media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java b/media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java
new file mode 100644
index 0000000..51f29f3
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.performance;
+
+/**
+ * @hide
+ */
+public class Throughput {
+
+ private final int mTotalFrames;
+ private final int mPeriodFrames;
+ private final int mPeriodTime;
+ private final int mPixels;
+
+ public Throughput(int totalFrames, int periodFrames, int periodTime, int pixels) {
+ mTotalFrames = totalFrames;
+ mPeriodFrames = periodFrames;
+ mPeriodTime = periodTime;
+ mPixels = pixels;
+ }
+
+ public int getTotalFrameCount() {
+ return mTotalFrames;
+ }
+
+ public int getPeriodFrameCount() {
+ return mPeriodFrames;
+ }
+
+ public int getPeriodTime() {
+ return mPeriodTime;
+ }
+
+ public float getFramesPerSecond() {
+ return mPeriodFrames / (float)mPeriodTime;
+ }
+
+ public float getNanosPerPixel() {
+ double frameTimeInNanos = (mPeriodTime / (double)mPeriodFrames) * 1000000.0;
+ return (float)(frameTimeInNanos / mPixels);
+ }
+
+ public String toString() {
+ return getFramesPerSecond() + " FPS";
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java b/media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java
new file mode 100644
index 0000000..ac837ed
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.performance;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.format.ObjectFormat;
+import android.os.SystemClock;
+
+/**
+ * @hide
+ */
+public class ThroughputFilter extends Filter {
+
+ @GenerateFieldPort(name = "period", hasDefault = true)
+ private int mPeriod = 5;
+
+ private long mLastTime = 0;
+
+ private int mTotalFrameCount = 0;
+ private int mPeriodFrameCount = 0;
+
+ private FrameFormat mOutputFormat;
+
+ public ThroughputFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input ports
+ addInputPort("frame");
+
+ // Add output ports
+ mOutputFormat = ObjectFormat.fromClass(Throughput.class, FrameFormat.TARGET_SIMPLE);
+ addOutputBasedOnInput("frame", "frame");
+ addOutputPort("throughput", mOutputFormat);
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void open(FilterContext env) {
+ mTotalFrameCount = 0;
+ mPeriodFrameCount = 0;
+ mLastTime = 0;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Pass through input frame
+ Frame input = pullInput("frame");
+ pushOutput("frame", input);
+
+ // Update stats
+ ++mTotalFrameCount;
+ ++mPeriodFrameCount;
+
+ // Check clock
+ if (mLastTime == 0) {
+ mLastTime = SystemClock.elapsedRealtime();
+ }
+ long curTime = SystemClock.elapsedRealtime();
+
+ // Output throughput info if time period is up
+ if ((curTime - mLastTime) >= (mPeriod * 1000)) {
+ FrameFormat inputFormat = input.getFormat();
+ int pixelCount = inputFormat.getWidth() * inputFormat.getHeight();
+ Throughput throughput = new Throughput(mTotalFrameCount,
+ mPeriodFrameCount,
+ mPeriod,
+ pixelCount);
+ Frame throughputFrame = context.getFrameManager().newFrame(mOutputFormat);
+ throughputFrame.setObjectValue(throughput);
+ pushOutput("throughput", throughputFrame);
+ mLastTime = curTime;
+ mPeriodFrameCount = 0;
+ }
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/performance/package-info.java b/media/mca/filterpacks/java/android/filterpacks/performance/package-info.java
new file mode 100644
index 0000000..8b77bbb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/performance/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.performance;
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java b/media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java
new file mode 100644
index 0000000..8c7cd69
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.text;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.format.ObjectFormat;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class StringLogger extends Filter {
+
+ public StringLogger(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("string", ObjectFormat.fromClass(Object.class,
+ FrameFormat.TARGET_SIMPLE));
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame input = pullInput("string");
+ String inputString = input.getObjectValue().toString();
+ Log.i("StringLogger", inputString);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java b/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java
new file mode 100644
index 0000000..cc33b89
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.text;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ObjectFormat;
+
+/**
+ * @hide
+ */
+public class StringSource extends Filter {
+
+ @GenerateFieldPort(name = "stringValue")
+ private String mString;
+
+ private FrameFormat mOutputFormat;
+
+ public StringSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ mOutputFormat = ObjectFormat.fromClass(String.class, FrameFormat.TARGET_SIMPLE);
+ addOutputPort("string", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame output = env.getFrameManager().newFrame(mOutputFormat);
+ output.setObjectValue(mString);
+ output.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+ pushOutput("string", output);
+ closeOutputPort("string");
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java b/media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java
new file mode 100644
index 0000000..0cf3477
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.text;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.format.ObjectFormat;
+
+/**
+ * @hide
+ */
+public class ToUpperCase extends Filter {
+
+ private FrameFormat mOutputFormat;
+
+ public ToUpperCase(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ mOutputFormat = ObjectFormat.fromClass(String.class, FrameFormat.TARGET_SIMPLE);
+ addMaskedInputPort("mixedcase", mOutputFormat);
+ addOutputPort("uppercase", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame input = pullInput("mixedcase");
+ String inputString = (String)input.getObjectValue();
+
+ Frame output = env.getFrameManager().newFrame(mOutputFormat);
+ output.setObjectValue(inputString.toUpperCase());
+
+ pushOutput("uppercase", output);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/package-info.java b/media/mca/filterpacks/java/android/filterpacks/text/package-info.java
new file mode 100644
index 0000000..371d3c1
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.text;
diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java
new file mode 100644
index 0000000..a5c1ccb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.ui;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterSurfaceView;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import android.graphics.Rect;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class SurfaceRenderFilter extends Filter implements SurfaceHolder.Callback {
+
+ private final int RENDERMODE_STRETCH = 0;
+ private final int RENDERMODE_FIT = 1;
+ private final int RENDERMODE_FILL_CROP = 2;
+
+ /** Required. Sets the destination filter surface view for this
+ * node.
+ */
+ @GenerateFinalPort(name = "surfaceView")
+ private FilterSurfaceView mSurfaceView;
+
+ /** Optional. Control how the incoming frames are rendered onto the
+ * output. Default is FIT.
+ * RENDERMODE_STRETCH: Just fill the output surfaceView.
+ * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
+ * have black bars.
+ * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
+ * bars. May crop.
+ */
+ @GenerateFieldPort(name = "renderMode", hasDefault = true)
+ private String mRenderModeString;
+
+ private boolean mIsBound = false;
+
+ private ShaderProgram mProgram;
+ private GLFrame mScreen;
+ private int mRenderMode = RENDERMODE_FIT;
+ private float mAspectRatio = 1.f;
+
+ private int mScreenWidth;
+ private int mScreenHeight;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "SurfaceRenderFilter";
+
+ public SurfaceRenderFilter(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Make sure we have a SurfaceView
+ if (mSurfaceView == null) {
+ throw new RuntimeException("NULL SurfaceView passed to SurfaceRenderFilter");
+ }
+
+ // Add input port
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ public void updateRenderMode() {
+ if (mRenderModeString != null) {
+ if (mRenderModeString.equals("stretch")) {
+ mRenderMode = RENDERMODE_STRETCH;
+ } else if (mRenderModeString.equals("fit")) {
+ mRenderMode = RENDERMODE_FIT;
+ } else if (mRenderModeString.equals("fill_crop")) {
+ mRenderMode = RENDERMODE_FILL_CROP;
+ } else {
+ throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
+ }
+ }
+ updateTargetRect();
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ // Create identity shader to render, and make sure to render upside-down, as textures
+ // are stored internally bottom-to-top.
+ mProgram = ShaderProgram.createIdentity(context);
+ mProgram.setSourceRect(0, 1, 1, -1);
+ mProgram.setClearsOutput(true);
+ mProgram.setClearColor(0.0f, 0.0f, 0.0f);
+
+ updateRenderMode();
+
+ // Create a frame representing the screen
+ MutableFrameFormat screenFormat = ImageFormat.create(mSurfaceView.getWidth(),
+ mSurfaceView.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
+ GLFrame.EXISTING_FBO_BINDING,
+ 0);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ // Bind surface view to us. This will emit a surfaceCreated and surfaceChanged call that
+ // will update our screen width and height.
+ mSurfaceView.unbind();
+ mSurfaceView.bindToListener(this, context.getGLEnvironment());
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Make sure we are bound to a surface before rendering
+ if (!mIsBound) {
+ Log.w("SurfaceRenderFilter",
+ this + ": Ignoring frame as there is no surface to render to!");
+ return;
+ }
+
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ GLEnvironment glEnv = mSurfaceView.getGLEnv();
+ if (glEnv != context.getGLEnvironment()) {
+ throw new RuntimeException("Surface created under different GLEnvironment!");
+ }
+
+
+ // Get input frame
+ Frame input = pullInput("frame");
+ boolean createdFrame = false;
+
+ float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
+ if (currentAspectRatio != mAspectRatio) {
+ if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
+ mAspectRatio = currentAspectRatio;
+ updateTargetRect();
+ }
+
+ // See if we need to copy to GPU
+ Frame gpuFrame = null;
+ if (mLogVerbose) Log.v("SurfaceRenderFilter", "Got input format: " + input.getFormat());
+ int target = input.getFormat().getTarget();
+ if (target != FrameFormat.TARGET_GPU) {
+ gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
+ FrameFormat.TARGET_GPU);
+ createdFrame = true;
+ } else {
+ gpuFrame = input;
+ }
+
+ // Activate our surface
+ glEnv.activateSurfaceWithId(mSurfaceView.getSurfaceId());
+
+ // Process
+ mProgram.process(gpuFrame, mScreen);
+
+ // And swap buffers
+ glEnv.swapBuffers();
+
+ if (createdFrame) {
+ gpuFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ updateTargetRect();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ mSurfaceView.unbind();
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mScreen != null) {
+ mScreen.release();
+ }
+ }
+
+ @Override
+ public synchronized void surfaceCreated(SurfaceHolder holder) {
+ mIsBound = true;
+ }
+
+ @Override
+ public synchronized void surfaceChanged(SurfaceHolder holder,
+ int format,
+ int width,
+ int height) {
+ // If the screen is null, we do not care about surface changes (yet). Once we have a
+ // screen object, we need to keep track of these changes.
+ if (mScreen != null) {
+ mScreenWidth = width;
+ mScreenHeight = height;
+ mScreen.setViewport(0, 0, mScreenWidth, mScreenHeight);
+ updateTargetRect();
+ }
+ }
+
+ @Override
+ public synchronized void surfaceDestroyed(SurfaceHolder holder) {
+ mIsBound = false;
+ }
+
+ private void updateTargetRect() {
+ if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
+ float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
+ float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+
+ switch (mRenderMode) {
+ case RENDERMODE_STRETCH:
+ mProgram.setTargetRect(0, 0, 1, 1);
+ break;
+ case RENDERMODE_FIT:
+ if (relativeAspectRatio > 1.0f) {
+ // Screen is wider than the camera, scale down X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ } else {
+ // Screen is taller than the camera, scale down Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ }
+ break;
+ case RENDERMODE_FILL_CROP:
+ if (relativeAspectRatio > 1) {
+ // Screen is wider than the camera, crop in Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ } else {
+ // Screen is taller than the camera, crop in X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java
new file mode 100644
index 0000000..308d168
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.ui;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import android.graphics.Rect;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class SurfaceTargetFilter extends Filter {
+
+ private final int RENDERMODE_STRETCH = 0;
+ private final int RENDERMODE_FIT = 1;
+ private final int RENDERMODE_FILL_CROP = 2;
+
+ /** Required. Sets the destination surface for this node. This assumes that
+ * higher-level code is ensuring that the surface is valid, and properly
+ * updates Surface parameters if they change.
+ */
+ @GenerateFinalPort(name = "surface")
+ private Surface mSurface;
+
+ /** Required. Width of the output surface */
+ @GenerateFieldPort(name = "owidth")
+ private int mScreenWidth;
+
+ /** Required. Height of the output surface */
+ @GenerateFieldPort(name = "oheight")
+ private int mScreenHeight;
+
+ /** Optional. Control how the incoming frames are rendered onto the
+ * output. Default is FIT.
+ * RENDERMODE_STRETCH: Just fill the output surfaceView.
+ * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
+ * have black bars.
+ * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
+ * bars. May crop.
+ */
+ @GenerateFieldPort(name = "renderMode", hasDefault = true)
+ private String mRenderModeString;
+
+ private ShaderProgram mProgram;
+ private GLEnvironment mGlEnv;
+ private GLFrame mScreen;
+ private int mRenderMode = RENDERMODE_FIT;
+ private float mAspectRatio = 1.f;
+
+ private int mSurfaceId = -1;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "SurfaceRenderFilter";
+
+ public SurfaceTargetFilter(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Make sure we have a Surface
+ if (mSurface == null) {
+ throw new RuntimeException("NULL Surface passed to SurfaceTargetFilter");
+ }
+
+ // Add input port
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ public void updateRenderMode() {
+ if (mRenderModeString != null) {
+ if (mRenderModeString.equals("stretch")) {
+ mRenderMode = RENDERMODE_STRETCH;
+ } else if (mRenderModeString.equals("fit")) {
+ mRenderMode = RENDERMODE_FIT;
+ } else if (mRenderModeString.equals("fill_crop")) {
+ mRenderMode = RENDERMODE_FILL_CROP;
+ } else {
+ throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
+ }
+ }
+ updateTargetRect();
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mGlEnv = context.getGLEnvironment();
+
+ // Create identity shader to render, and make sure to render upside-down, as textures
+ // are stored internally bottom-to-top.
+ mProgram = ShaderProgram.createIdentity(context);
+ mProgram.setSourceRect(0, 1, 1, -1);
+ mProgram.setClearsOutput(true);
+ mProgram.setClearColor(0.0f, 0.0f, 0.0f);
+
+ MutableFrameFormat screenFormat = ImageFormat.create(mScreenWidth,
+ mScreenHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
+ GLFrame.EXISTING_FBO_BINDING,
+ 0);
+
+ // Set up cropping
+ updateRenderMode();
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ registerSurface();
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ // Get input frame
+ Frame input = pullInput("frame");
+ boolean createdFrame = false;
+
+ float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
+ if (currentAspectRatio != mAspectRatio) {
+ if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
+ mAspectRatio = currentAspectRatio;
+ updateTargetRect();
+ }
+
+ // See if we need to copy to GPU
+ Frame gpuFrame = null;
+ if (mLogVerbose) Log.v("SurfaceRenderFilter", "Got input format: " + input.getFormat());
+ int target = input.getFormat().getTarget();
+ if (target != FrameFormat.TARGET_GPU) {
+ gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
+ FrameFormat.TARGET_GPU);
+ createdFrame = true;
+ } else {
+ gpuFrame = input;
+ }
+
+ // Activate our surface
+ mGlEnv.activateSurfaceWithId(mSurfaceId);
+
+ // Process
+ mProgram.process(gpuFrame, mScreen);
+
+ // And swap buffers
+ mGlEnv.swapBuffers();
+
+ if (createdFrame) {
+ gpuFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ mScreen.setViewport(0, 0, mScreenWidth, mScreenHeight);
+ updateTargetRect();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ unregisterSurface();
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mScreen != null) {
+ mScreen.release();
+ }
+ }
+
+ private void updateTargetRect() {
+ if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
+ float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
+ float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+
+ switch (mRenderMode) {
+ case RENDERMODE_STRETCH:
+ mProgram.setTargetRect(0, 0, 1, 1);
+ break;
+ case RENDERMODE_FIT:
+ if (relativeAspectRatio > 1.0f) {
+ // Screen is wider than the camera, scale down X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ } else {
+ // Screen is taller than the camera, scale down Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ }
+ break;
+ case RENDERMODE_FILL_CROP:
+ if (relativeAspectRatio > 1) {
+ // Screen is wider than the camera, crop in Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ } else {
+ // Screen is taller than the camera, crop in X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ }
+ break;
+ }
+ }
+ }
+
+ private void registerSurface() {
+ mSurfaceId = mGlEnv.registerSurface(mSurface);
+ if (mSurfaceId < 0) {
+ throw new RuntimeException("Could not register Surface: " + mSurface);
+ }
+ }
+
+ private void unregisterSurface() {
+ if (mSurfaceId > 0) {
+ mGlEnv.unregisterSurfaceId(mSurfaceId);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/package-info.java b/media/mca/filterpacks/java/android/filterpacks/ui/package-info.java
new file mode 100644
index 0000000..9ed7d51
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/ui/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.ui;
diff --git a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
new file mode 100644
index 0000000..52c9fda
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
@@ -0,0 +1,976 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videoproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.Frame;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.opengl.GLES20;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.lang.ArrayIndexOutOfBoundsException;
+import java.lang.Math;
+import java.util.Arrays;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class BackDropperFilter extends Filter {
+ /** User-visible parameters */
+
+ private final int BACKGROUND_STRETCH = 0;
+ private final int BACKGROUND_FIT = 1;
+ private final int BACKGROUND_FILL_CROP = 2;
+
+ @GenerateFieldPort(name = "backgroundFitMode", hasDefault = true)
+ private int mBackgroundFitMode = BACKGROUND_FILL_CROP;
+ @GenerateFieldPort(name = "learningDuration", hasDefault = true)
+ private int mLearningDuration = DEFAULT_LEARNING_DURATION;
+ @GenerateFieldPort(name = "learningVerifyDuration", hasDefault = true)
+ private int mLearningVerifyDuration = DEFAULT_LEARNING_VERIFY_DURATION;
+ @GenerateFieldPort(name = "acceptStddev", hasDefault = true)
+ private float mAcceptStddev = DEFAULT_ACCEPT_STDDEV;
+ @GenerateFieldPort(name = "hierLrgScale", hasDefault = true)
+ private float mHierarchyLrgScale = DEFAULT_HIER_LRG_SCALE;
+ @GenerateFieldPort(name = "hierMidScale", hasDefault = true)
+ private float mHierarchyMidScale = DEFAULT_HIER_MID_SCALE;
+ @GenerateFieldPort(name = "hierSmlScale", hasDefault = true)
+ private float mHierarchySmlScale = DEFAULT_HIER_SML_SCALE;
+
+ // Dimensions of foreground / background mask. Optimum value should take into account only
+ // image contents, NOT dimensions of input video stream.
+ @GenerateFieldPort(name = "maskWidthExp", hasDefault = true)
+ private int mMaskWidthExp = DEFAULT_MASK_WIDTH_EXPONENT;
+ @GenerateFieldPort(name = "maskHeightExp", hasDefault = true)
+ private int mMaskHeightExp = DEFAULT_MASK_HEIGHT_EXPONENT;
+
+ // Levels at which to compute foreground / background decision. Think of them as are deltas
+ // SUBTRACTED from maskWidthExp and maskHeightExp.
+ @GenerateFieldPort(name = "hierLrgExp", hasDefault = true)
+ private int mHierarchyLrgExp = DEFAULT_HIER_LRG_EXPONENT;
+ @GenerateFieldPort(name = "hierMidExp", hasDefault = true)
+ private int mHierarchyMidExp = DEFAULT_HIER_MID_EXPONENT;
+ @GenerateFieldPort(name = "hierSmlExp", hasDefault = true)
+ private int mHierarchySmlExp = DEFAULT_HIER_SML_EXPONENT;
+
+ @GenerateFieldPort(name = "lumScale", hasDefault = true)
+ private float mLumScale = DEFAULT_Y_SCALE_FACTOR;
+ @GenerateFieldPort(name = "chromaScale", hasDefault = true)
+ private float mChromaScale = DEFAULT_UV_SCALE_FACTOR;
+ @GenerateFieldPort(name = "maskBg", hasDefault = true)
+ private float mMaskBg = DEFAULT_MASK_BLEND_BG;
+ @GenerateFieldPort(name = "maskFg", hasDefault = true)
+ private float mMaskFg = DEFAULT_MASK_BLEND_FG;
+ @GenerateFieldPort(name = "exposureChange", hasDefault = true)
+ private float mExposureChange = DEFAULT_EXPOSURE_CHANGE;
+ @GenerateFieldPort(name = "whitebalanceredChange", hasDefault = true)
+ private float mWhiteBalanceRedChange = DEFAULT_WHITE_BALANCE_RED_CHANGE;
+ @GenerateFieldPort(name = "whitebalanceblueChange", hasDefault = true)
+ private float mWhiteBalanceBlueChange = DEFAULT_WHITE_BALANCE_BLUE_CHANGE;
+ @GenerateFieldPort(name = "autowbToggle", hasDefault = true)
+ private int mAutoWBToggle = DEFAULT_WHITE_BALANCE_TOGGLE;
+
+ // TODO: These are not updatable:
+ @GenerateFieldPort(name = "learningAdaptRate", hasDefault = true)
+ private float mAdaptRateLearning = DEFAULT_LEARNING_ADAPT_RATE;
+ @GenerateFieldPort(name = "adaptRateBg", hasDefault = true)
+ private float mAdaptRateBg = DEFAULT_ADAPT_RATE_BG;
+ @GenerateFieldPort(name = "adaptRateFg", hasDefault = true)
+ private float mAdaptRateFg = DEFAULT_ADAPT_RATE_FG;
+ @GenerateFieldPort(name = "maskVerifyRate", hasDefault = true)
+ private float mVerifyRate = DEFAULT_MASK_VERIFY_RATE;
+ @GenerateFieldPort(name = "learningDoneListener", hasDefault = true)
+ private LearningDoneListener mLearningDoneListener = null;
+
+ @GenerateFieldPort(name = "useTheForce", hasDefault = true)
+ private boolean mUseTheForce = false;
+
+ @GenerateFinalPort(name = "provideDebugOutputs", hasDefault = true)
+ private boolean mProvideDebugOutputs = false;
+
+ // Whether to mirror the background or not. For ex, the Camera app
+ // would mirror the preview for the front camera
+ @GenerateFieldPort(name = "mirrorBg", hasDefault = true)
+ private boolean mMirrorBg = false;
+
+ // The orientation of the display. This will change the flipping
+ // coordinates, if we were to mirror the background
+ @GenerateFieldPort(name = "orientation", hasDefault = true)
+ private int mOrientation = 0;
+
+ /** Default algorithm parameter values, for non-shader use */
+
+ // Frame count for learning bg model
+ private static final int DEFAULT_LEARNING_DURATION = 40;
+ // Frame count for learning verification
+ private static final int DEFAULT_LEARNING_VERIFY_DURATION = 10;
+ // Maximum distance (in standard deviations) for considering a pixel as background
+ private static final float DEFAULT_ACCEPT_STDDEV = 0.85f;
+ // Variance threshold scale factor for large scale of hierarchy
+ private static final float DEFAULT_HIER_LRG_SCALE = 0.7f;
+ // Variance threshold scale factor for medium scale of hierarchy
+ private static final float DEFAULT_HIER_MID_SCALE = 0.6f;
+ // Variance threshold scale factor for small scale of hierarchy
+ private static final float DEFAULT_HIER_SML_SCALE = 0.5f;
+ // Width of foreground / background mask.
+ private static final int DEFAULT_MASK_WIDTH_EXPONENT = 8;
+ // Height of foreground / background mask.
+ private static final int DEFAULT_MASK_HEIGHT_EXPONENT = 8;
+ // Area over which to average for large scale (length in pixels = 2^HIERARCHY_*_EXPONENT)
+ private static final int DEFAULT_HIER_LRG_EXPONENT = 3;
+ // Area over which to average for medium scale
+ private static final int DEFAULT_HIER_MID_EXPONENT = 2;
+ // Area over which to average for small scale
+ private static final int DEFAULT_HIER_SML_EXPONENT = 0;
+ // Scale factor for luminance channel in distance calculations (larger = more significant)
+ private static final float DEFAULT_Y_SCALE_FACTOR = 0.40f;
+ // Scale factor for chroma channels in distance calculations
+ private static final float DEFAULT_UV_SCALE_FACTOR = 1.35f;
+ // Mask value to start blending away from background
+ private static final float DEFAULT_MASK_BLEND_BG = 0.65f;
+ // Mask value to start blending away from foreground
+ private static final float DEFAULT_MASK_BLEND_FG = 0.95f;
+ // Exposure stop number to change the brightness of foreground
+ private static final float DEFAULT_EXPOSURE_CHANGE = 1.0f;
+ // White balance change in Red channel for foreground
+ private static final float DEFAULT_WHITE_BALANCE_RED_CHANGE = 0.0f;
+ // White balance change in Blue channel for foreground
+ private static final float DEFAULT_WHITE_BALANCE_BLUE_CHANGE = 0.0f;
+ // Variable to control automatic white balance effect
+ // 0.f -> Auto WB is off; 1.f-> Auto WB is on
+ private static final int DEFAULT_WHITE_BALANCE_TOGGLE = 0;
+
+ // Default rate at which to learn bg model during learning period
+ private static final float DEFAULT_LEARNING_ADAPT_RATE = 0.2f;
+ // Default rate at which to learn bg model from new background pixels
+ private static final float DEFAULT_ADAPT_RATE_BG = 0.0f;
+ // Default rate at which to learn bg model from new foreground pixels
+ private static final float DEFAULT_ADAPT_RATE_FG = 0.0f;
+ // Default rate at which to verify whether background is stable
+ private static final float DEFAULT_MASK_VERIFY_RATE = 0.25f;
+ // Default rate at which to verify whether background is stable
+ private static final int DEFAULT_LEARNING_DONE_THRESHOLD = 20;
+
+ // Default 3x3 matrix, column major, for fitting background 1:1
+ private static final float[] DEFAULT_BG_FIT_TRANSFORM = new float[] {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+ };
+
+ /** Default algorithm parameter values, for shader use */
+
+ // Area over which to blur binary mask values (length in pixels = 2^MASK_SMOOTH_EXPONENT)
+ private static final String MASK_SMOOTH_EXPONENT = "2.0";
+ // Scale value for mapping variance distance to fit nicely to 0-1, 8-bit
+ private static final String DISTANCE_STORAGE_SCALE = "0.6";
+ // Scale value for mapping variance to fit nicely to 0-1, 8-bit
+ private static final String VARIANCE_STORAGE_SCALE = "5.0";
+ // Default scale of auto white balance parameters
+ private static final String DEFAULT_AUTO_WB_SCALE = "0.25";
+ // Minimum variance (0-255 scale)
+ private static final String MIN_VARIANCE = "3.0";
+ // Column-major array for 4x4 matrix converting RGB to YCbCr, JPEG definition (no pedestal)
+ private static final String RGB_TO_YUV_MATRIX = "0.299, -0.168736, 0.5, 0.000, " +
+ "0.587, -0.331264, -0.418688, 0.000, " +
+ "0.114, 0.5, -0.081312, 0.000, " +
+ "0.000, 0.5, 0.5, 1.000 ";
+ /** Stream names */
+
+ private static final String[] mInputNames = {"video",
+ "background"};
+
+ private static final String[] mOutputNames = {"video"};
+
+ private static final String[] mDebugOutputNames = {"debug1",
+ "debug2"};
+
+ /** Other private variables */
+
+ private FrameFormat mOutputFormat;
+ private MutableFrameFormat mMemoryFormat;
+ private MutableFrameFormat mMaskFormat;
+ private MutableFrameFormat mAverageFormat;
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "BackDropperFilter";
+
+ /** Shader source code */
+
+ // Shared uniforms and utility functions
+ private static String mSharedUtilShader =
+ "precision mediump float;\n" +
+ "uniform float fg_adapt_rate;\n" +
+ "uniform float bg_adapt_rate;\n" +
+ "const mat4 coeff_yuv = mat4(" + RGB_TO_YUV_MATRIX + ");\n" +
+ "const float dist_scale = " + DISTANCE_STORAGE_SCALE + ";\n" +
+ "const float inv_dist_scale = 1. / dist_scale;\n" +
+ "const float var_scale=" + VARIANCE_STORAGE_SCALE + ";\n" +
+ "const float inv_var_scale = 1. / var_scale;\n" +
+ "const float min_variance = inv_var_scale *" + MIN_VARIANCE + "/ 256.;\n" +
+ "const float auto_wb_scale = " + DEFAULT_AUTO_WB_SCALE + ";\n" +
+ "\n" +
+ // Variance distance in luminance between current pixel and background model
+ "float gauss_dist_y(float y, float mean, float variance) {\n" +
+ " float dist = (y - mean) * (y - mean) / variance;\n" +
+ " return dist;\n" +
+ "}\n" +
+ // Sum of variance distances in chroma between current pixel and background
+ // model
+ "float gauss_dist_uv(vec2 uv, vec2 mean, vec2 variance) {\n" +
+ " vec2 dist = (uv - mean) * (uv - mean) / variance;\n" +
+ " return dist.r + dist.g;\n" +
+ "}\n" +
+ // Select learning rate for pixel based on smoothed decision mask alpha
+ "float local_adapt_rate(float alpha) {\n" +
+ " return mix(bg_adapt_rate, fg_adapt_rate, alpha);\n" +
+ "}\n" +
+ "\n";
+
+ // Distance calculation shader. Calculates a distance metric between the foreground and the
+ // current background model, in both luminance and in chroma (yuv space). Distance is
+ // measured in variances from the mean background value. For chroma, the distance is the sum
+ // of the two individual color channel distances. The distances are output on the b and alpha
+ // channels, r and g are for debug information.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Background mean mask.
+ // tex_sampler_2: Background variance mask.
+ // subsample_level: Level on foreground frame's mip-map.
+ private static final String mBgDistanceShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float subsample_level;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord, subsample_level);\n" +
+ " vec4 fg = coeff_yuv * vec4(fg_rgb.rgb, 1.);\n" +
+ " vec4 mean = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " vec4 variance = inv_var_scale * texture2D(tex_sampler_2, v_texcoord);\n" +
+ "\n" +
+ " float dist_y = gauss_dist_y(fg.r, mean.r, variance.r);\n" +
+ " float dist_uv = gauss_dist_uv(fg.gb, mean.gb, variance.gb);\n" +
+ " gl_FragColor = vec4(0.5*fg.rg, dist_scale*dist_y, dist_scale*dist_uv);\n" +
+ "}\n";
+
+ // Foreground/background mask decision shader. Decides whether a frame is in the foreground or
+ // the background using a hierarchical threshold on the distance. Binary foreground/background
+ // mask is placed in the alpha channel. The RGB channels contain debug information.
+ private static final String mBgMaskShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float accept_variance;\n" +
+ "uniform vec2 yuv_weights;\n" +
+ "uniform float scale_lrg;\n" +
+ "uniform float scale_mid;\n" +
+ "uniform float scale_sml;\n" +
+ "uniform float exp_lrg;\n" +
+ "uniform float exp_mid;\n" +
+ "uniform float exp_sml;\n" +
+ "varying vec2 v_texcoord;\n" +
+ // Decide whether pixel is foreground or background based on Y and UV
+ // distance and maximum acceptable variance.
+ // yuv_weights.x is smaller than yuv_weights.y to discount the influence of shadow
+ "bool is_fg(vec2 dist_yc, float accept_variance) {\n" +
+ " return ( dot(yuv_weights, dist_yc) >= accept_variance );\n" +
+ "}\n" +
+ "void main() {\n" +
+ " vec4 dist_lrg_sc = texture2D(tex_sampler_0, v_texcoord, exp_lrg);\n" +
+ " vec4 dist_mid_sc = texture2D(tex_sampler_0, v_texcoord, exp_mid);\n" +
+ " vec4 dist_sml_sc = texture2D(tex_sampler_0, v_texcoord, exp_sml);\n" +
+ " vec2 dist_lrg = inv_dist_scale * dist_lrg_sc.ba;\n" +
+ " vec2 dist_mid = inv_dist_scale * dist_mid_sc.ba;\n" +
+ " vec2 dist_sml = inv_dist_scale * dist_sml_sc.ba;\n" +
+ " vec2 norm_dist = 0.75 * dist_sml / accept_variance;\n" + // For debug viz
+ " bool is_fg_lrg = is_fg(dist_lrg, accept_variance * scale_lrg);\n" +
+ " bool is_fg_mid = is_fg_lrg || is_fg(dist_mid, accept_variance * scale_mid);\n" +
+ " float is_fg_sml =\n" +
+ " float(is_fg_mid || is_fg(dist_sml, accept_variance * scale_sml));\n" +
+ " float alpha = 0.5 * is_fg_sml + 0.3 * float(is_fg_mid) + 0.2 * float(is_fg_lrg);\n" +
+ " gl_FragColor = vec4(alpha, norm_dist, is_fg_sml);\n" +
+ "}\n";
+
+ // Automatic White Balance parameter decision shader
+ // Use the Gray World assumption that in a white balance corrected image, the average of R, G, B
+ // channel will be a common gray value.
+ // To match the white balance of foreground and background, the average of R, G, B channel of
+ // two videos should match.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Mip-map for background (playback) video frame.
+ // pyramid_depth: Depth of input frames' mip-maps.
+ private static final String mAutomaticWhiteBalance =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float pyramid_depth;\n" +
+ "uniform bool autowb_toggle;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 mean_video = texture2D(tex_sampler_0, v_texcoord, pyramid_depth);\n"+
+ " vec4 mean_bg = texture2D(tex_sampler_1, v_texcoord, pyramid_depth);\n" +
+ // If Auto WB is toggled off, the return texture will be a unicolor texture of value 1
+ // If Auto WB is toggled on, the return texture will be a unicolor texture with
+ // adjustment parameters for R and B channels stored in the corresponding channel
+ " float green_normalizer = mean_video.g / mean_bg.g;\n"+
+ " vec4 adjusted_value = vec4(mean_bg.r / mean_video.r * green_normalizer, 1., \n" +
+ " mean_bg.b / mean_video.b * green_normalizer, 1.) * auto_wb_scale; \n" +
+ " gl_FragColor = autowb_toggle ? adjusted_value : vec4(auto_wb_scale);\n" +
+ "}\n";
+
+
+ // Background subtraction shader. Uses a mipmap of the binary mask map to blend smoothly between
+ // foreground and background
+ // Inputs:
+ // tex_sampler_0: Foreground (live) video frame.
+ // tex_sampler_1: Background (playback) video frame.
+ // tex_sampler_2: Foreground/background mask.
+ // tex_sampler_3: Auto white-balance factors.
+ private static final String mBgSubtractShader =
+ "uniform mat3 bg_fit_transform;\n" +
+ "uniform float mask_blend_bg;\n" +
+ "uniform float mask_blend_fg;\n" +
+ "uniform float exposure_change;\n" +
+ "uniform float whitebalancered_change;\n" +
+ "uniform float whitebalanceblue_change;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform sampler2D tex_sampler_3;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec2 bg_texcoord = (bg_fit_transform * vec3(v_texcoord, 1.)).xy;\n" +
+ " vec4 bg_rgb = texture2D(tex_sampler_1, bg_texcoord);\n" +
+ // The foreground texture is modified by multiplying both manual and auto white balance changes in R and B
+ // channel and multiplying exposure change in all R, G, B channels.
+ " vec4 wb_auto_scale = texture2D(tex_sampler_3, v_texcoord) * exposure_change / auto_wb_scale;\n" +
+ " vec4 wb_manual_scale = vec4(1. + whitebalancered_change, 1., 1. + whitebalanceblue_change, 1.);\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 fg_adjusted = fg_rgb * wb_manual_scale * wb_auto_scale;\n"+
+ " vec4 mask = texture2D(tex_sampler_2, v_texcoord, \n" +
+ " " + MASK_SMOOTH_EXPONENT + ");\n" +
+ " float alpha = smoothstep(mask_blend_bg, mask_blend_fg, mask.a);\n" +
+ " gl_FragColor = mix(bg_rgb, fg_adjusted, alpha);\n";
+
+ // May the Force... Makes the foreground object translucent blue, with a bright
+ // blue-white outline
+ private static final String mBgSubtractForceShader =
+ " vec4 ghost_rgb = (fg_adjusted * 0.7 + vec4(0.3,0.3,0.4,0.))*0.65 + \n" +
+ " 0.35*bg_rgb;\n" +
+ " float glow_start = 0.75 * mask_blend_bg; \n"+
+ " float glow_max = mask_blend_bg; \n"+
+ " gl_FragColor = mask.a < glow_start ? bg_rgb : \n" +
+ " mask.a < glow_max ? mix(bg_rgb, vec4(0.9,0.9,1.0,1.0), \n" +
+ " (mask.a - glow_start) / (glow_max - glow_start) ) : \n" +
+ " mask.a < mask_blend_fg ? mix(vec4(0.9,0.9,1.0,1.0), ghost_rgb, \n" +
+ " (mask.a - glow_max) / (mask_blend_fg - glow_max) ) : \n" +
+ " ghost_rgb;\n" +
+ "}\n";
+
+ // Background model mean update shader. Skews the current model mean toward the most recent pixel
+ // value for a pixel, weighted by the learning rate and by whether the pixel is classified as
+ // foreground or background.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Background mean mask.
+ // tex_sampler_2: Foreground/background mask.
+ // subsample_level: Level on foreground frame's mip-map.
+ private static final String mUpdateBgModelMeanShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float subsample_level;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord, subsample_level);\n" +
+ " vec4 fg = coeff_yuv * vec4(fg_rgb.rgb, 1.);\n" +
+ " vec4 mean = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_2, v_texcoord, \n" +
+ " " + MASK_SMOOTH_EXPONENT + ");\n" +
+ "\n" +
+ " float alpha = local_adapt_rate(mask.a);\n" +
+ " vec4 new_mean = mix(mean, fg, alpha);\n" +
+ " gl_FragColor = new_mean;\n" +
+ "}\n";
+
+ // Background model variance update shader. Skews the current model variance toward the most
+ // recent variance for the pixel, weighted by the learning rate and by whether the pixel is
+ // classified as foreground or background.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Background mean mask.
+ // tex_sampler_2: Background variance mask.
+ // tex_sampler_3: Foreground/background mask.
+ // subsample_level: Level on foreground frame's mip-map.
+ // TODO: to improve efficiency, use single mark for mean + variance, then merge this into
+ // mUpdateBgModelMeanShader.
+ private static final String mUpdateBgModelVarianceShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform sampler2D tex_sampler_3;\n" +
+ "uniform float subsample_level;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord, subsample_level);\n" +
+ " vec4 fg = coeff_yuv * vec4(fg_rgb.rgb, 1.);\n" +
+ " vec4 mean = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " vec4 variance = inv_var_scale * texture2D(tex_sampler_2, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_3, v_texcoord, \n" +
+ " " + MASK_SMOOTH_EXPONENT + ");\n" +
+ "\n" +
+ " float alpha = local_adapt_rate(mask.a);\n" +
+ " vec4 cur_variance = (fg-mean)*(fg-mean);\n" +
+ " vec4 new_variance = mix(variance, cur_variance, alpha);\n" +
+ " new_variance = max(new_variance, vec4(min_variance));\n" +
+ " gl_FragColor = var_scale * new_variance;\n" +
+ "}\n";
+
+ // Background verification shader. Skews the current background verification mask towards the
+ // most recent frame, weighted by the learning rate.
+ private static final String mMaskVerifyShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float verify_rate;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 lastmask = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " float newmask = mix(lastmask.a, mask.a, verify_rate);\n" +
+ " gl_FragColor = vec4(0., 0., 0., newmask);\n" +
+ "}\n";
+
+ /** Shader program objects */
+
+ private ShaderProgram mBgDistProgram;
+ private ShaderProgram mBgMaskProgram;
+ private ShaderProgram mBgSubtractProgram;
+ private ShaderProgram mBgUpdateMeanProgram;
+ private ShaderProgram mBgUpdateVarianceProgram;
+ private ShaderProgram mCopyOutProgram;
+ private ShaderProgram mAutomaticWhiteBalanceProgram;
+ private ShaderProgram mMaskVerifyProgram;
+ private ShaderProgram copyShaderProgram;
+
+ /** Background model storage */
+
+ private boolean mPingPong;
+ private GLFrame mBgMean[];
+ private GLFrame mBgVariance[];
+ private GLFrame mMaskVerify[];
+ private GLFrame mDistance;
+ private GLFrame mAutoWB;
+ private GLFrame mMask;
+ private GLFrame mVideoInput;
+ private GLFrame mBgInput;
+ private GLFrame mMaskAverage;
+
+ /** Overall filter state */
+
+ private boolean isOpen;
+ private int mFrameCount;
+ private boolean mStartLearning;
+ private boolean mBackgroundFitModeChanged;
+ private float mRelativeAspect;
+ private int mPyramidDepth;
+ private int mSubsampleLevel;
+
+ /** Learning listener object */
+
+ public interface LearningDoneListener {
+ public void onLearningDone(BackDropperFilter filter);
+ }
+
+ /** Public Filter methods */
+
+ public BackDropperFilter(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Inputs.
+ // TODO: Target should be GPU, but relaxed for now.
+ FrameFormat imageFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_UNSPECIFIED);
+ for (String inputName : mInputNames) {
+ addMaskedInputPort(inputName, imageFormat);
+ }
+ // Normal outputs
+ for (String outputName : mOutputNames) {
+ addOutputBasedOnInput(outputName, "video");
+ }
+
+ // Debug outputs
+ if (mProvideDebugOutputs) {
+ for (String outputName : mDebugOutputNames) {
+ addOutputBasedOnInput(outputName, "video");
+ }
+ }
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ // Create memory format based on video input.
+ MutableFrameFormat format = inputFormat.mutableCopy();
+ // Is this a debug output port? If so, leave dimensions unspecified.
+ if (!Arrays.asList(mOutputNames).contains(portName)) {
+ format.setDimensions(FrameFormat.SIZE_UNSPECIFIED, FrameFormat.SIZE_UNSPECIFIED);
+ }
+ return format;
+ }
+
+ private boolean createMemoryFormat(FrameFormat inputFormat) {
+ // We can't resize because that would require re-learning.
+ if (mMemoryFormat != null) {
+ return false;
+ }
+
+ if (inputFormat.getWidth() == FrameFormat.SIZE_UNSPECIFIED ||
+ inputFormat.getHeight() == FrameFormat.SIZE_UNSPECIFIED) {
+ throw new RuntimeException("Attempting to process input frame with unknown size");
+ }
+
+ mMaskFormat = inputFormat.mutableCopy();
+ int maskWidth = (int)Math.pow(2, mMaskWidthExp);
+ int maskHeight = (int)Math.pow(2, mMaskHeightExp);
+ mMaskFormat.setDimensions(maskWidth, maskHeight);
+
+ mPyramidDepth = Math.max(mMaskWidthExp, mMaskHeightExp);
+ mMemoryFormat = mMaskFormat.mutableCopy();
+ int widthExp = Math.max(mMaskWidthExp, pyramidLevel(inputFormat.getWidth()));
+ int heightExp = Math.max(mMaskHeightExp, pyramidLevel(inputFormat.getHeight()));
+ mPyramidDepth = Math.max(widthExp, heightExp);
+ int memWidth = Math.max(maskWidth, (int)Math.pow(2, widthExp));
+ int memHeight = Math.max(maskHeight, (int)Math.pow(2, heightExp));
+ mMemoryFormat.setDimensions(memWidth, memHeight);
+ mSubsampleLevel = mPyramidDepth - Math.max(mMaskWidthExp, mMaskHeightExp);
+
+ if (mLogVerbose) {
+ Log.v(TAG, "Mask frames size " + maskWidth + " x " + maskHeight);
+ Log.v(TAG, "Pyramid levels " + widthExp + " x " + heightExp);
+ Log.v(TAG, "Memory frames size " + memWidth + " x " + memHeight);
+ }
+
+ mAverageFormat = inputFormat.mutableCopy();
+ mAverageFormat.setDimensions(1,1);
+ return true;
+ }
+
+ public void prepare(FilterContext context){
+ if (mLogVerbose) Log.v(TAG, "Preparing BackDropperFilter!");
+
+ mBgMean = new GLFrame[2];
+ mBgVariance = new GLFrame[2];
+ mMaskVerify = new GLFrame[2];
+ copyShaderProgram = ShaderProgram.createIdentity(context);
+ }
+
+ private void allocateFrames(FrameFormat inputFormat, FilterContext context) {
+ if (!createMemoryFormat(inputFormat)) {
+ return; // All set.
+ }
+ if (mLogVerbose) Log.v(TAG, "Allocating BackDropperFilter frames");
+
+ // Create initial background model values
+ int numBytes = mMaskFormat.getSize();
+ byte[] initialBgMean = new byte[numBytes];
+ byte[] initialBgVariance = new byte[numBytes];
+ byte[] initialMaskVerify = new byte[numBytes];
+ for (int i = 0; i < numBytes; i++) {
+ initialBgMean[i] = (byte)128;
+ initialBgVariance[i] = (byte)10;
+ initialMaskVerify[i] = (byte)0;
+ }
+
+ // Get frames to store background model in
+ for (int i = 0; i < 2; i++) {
+ mBgMean[i] = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mBgMean[i].setData(initialBgMean, 0, numBytes);
+
+ mBgVariance[i] = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mBgVariance[i].setData(initialBgVariance, 0, numBytes);
+
+ mMaskVerify[i] = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mMaskVerify[i].setData(initialMaskVerify, 0, numBytes);
+ }
+
+ // Get frames to store other textures in
+ if (mLogVerbose) Log.v(TAG, "Done allocating texture for Mean and Variance objects!");
+
+ mDistance = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mMask = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mAutoWB = (GLFrame)context.getFrameManager().newFrame(mAverageFormat);
+ mVideoInput = (GLFrame)context.getFrameManager().newFrame(mMemoryFormat);
+ mBgInput = (GLFrame)context.getFrameManager().newFrame(mMemoryFormat);
+ mMaskAverage = (GLFrame)context.getFrameManager().newFrame(mAverageFormat);
+
+ // Create shader programs
+ mBgDistProgram = new ShaderProgram(context, mSharedUtilShader + mBgDistanceShader);
+ mBgDistProgram.setHostValue("subsample_level", (float)mSubsampleLevel);
+
+ mBgMaskProgram = new ShaderProgram(context, mSharedUtilShader + mBgMaskShader);
+ mBgMaskProgram.setHostValue("accept_variance", mAcceptStddev * mAcceptStddev);
+ float[] yuvWeights = { mLumScale, mChromaScale };
+ mBgMaskProgram.setHostValue("yuv_weights", yuvWeights );
+ mBgMaskProgram.setHostValue("scale_lrg", mHierarchyLrgScale);
+ mBgMaskProgram.setHostValue("scale_mid", mHierarchyMidScale);
+ mBgMaskProgram.setHostValue("scale_sml", mHierarchySmlScale);
+ mBgMaskProgram.setHostValue("exp_lrg", (float)(mSubsampleLevel + mHierarchyLrgExp));
+ mBgMaskProgram.setHostValue("exp_mid", (float)(mSubsampleLevel + mHierarchyMidExp));
+ mBgMaskProgram.setHostValue("exp_sml", (float)(mSubsampleLevel + mHierarchySmlExp));
+
+ if (mUseTheForce) {
+ mBgSubtractProgram = new ShaderProgram(context, mSharedUtilShader + mBgSubtractShader + mBgSubtractForceShader);
+ } else {
+ mBgSubtractProgram = new ShaderProgram(context, mSharedUtilShader + mBgSubtractShader + "}\n");
+ }
+ mBgSubtractProgram.setHostValue("bg_fit_transform", DEFAULT_BG_FIT_TRANSFORM);
+ mBgSubtractProgram.setHostValue("mask_blend_bg", mMaskBg);
+ mBgSubtractProgram.setHostValue("mask_blend_fg", mMaskFg);
+ mBgSubtractProgram.setHostValue("exposure_change", mExposureChange);
+ mBgSubtractProgram.setHostValue("whitebalanceblue_change", mWhiteBalanceBlueChange);
+ mBgSubtractProgram.setHostValue("whitebalancered_change", mWhiteBalanceRedChange);
+
+
+ mBgUpdateMeanProgram = new ShaderProgram(context, mSharedUtilShader + mUpdateBgModelMeanShader);
+ mBgUpdateMeanProgram.setHostValue("subsample_level", (float)mSubsampleLevel);
+
+ mBgUpdateVarianceProgram = new ShaderProgram(context, mSharedUtilShader + mUpdateBgModelVarianceShader);
+ mBgUpdateVarianceProgram.setHostValue("subsample_level", (float)mSubsampleLevel);
+
+ mCopyOutProgram = ShaderProgram.createIdentity(context);
+
+ mAutomaticWhiteBalanceProgram = new ShaderProgram(context, mSharedUtilShader + mAutomaticWhiteBalance);
+ mAutomaticWhiteBalanceProgram.setHostValue("pyramid_depth", (float)mPyramidDepth);
+ mAutomaticWhiteBalanceProgram.setHostValue("autowb_toggle", mAutoWBToggle);
+
+ mMaskVerifyProgram = new ShaderProgram(context, mSharedUtilShader + mMaskVerifyShader);
+ mMaskVerifyProgram.setHostValue("verify_rate", mVerifyRate);
+
+ if (mLogVerbose) Log.v(TAG, "Shader width set to " + mMemoryFormat.getWidth());
+
+ mRelativeAspect = 1.f;
+
+ mFrameCount = 0;
+ mStartLearning = true;
+ }
+
+ public void process(FilterContext context) {
+ // Grab inputs and ready intermediate frames and outputs.
+ Frame video = pullInput("video");
+ Frame background = pullInput("background");
+ allocateFrames(video.getFormat(), context);
+
+ // Update learning rate after initial learning period
+ if (mStartLearning) {
+ if (mLogVerbose) Log.v(TAG, "Starting learning");
+ mBgUpdateMeanProgram.setHostValue("bg_adapt_rate", mAdaptRateLearning);
+ mBgUpdateMeanProgram.setHostValue("fg_adapt_rate", mAdaptRateLearning);
+ mBgUpdateVarianceProgram.setHostValue("bg_adapt_rate", mAdaptRateLearning);
+ mBgUpdateVarianceProgram.setHostValue("fg_adapt_rate", mAdaptRateLearning);
+ mFrameCount = 0;
+ mStartLearning = false;
+ }
+
+ // Select correct pingpong buffers
+ int inputIndex = mPingPong ? 0 : 1;
+ int outputIndex = mPingPong ? 1 : 0;
+ mPingPong = !mPingPong;
+
+ // Check relative aspect ratios
+ updateBgScaling(video, background, mBackgroundFitModeChanged);
+ mBackgroundFitModeChanged = false;
+
+ // Make copies for input frames to GLFrames
+
+ copyShaderProgram.process(video, mVideoInput);
+ copyShaderProgram.process(background, mBgInput);
+
+ mVideoInput.generateMipMap();
+ mVideoInput.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ mBgInput.generateMipMap();
+ mBgInput.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ // Process shaders
+ Frame[] distInputs = { mVideoInput, mBgMean[inputIndex], mBgVariance[inputIndex] };
+ mBgDistProgram.process(distInputs, mDistance);
+ mDistance.generateMipMap();
+ mDistance.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ mBgMaskProgram.process(mDistance, mMask);
+ mMask.generateMipMap();
+ mMask.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ Frame[] autoWBInputs = { mVideoInput, mBgInput };
+ mAutomaticWhiteBalanceProgram.process(autoWBInputs, mAutoWB);
+
+ if (mFrameCount <= mLearningDuration) {
+ // During learning
+ pushOutput("video", video);
+
+ if (mFrameCount == mLearningDuration - mLearningVerifyDuration) {
+ copyShaderProgram.process(mMask, mMaskVerify[outputIndex]);
+
+ mBgUpdateMeanProgram.setHostValue("bg_adapt_rate", mAdaptRateBg);
+ mBgUpdateMeanProgram.setHostValue("fg_adapt_rate", mAdaptRateFg);
+ mBgUpdateVarianceProgram.setHostValue("bg_adapt_rate", mAdaptRateBg);
+ mBgUpdateVarianceProgram.setHostValue("fg_adapt_rate", mAdaptRateFg);
+
+
+ } else if (mFrameCount > mLearningDuration - mLearningVerifyDuration) {
+ // In the learning verification stage, compute background masks and a weighted average
+ // with weights grow exponentially with time
+ Frame[] maskVerifyInputs = {mMaskVerify[inputIndex], mMask};
+ mMaskVerifyProgram.process(maskVerifyInputs, mMaskVerify[outputIndex]);
+ mMaskVerify[outputIndex].generateMipMap();
+ mMaskVerify[outputIndex].setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ }
+
+ if (mFrameCount == mLearningDuration) {
+ // In the last verification frame, verify if the verification mask is almost blank
+ // If not, restart learning
+ copyShaderProgram.process(mMaskVerify[outputIndex], mMaskAverage);
+ ByteBuffer mMaskAverageByteBuffer = mMaskAverage.getData();
+ byte[] mask_average = mMaskAverageByteBuffer.array();
+ int bi = (int)(mask_average[3] & 0xFF);
+ if (mLogVerbose) Log.v(TAG, String.format("Mask_average is %d", bi));
+
+ if (bi >= DEFAULT_LEARNING_DONE_THRESHOLD) {
+ mStartLearning = true; // Restart learning
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Learning done");
+ if (mLearningDoneListener != null) {
+ mLearningDoneListener.onLearningDone(this);
+ }
+ }
+ }
+ } else {
+ Frame output = context.getFrameManager().newFrame(video.getFormat());
+ Frame[] subtractInputs = { video, background, mMask, mAutoWB };
+ mBgSubtractProgram.process(subtractInputs, output);
+ pushOutput("video", output);
+ output.release();
+ }
+
+ // Compute mean and variance of the background
+ if (mFrameCount < mLearningDuration - mLearningVerifyDuration ||
+ mAdaptRateBg > 0.0 || mAdaptRateFg > 0.0) {
+ Frame[] meanUpdateInputs = { mVideoInput, mBgMean[inputIndex], mMask };
+ mBgUpdateMeanProgram.process(meanUpdateInputs, mBgMean[outputIndex]);
+ mBgMean[outputIndex].generateMipMap();
+ mBgMean[outputIndex].setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ Frame[] varianceUpdateInputs = {
+ mVideoInput, mBgMean[inputIndex], mBgVariance[inputIndex], mMask
+ };
+ mBgUpdateVarianceProgram.process(varianceUpdateInputs, mBgVariance[outputIndex]);
+ mBgVariance[outputIndex].generateMipMap();
+ mBgVariance[outputIndex].setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ }
+
+ // Provide debug output to two smaller viewers
+ if (mProvideDebugOutputs) {
+ Frame dbg1 = context.getFrameManager().newFrame(video.getFormat());
+ mCopyOutProgram.process(video, dbg1);
+ pushOutput("debug1", dbg1);
+ dbg1.release();
+
+ Frame dbg2 = context.getFrameManager().newFrame(mMemoryFormat);
+ mCopyOutProgram.process(mMask, dbg2);
+ pushOutput("debug2", dbg2);
+ dbg2.release();
+ }
+
+ mFrameCount++;
+
+ if (mLogVerbose) {
+ if (mFrameCount % 30 == 0) {
+ if (startTime == -1) {
+ context.getGLEnvironment().activate();
+ GLES20.glFinish();
+ startTime = SystemClock.elapsedRealtime();
+ } else {
+ context.getGLEnvironment().activate();
+ GLES20.glFinish();
+ long endTime = SystemClock.elapsedRealtime();
+ Log.v(TAG, "Avg. frame duration: " + String.format("%.2f",(endTime-startTime)/30.) +
+ " ms. Avg. fps: " + String.format("%.2f", 1000./((endTime-startTime)/30.)) );
+ startTime = endTime;
+ }
+ }
+ }
+ }
+
+ private long startTime = -1;
+
+ public void close(FilterContext context) {
+ if (mMemoryFormat == null) {
+ return;
+ }
+
+ if (mLogVerbose) Log.v(TAG, "Filter Closing!");
+ for (int i = 0; i < 2; i++) {
+ mBgMean[i].release();
+ mBgVariance[i].release();
+ mMaskVerify[i].release();
+ }
+ mDistance.release();
+ mMask.release();
+ mAutoWB.release();
+ mVideoInput.release();
+ mBgInput.release();
+ mMaskAverage.release();
+
+ mMemoryFormat = null;
+ }
+
+ // Relearn background model
+ synchronized public void relearn() {
+ // Let the processing thread know about learning restart
+ mStartLearning = true;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // TODO: Many of these can be made ProgramPorts!
+ if (name.equals("backgroundFitMode")) {
+ mBackgroundFitModeChanged = true;
+ } else if (name.equals("acceptStddev")) {
+ mBgMaskProgram.setHostValue("accept_variance", mAcceptStddev * mAcceptStddev);
+ } else if (name.equals("hierLrgScale")) {
+ mBgMaskProgram.setHostValue("scale_lrg", mHierarchyLrgScale);
+ } else if (name.equals("hierMidScale")) {
+ mBgMaskProgram.setHostValue("scale_mid", mHierarchyMidScale);
+ } else if (name.equals("hierSmlScale")) {
+ mBgMaskProgram.setHostValue("scale_sml", mHierarchySmlScale);
+ } else if (name.equals("hierLrgExp")) {
+ mBgMaskProgram.setHostValue("exp_lrg", (float)(mSubsampleLevel + mHierarchyLrgExp));
+ } else if (name.equals("hierMidExp")) {
+ mBgMaskProgram.setHostValue("exp_mid", (float)(mSubsampleLevel + mHierarchyMidExp));
+ } else if (name.equals("hierSmlExp")) {
+ mBgMaskProgram.setHostValue("exp_sml", (float)(mSubsampleLevel + mHierarchySmlExp));
+ } else if (name.equals("lumScale") || name.equals("chromaScale")) {
+ float[] yuvWeights = { mLumScale, mChromaScale };
+ mBgMaskProgram.setHostValue("yuv_weights", yuvWeights );
+ } else if (name.equals("maskBg")) {
+ mBgSubtractProgram.setHostValue("mask_blend_bg", mMaskBg);
+ } else if (name.equals("maskFg")) {
+ mBgSubtractProgram.setHostValue("mask_blend_fg", mMaskFg);
+ } else if (name.equals("exposureChange")) {
+ mBgSubtractProgram.setHostValue("exposure_change", mExposureChange);
+ } else if (name.equals("whitebalanceredChange")) {
+ mBgSubtractProgram.setHostValue("whitebalancered_change", mWhiteBalanceRedChange);
+ } else if (name.equals("whitebalanceblueChange")) {
+ mBgSubtractProgram.setHostValue("whitebalanceblue_change", mWhiteBalanceBlueChange);
+ } else if (name.equals("autowbToggle")){
+ mAutomaticWhiteBalanceProgram.setHostValue("autowb_toggle", mAutoWBToggle);
+ }
+ }
+
+ private void updateBgScaling(Frame video, Frame background, boolean fitModeChanged) {
+ float foregroundAspect = (float)video.getFormat().getWidth() / video.getFormat().getHeight();
+ float backgroundAspect = (float)background.getFormat().getWidth() / background.getFormat().getHeight();
+ float currentRelativeAspect = foregroundAspect/backgroundAspect;
+ if (currentRelativeAspect != mRelativeAspect || fitModeChanged) {
+ mRelativeAspect = currentRelativeAspect;
+ float xMin = 0.f, xWidth = 1.f, yMin = 0.f, yWidth = 1.f;
+ switch (mBackgroundFitMode) {
+ case BACKGROUND_STRETCH:
+ // Just map 1:1
+ break;
+ case BACKGROUND_FIT:
+ if (mRelativeAspect > 1.0f) {
+ // Foreground is wider than background, scale down
+ // background in X
+ xMin = 0.5f - 0.5f * mRelativeAspect;
+ xWidth = 1.f * mRelativeAspect;
+ } else {
+ // Foreground is taller than background, scale down
+ // background in Y
+ yMin = 0.5f - 0.5f / mRelativeAspect;
+ yWidth = 1 / mRelativeAspect;
+ }
+ break;
+ case BACKGROUND_FILL_CROP:
+ if (mRelativeAspect > 1.0f) {
+ // Foreground is wider than background, crop
+ // background in Y
+ yMin = 0.5f - 0.5f / mRelativeAspect;
+ yWidth = 1.f / mRelativeAspect;
+ } else {
+ // Foreground is taller than background, crop
+ // background in X
+ xMin = 0.5f - 0.5f * mRelativeAspect;
+ xWidth = mRelativeAspect;
+ }
+ break;
+ }
+ // If mirroring is required (for ex. the camera mirrors the preview
+ // in the front camera)
+ // TODO: Backdropper does not attempt to apply any other transformation
+ // than just flipping. However, in the current state, it's "x-axis" is always aligned
+ // with the Camera's width. Hence, we need to define the mirroring based on the camera
+ // orientation. In the future, a cleaner design would be to cast away all the rotation
+ // in a separate place.
+ if (mMirrorBg) {
+ if (mLogVerbose) Log.v(TAG, "Mirroring the background!");
+ // Mirroring in portrait
+ if (mOrientation == 0 || mOrientation == 180) {
+ xWidth = -xWidth;
+ xMin = 1.0f - xMin;
+ } else {
+ // Mirroring in landscape
+ yWidth = -yWidth;
+ yMin = 1.0f - yMin;
+ }
+ }
+ if (mLogVerbose) Log.v(TAG, "bgTransform: xMin, yMin, xWidth, yWidth : " +
+ xMin + ", " + yMin + ", " + xWidth + ", " + yWidth +
+ ", mRelAspRatio = " + mRelativeAspect);
+ // The following matrix is the transpose of the actual matrix
+ float[] bgTransform = {xWidth, 0.f, 0.f,
+ 0.f, yWidth, 0.f,
+ xMin, yMin, 1.f};
+ mBgSubtractProgram.setHostValue("bg_fit_transform", bgTransform);
+ }
+ }
+
+ private int pyramidLevel(int size) {
+ return (int)Math.floor(Math.log10(size) / Math.log10(2)) - 1;
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
new file mode 100644
index 0000000..3657d8a
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videosink;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+import android.os.ConditionVariable;
+import android.media.MediaRecorder;
+import android.media.CamcorderProfile;
+import android.filterfw.core.GLEnvironment;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/** @hide */
+public class MediaEncoderFilter extends Filter {
+
+ /** User-visible parameters */
+
+ /** Recording state. When set to false, recording will stop, or will not
+ * start if not yet running the graph. Instead, frames are simply ignored.
+ * When switched back to true, recording will restart. This allows a single
+ * graph to both provide preview and to record video. If this is false,
+ * recording settings can be updated while the graph is running.
+ */
+ @GenerateFieldPort(name = "recording", hasDefault = true)
+ private boolean mRecording = true;
+
+ /** Filename to save the output. */
+ @GenerateFieldPort(name = "outputFile", hasDefault = true)
+ private String mOutputFile = new String("/sdcard/MediaEncoderOut.mp4");
+
+ /** File Descriptor to save the output. */
+ @GenerateFieldPort(name = "outputFileDescriptor", hasDefault = true)
+ private FileDescriptor mFd = null;
+
+ /** Input audio source. If not set, no audio will be recorded.
+ * Select from the values in MediaRecorder.AudioSource
+ */
+ @GenerateFieldPort(name = "audioSource", hasDefault = true)
+ private int mAudioSource = NO_AUDIO_SOURCE;
+
+ /** Media recorder info listener, which needs to implement
+ * MediaRecorder.OnInfoListener. Set this to receive notifications about
+ * recording events.
+ */
+ @GenerateFieldPort(name = "infoListener", hasDefault = true)
+ private MediaRecorder.OnInfoListener mInfoListener = null;
+
+ /** Media recorder error listener, which needs to implement
+ * MediaRecorder.OnErrorListener. Set this to receive notifications about
+ * recording errors.
+ */
+ @GenerateFieldPort(name = "errorListener", hasDefault = true)
+ private MediaRecorder.OnErrorListener mErrorListener = null;
+
+ /** Media recording done callback, which needs to implement OnRecordingDoneListener.
+ * Set this to finalize media upon completion of media recording.
+ */
+ @GenerateFieldPort(name = "recordingDoneListener", hasDefault = true)
+ private OnRecordingDoneListener mRecordingDoneListener = null;
+
+ /** Orientation hint. Used for indicating proper video playback orientation.
+ * Units are in degrees of clockwise rotation, valid values are (0, 90, 180,
+ * 270).
+ */
+ @GenerateFieldPort(name = "orientationHint", hasDefault = true)
+ private int mOrientationHint = 0;
+
+ /** Camcorder profile to use. Select from the profiles available in
+ * android.media.CamcorderProfile. If this field is set, it overrides
+ * settings to width, height, framerate, outputFormat, and videoEncoder.
+ */
+ @GenerateFieldPort(name = "recordingProfile", hasDefault = true)
+ private CamcorderProfile mProfile = null;
+
+ /** Frame width to be encoded, defaults to 320.
+ * Actual received frame size has to match this */
+ @GenerateFieldPort(name = "width", hasDefault = true)
+ private int mWidth = 320;
+
+ /** Frame height to to be encoded, defaults to 240.
+ * Actual received frame size has to match */
+ @GenerateFieldPort(name = "height", hasDefault = true)
+ private int mHeight = 240;
+
+ /** Stream framerate to encode the frames at.
+ * By default, frames are encoded at 30 FPS*/
+ @GenerateFieldPort(name = "framerate", hasDefault = true)
+ private int mFps = 30;
+
+ /** The output format to encode the frames in.
+ * Choose an output format from the options in
+ * android.media.MediaRecorder.OutputFormat */
+ @GenerateFieldPort(name = "outputFormat", hasDefault = true)
+ private int mOutputFormat = MediaRecorder.OutputFormat.MPEG_4;
+
+ /** The videoencoder to encode the frames with.
+ * Choose a videoencoder from the options in
+ * android.media.MediaRecorder.VideoEncoder */
+ @GenerateFieldPort(name = "videoEncoder", hasDefault = true)
+ private int mVideoEncoder = MediaRecorder.VideoEncoder.H264;
+
+ /** The input region to read from the frame. The corners of this quad are
+ * mapped to the output rectangle. The input frame ranges from (0,0)-(1,1),
+ * top-left to bottom-right. The corners of the quad are specified in the
+ * order bottom-left, bottom-right, top-left, top-right.
+ */
+ @GenerateFieldPort(name = "inputRegion", hasDefault = true)
+ private Quad mSourceRegion;
+
+ /** The maximum filesize (in bytes) of the recording session.
+ * By default, it will be 0 and will be passed on to the MediaRecorder.
+ * If the limit is zero or negative, MediaRecorder will disable the limit*/
+ @GenerateFieldPort(name = "maxFileSize", hasDefault = true)
+ private long mMaxFileSize = 0;
+
+ /** The maximum duration (in milliseconds) of the recording session.
+ * By default, it will be 0 and will be passed on to the MediaRecorder.
+ * If the limit is zero or negative, MediaRecorder will record indefinitely*/
+ @GenerateFieldPort(name = "maxDurationMs", hasDefault = true)
+ private int mMaxDurationMs = 0;
+
+ /** TimeLapse Interval between frames.
+ * By default, it will be 0. Whether the recording is timelapsed
+ * is inferred based on its value being greater than 0 */
+ @GenerateFieldPort(name = "timelapseRecordingIntervalUs", hasDefault = true)
+ private long mTimeBetweenTimeLapseFrameCaptureUs = 0;
+
+ // End of user visible parameters
+
+ private static final int NO_AUDIO_SOURCE = -1;
+
+ private int mSurfaceId;
+ private ShaderProgram mProgram;
+ private GLFrame mScreen;
+
+ private boolean mRecordingActive = false;
+ private long mTimestampNs = 0;
+ private long mLastTimeLapseFrameRealTimestampNs = 0;
+ private int mNumFramesEncoded = 0;
+ // Used to indicate whether recording is timelapsed.
+ // Inferred based on (mTimeBetweenTimeLapseFrameCaptureUs > 0)
+ private boolean mCaptureTimeLapse = false;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "MediaEncoderFilter";
+
+ // Our hook to the encoder
+ private MediaRecorder mMediaRecorder;
+
+ /** Callback to be called when media recording completes. */
+
+ public interface OnRecordingDoneListener {
+ public void onRecordingDone();
+ }
+
+ public MediaEncoderFilter(String name) {
+ super(name);
+ Point bl = new Point(0, 0);
+ Point br = new Point(1, 0);
+ Point tl = new Point(0, 1);
+ Point tr = new Point(1, 1);
+ mSourceRegion = new Quad(bl, br, tl, tr);
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port- will accept RGBA GLFrames
+ addMaskedInputPort("videoframe", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Port " + name + " has been updated");
+ if (name.equals("recording")) return;
+ if (name.equals("inputRegion")) {
+ if (isOpen()) updateSourceRegion();
+ return;
+ }
+ // TODO: Not sure if it is possible to update the maxFileSize
+ // when the recording is going on. For now, not doing that.
+ if (isOpen() && mRecordingActive) {
+ throw new RuntimeException("Cannot change recording parameters"
+ + " when the filter is recording!");
+ }
+ }
+
+ private void updateSourceRegion() {
+ // Flip source quad to map to OpenGL origin
+ Quad flippedRegion = new Quad();
+ flippedRegion.p0 = mSourceRegion.p2;
+ flippedRegion.p1 = mSourceRegion.p3;
+ flippedRegion.p2 = mSourceRegion.p0;
+ flippedRegion.p3 = mSourceRegion.p1;
+ mProgram.setSourceRegion(flippedRegion);
+ }
+
+ // update the MediaRecorderParams based on the variables.
+ // These have to be in certain order as per the MediaRecorder
+ // documentation
+ private void updateMediaRecorderParams() {
+ mCaptureTimeLapse = mTimeBetweenTimeLapseFrameCaptureUs > 0;
+ final int GRALLOC_BUFFER = 2;
+ mMediaRecorder.setVideoSource(GRALLOC_BUFFER);
+ if (!mCaptureTimeLapse && (mAudioSource != NO_AUDIO_SOURCE)) {
+ mMediaRecorder.setAudioSource(mAudioSource);
+ }
+ if (mProfile != null) {
+ mMediaRecorder.setProfile(mProfile);
+ mFps = mProfile.videoFrameRate;
+ } else {
+ mMediaRecorder.setOutputFormat(mOutputFormat);
+ mMediaRecorder.setVideoEncoder(mVideoEncoder);
+ mMediaRecorder.setVideoSize(mWidth, mHeight);
+ mMediaRecorder.setVideoFrameRate(mFps);
+ }
+ mMediaRecorder.setOrientationHint(mOrientationHint);
+ mMediaRecorder.setOnInfoListener(mInfoListener);
+ mMediaRecorder.setOnErrorListener(mErrorListener);
+ if (mFd != null) {
+ mMediaRecorder.setOutputFile(mFd);
+ } else {
+ mMediaRecorder.setOutputFile(mOutputFile);
+ }
+ try {
+ mMediaRecorder.setMaxFileSize(mMaxFileSize);
+ } catch (Exception e) {
+ // Following the logic in VideoCamera.java (in Camera app)
+ // We are going to ignore failure of setMaxFileSize here, as
+ // a) The composer selected may simply not support it, or
+ // b) The underlying media framework may not handle 64-bit range
+ // on the size restriction.
+ Log.w(TAG, "Setting maxFileSize on MediaRecorder unsuccessful! "
+ + e.getMessage());
+ }
+ mMediaRecorder.setMaxDuration(mMaxDurationMs);
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing");
+
+ mProgram = ShaderProgram.createIdentity(context);
+
+ mRecordingActive = false;
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening");
+ updateSourceRegion();
+ if (mRecording) startRecording(context);
+ }
+
+ private void startRecording(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting recording");
+
+ // Create a frame representing the screen
+ MutableFrameFormat screenFormat = new MutableFrameFormat(
+ FrameFormat.TYPE_BYTE, FrameFormat.TARGET_GPU);
+ screenFormat.setBytesPerSample(4);
+
+ int width, height;
+ if (mProfile != null) {
+ width = mProfile.videoFrameWidth;
+ height = mProfile.videoFrameHeight;
+ } else {
+ width = mWidth;
+ height = mHeight;
+ }
+ screenFormat.setDimensions(width, height);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(
+ screenFormat, GLFrame.EXISTING_FBO_BINDING, 0);
+
+ // Initialize the media recorder
+
+ mMediaRecorder = new MediaRecorder();
+ updateMediaRecorderParams();
+
+ try {
+ mMediaRecorder.prepare();
+ } catch (IllegalStateException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException("IOException in"
+ + "MediaRecorder.prepare()!", e);
+ } catch (Exception e) {
+ throw new RuntimeException("Unknown Exception in"
+ + "MediaRecorder.prepare()!", e);
+ }
+ // Make sure start() is called before trying to
+ // register the surface. The native window handle needed to create
+ // the surface is initiated in start()
+ mMediaRecorder.start();
+ if (mLogVerbose) Log.v(TAG, "Open: registering surface from Mediarecorder");
+ mSurfaceId = context.getGLEnvironment().
+ registerSurfaceFromMediaRecorder(mMediaRecorder);
+ mNumFramesEncoded = 0;
+ mRecordingActive = true;
+ }
+
+ public boolean skipFrameAndModifyTimestamp(long timestampNs) {
+ // first frame- encode. Don't skip
+ if (mNumFramesEncoded == 0) {
+ mLastTimeLapseFrameRealTimestampNs = timestampNs;
+ mTimestampNs = timestampNs;
+ if (mLogVerbose) Log.v(TAG, "timelapse: FIRST frame, last real t= "
+ + mLastTimeLapseFrameRealTimestampNs +
+ ", setting t = " + mTimestampNs );
+ return false;
+ }
+
+ // Workaround to bypass the first 2 input frames for skipping.
+ // The first 2 output frames from the encoder are: decoder specific info and
+ // the compressed video frame data for the first input video frame.
+ if (mNumFramesEncoded >= 2 && timestampNs <
+ (mLastTimeLapseFrameRealTimestampNs + 1000L * mTimeBetweenTimeLapseFrameCaptureUs)) {
+ // If 2 frames have been already encoded,
+ // Skip all frames from last encoded frame until
+ // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
+ if (mLogVerbose) Log.v(TAG, "timelapse: skipping intermediate frame");
+ return true;
+ } else {
+ // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
+ // - Reset mLastTimeLapseFrameRealTimestampNs to current time.
+ // - Artificially modify timestampNs to be one frame time (1/framerate) ahead
+ // of the last encoded frame's time stamp.
+ if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, Timestamp t = " + timestampNs +
+ ", last real t= " + mLastTimeLapseFrameRealTimestampNs +
+ ", interval = " + mTimeBetweenTimeLapseFrameCaptureUs);
+ mLastTimeLapseFrameRealTimestampNs = timestampNs;
+ mTimestampNs = mTimestampNs + (1000000000L / (long)mFps);
+ if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, setting t = "
+ + mTimestampNs + ", delta t = " + (1000000000L / (long)mFps) +
+ ", fps = " + mFps );
+ return false;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ GLEnvironment glEnv = context.getGLEnvironment();
+ // Get input frame
+ Frame input = pullInput("videoframe");
+
+ // Check if recording needs to start
+ if (!mRecordingActive && mRecording) {
+ startRecording(context);
+ }
+ // Check if recording needs to stop
+ if (mRecordingActive && !mRecording) {
+ stopRecording(context);
+ }
+
+ if (!mRecordingActive) return;
+
+ if (mCaptureTimeLapse) {
+ if (skipFrameAndModifyTimestamp(input.getTimestamp())) {
+ return;
+ }
+ } else {
+ mTimestampNs = input.getTimestamp();
+ }
+
+ // Activate our surface
+ glEnv.activateSurfaceWithId(mSurfaceId);
+
+ // Process
+ mProgram.process(input, mScreen);
+
+ // Set timestamp from input
+ glEnv.setSurfaceTimestamp(mTimestampNs);
+ // And swap buffers
+ glEnv.swapBuffers();
+ mNumFramesEncoded++;
+ if (mLogVerbose) Log.v(TAG, "numFramesEncoded = " + mNumFramesEncoded);
+ }
+
+ private void stopRecording(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Stopping recording");
+
+ mRecordingActive = false;
+ mNumFramesEncoded = 0;
+ GLEnvironment glEnv = context.getGLEnvironment();
+ // The following call will switch the surface_id to 0
+ // (thus, calling eglMakeCurrent on surface with id 0) and
+ // then call eglDestroy on the surface. Hence, this will
+ // call disconnect the SurfaceMediaSource, which is needed to
+ // be called before calling Stop on the mediarecorder
+ if (mLogVerbose) Log.v(TAG, String.format("Unregistering surface %d", mSurfaceId));
+ glEnv.unregisterSurfaceId(mSurfaceId);
+ try {
+ mMediaRecorder.stop();
+ } catch (RuntimeException e) {
+ throw new MediaRecorderStopException("MediaRecorder.stop() failed!", e);
+ }
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+
+ mScreen.release();
+ mScreen = null;
+
+ // Use an EffectsRecorder callback to forward a media finalization
+ // call so that it creates the video thumbnail, and whatever else needs
+ // to be done to finalize media.
+ if (mRecordingDoneListener != null) {
+ mRecordingDoneListener.onRecordingDone();
+ }
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Closing");
+ if (mRecordingActive) stopRecording(context);
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ // Release all the resources associated with the MediaRecorder
+ // and GLFrame members
+ if (mMediaRecorder != null) {
+ mMediaRecorder.release();
+ }
+ if (mScreen != null) {
+ mScreen.release();
+ }
+
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java
new file mode 100644
index 0000000..dbf9768
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videosink;
+
+import java.lang.RuntimeException;
+import android.util.Log;
+
+/** @hide **/
+public class MediaRecorderStopException extends RuntimeException {
+
+ private static final String TAG = "MediaRecorderStopException";
+
+ public MediaRecorderStopException(String msg) {
+ super(msg);
+ }
+
+ public MediaRecorderStopException() {
+ super();
+ }
+
+ public MediaRecorderStopException(String msg, Throwable t) {
+ super(msg, t);
+ }
+
+ public MediaRecorderStopException(Throwable t) {
+ super(t);
+ }
+}
+
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java
new file mode 100644
index 0000000..2c474ab
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videosrc;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CameraSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** Camera ID to use for input. Defaults to 0. */
+ @GenerateFieldPort(name = "id", hasDefault = true)
+ private int mCameraId = 0;
+
+ /** Frame width to request from camera. Actual size may not match requested. */
+ @GenerateFieldPort(name = "width", hasDefault = true)
+ private int mWidth = 320;
+
+ /** Frame height to request from camera. Actual size may not match requested. */
+ @GenerateFieldPort(name = "height", hasDefault = true)
+ private int mHeight = 240;
+
+ /** Stream framerate to request from camera. Actual frame rate may not match requested. */
+ @GenerateFieldPort(name = "framerate", hasDefault = true)
+ private int mFps = 30;
+
+ /** Whether the filter should always wait for a new frame from the camera
+ * before providing output. If set to false, the filter will keep
+ * outputting the last frame it received from the camera if multiple process
+ * calls are received before the next update from the Camera. Defaults to true.
+ */
+ @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ private Camera mCamera;
+ private GLFrame mCameraFrame;
+ private SurfaceTexture mSurfaceTexture;
+ private ShaderProgram mFrameExtractor;
+ private MutableFrameFormat mOutputFormat;
+
+ private float[] mCameraTransform;
+ private float[] mMappedCoords;
+ // These default source coordinates perform the necessary flip
+ // for converting from OpenGL origin to MFF/Bitmap origin.
+ private static final float[] mSourceCoords = { 0, 1, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 0, 0, 1 };
+
+ private static final int NEWFRAME_TIMEOUT = 100; //ms
+ private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
+
+ private boolean mNewFrameAvailable;
+
+ private Camera.Parameters mCameraParameters;
+
+ private static final String mFrameShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "CameraSource";
+
+ public CameraSource(String name) {
+ super(name);
+ mCameraTransform = new float[16];
+ mMappedCoords = new float[16];
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing");
+ // Compile shader TODO: Move to onGLEnvSomething?
+ mFrameExtractor = new ShaderProgram(context, mFrameShader);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening");
+ // Open camera
+ mCamera = Camera.open(mCameraId);
+
+ // Set parameters
+ getCameraParameters();
+ mCamera.setParameters(mCameraParameters);
+
+ // Create frame formats
+ createFormats();
+
+ // Bind it to our camera frame
+ mCameraFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+ mSurfaceTexture = new SurfaceTexture(mCameraFrame.getTextureId());
+ try {
+ mCamera.setPreviewTexture(mSurfaceTexture);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not bind camera surface texture: " +
+ e.getMessage() + "!");
+ }
+
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
+ // Start the preview
+ mNewFrameAvailable = false;
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ if (mWaitForNewFrame) {
+ int waitCount = 0;
+ while (!mNewFrameAvailable) {
+ if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
+ throw new RuntimeException("Timeout waiting for new frame");
+ }
+ try {
+ this.wait(NEWFRAME_TIMEOUT);
+ } catch (InterruptedException e) {
+ if (mLogVerbose) Log.v(TAG, "Interrupted while waiting for new frame");
+ }
+ }
+ mNewFrameAvailable = false;
+ if (mLogVerbose) Log.v(TAG, "Got new frame");
+ }
+
+ mSurfaceTexture.updateTexImage();
+
+ if (mLogVerbose) Log.v(TAG, "Using frame extractor in thread: " + Thread.currentThread());
+ mSurfaceTexture.getTransformMatrix(mCameraTransform);
+ Matrix.multiplyMM(mMappedCoords, 0,
+ mCameraTransform, 0,
+ mSourceCoords, 0);
+ mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
+ mMappedCoords[4], mMappedCoords[5],
+ mMappedCoords[8], mMappedCoords[9],
+ mMappedCoords[12], mMappedCoords[13]);
+
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mCameraFrame, output);
+
+ long timestamp = mSurfaceTexture.getTimestamp();
+ if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
+ output.setTimestamp(timestamp);
+
+ pushOutput("video", output);
+
+ // Release pushed frame
+ output.release();
+
+ if (mLogVerbose) Log.v(TAG, "Done processing new frame");
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Closing");
+
+ mCamera.release();
+ mCamera = null;
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mCameraFrame != null) {
+ mCameraFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (name.equals("framerate")) {
+ getCameraParameters();
+ int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
+ mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ mCamera.setParameters(mCameraParameters);
+ }
+ }
+
+ synchronized public Camera.Parameters getCameraParameters() {
+ boolean closeCamera = false;
+ if (mCameraParameters == null) {
+ if (mCamera == null) {
+ mCamera = Camera.open(mCameraId);
+ closeCamera = true;
+ }
+ mCameraParameters = mCamera.getParameters();
+
+ if (closeCamera) {
+ mCamera.release();
+ mCamera = null;
+ }
+ }
+
+ int closestSize[] = findClosestSize(mWidth, mHeight, mCameraParameters);
+ mWidth = closestSize[0];
+ mHeight = closestSize[1];
+ mCameraParameters.setPreviewSize(mWidth, mHeight);
+
+ int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
+
+ mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+ return mCameraParameters;
+ }
+
+ /** Update camera parameters. Image resolution cannot be changed. */
+ synchronized public void setCameraParameters(Camera.Parameters params) {
+ params.setPreviewSize(mWidth, mHeight);
+ mCameraParameters = params;
+ if (isOpen()) {
+ mCamera.setParameters(mCameraParameters);
+ }
+ }
+
+ private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
+ List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+ int closestWidth = -1;
+ int closestHeight = -1;
+ int smallestWidth = previewSizes.get(0).width;
+ int smallestHeight = previewSizes.get(0).height;
+ for (Camera.Size size : previewSizes) {
+ // Best match defined as not being larger in either dimension than
+ // the requested size, but as close as possible. The below isn't a
+ // stable selection (reording the size list can give different
+ // results), but since this is a fallback nicety, that's acceptable.
+ if ( size.width <= width &&
+ size.height <= height &&
+ size.width >= closestWidth &&
+ size.height >= closestHeight) {
+ closestWidth = size.width;
+ closestHeight = size.height;
+ }
+ if ( size.width < smallestWidth &&
+ size.height < smallestHeight) {
+ smallestWidth = size.width;
+ smallestHeight = size.height;
+ }
+ }
+ if (closestWidth == -1) {
+ // Requested size is smaller than any listed size; match with smallest possible
+ closestWidth = smallestWidth;
+ closestHeight = smallestHeight;
+ }
+
+ if (mLogVerbose) {
+ Log.v(TAG,
+ "Requested resolution: (" + width + ", " + height
+ + "). Closest match: (" + closestWidth + ", "
+ + closestHeight + ").");
+ }
+ int[] closestSize = {closestWidth, closestHeight};
+ return closestSize;
+ }
+
+ private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
+ List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
+ int[] closestRange = supportedFpsRanges.get(0);
+ for (int[] range : supportedFpsRanges) {
+ if (range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] < fps*1000 &&
+ range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] > fps*1000 &&
+ range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] >
+ closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] &&
+ range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] <
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
+ closestRange = range;
+ }
+ }
+ if (mLogVerbose) Log.v(TAG, "Requested fps: " + fps
+ + ".Closest frame rate range: ["
+ + closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.
+ + ","
+ + closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.
+ + "]");
+
+ return closestRange;
+ }
+
+ private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from camera");
+ synchronized(CameraSource.this) {
+ mNewFrameAvailable = true;
+ CameraSource.this.notify();
+ }
+ }
+ };
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
new file mode 100644
index 0000000..9c40cec
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videosrc;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.lang.IllegalArgumentException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class MediaSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** The source URL for the media source. Can be an http: link to a remote
+ * resource, or a file: link to a local media file
+ */
+ @GenerateFieldPort(name = "sourceUrl", hasDefault = true)
+ private String mSourceUrl = "";
+
+ /** An open asset file descriptor to a local media source. Default is null */
+ @GenerateFieldPort(name = "sourceAsset", hasDefault = true)
+ private AssetFileDescriptor mSourceAsset = null;
+
+ /** Whether the media source is a URL or an asset file descriptor. Defaults
+ * to false.
+ */
+ @GenerateFieldPort(name = "sourceIsUrl", hasDefault = true)
+ private boolean mSelectedIsUrl = false;
+
+ /** Whether the filter will always wait for a new video frame, or whether it
+ * will output an old frame again if a new frame isn't available. Defaults
+ * to true.
+ */
+ @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ /** Whether the media source should loop automatically or not. Defaults to
+ * true.
+ */
+ @GenerateFieldPort(name = "loop", hasDefault = true)
+ private boolean mLooping = true;
+
+ /** Volume control. Currently sound is piped directly to the speakers, so
+ * this defaults to mute.
+ */
+ @GenerateFieldPort(name = "volume", hasDefault = true)
+ private float mVolume = 0.f;
+
+ /** Orientation. This controls the output orientation of the video. Valid
+ * values are 0, 90, 180, 270
+ */
+ @GenerateFieldPort(name = "orientation", hasDefault = true)
+ private int mOrientation = 0;
+
+ private MediaPlayer mMediaPlayer;
+ private GLFrame mMediaFrame;
+ private SurfaceTexture mSurfaceTexture;
+ private ShaderProgram mFrameExtractor;
+ private MutableFrameFormat mOutputFormat;
+ private int mWidth, mHeight;
+
+ // Total timeouts will be PREP_TIMEOUT*PREP_TIMEOUT_REPEAT
+ private static final int PREP_TIMEOUT = 100; // ms
+ private static final int PREP_TIMEOUT_REPEAT = 100;
+ private static final int NEWFRAME_TIMEOUT = 100; //ms
+ private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
+
+ // This is an identity shader; not using the default identity
+ // shader because reading from a SurfaceTexture requires the
+ // GL_OES_EGL_image_external extension.
+ private final String mFrameShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ // The following transforms enable rotation of the decoded source.
+ // These are multiplied with the transform obtained from the
+ // SurfaceTexture to get the final transform to be set on the media source.
+ // Currently, given a device orientation, the MediaSource rotates in such a way
+ // that the source is displayed upright. A particular use case
+ // is "Background Replacement" feature in the Camera app
+ // where the MediaSource rotates the source to align with the camera feed and pass it
+ // on to the backdropper filter. The backdropper only does the blending
+ // and does not have to do any rotation
+ // (except for mirroring in case of front camera).
+ // TODO: Currently the rotations are spread over a bunch of stages in the
+ // pipeline. A cleaner design
+ // could be to cast away all the rotation in a separate filter or attach a transform
+ // to the frame so that MediaSource itself need not know about any rotation.
+ private static final float[] mSourceCoords_0 = { 1, 1, 0, 1,
+ 0, 1, 0, 1,
+ 1, 0, 0, 1,
+ 0, 0, 0, 1 };
+ private static final float[] mSourceCoords_270 = { 0, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 1, 0, 1,
+ 1, 0, 0, 1 };
+ private static final float[] mSourceCoords_180 = { 0, 0, 0, 1,
+ 1, 0, 0, 1,
+ 0, 1, 0, 1,
+ 1, 1, 0, 1 };
+ private static final float[] mSourceCoords_90 = { 1, 0, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 0, 1, 0, 1 };
+
+ private boolean mGotSize;
+ private boolean mPrepared;
+ private boolean mPlaying;
+ private boolean mNewFrameAvailable;
+ private boolean mOrientationUpdated;
+ private boolean mPaused;
+ private boolean mCompleted;
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "MediaSource";
+
+ public MediaSource(String name) {
+ super(name);
+ mNewFrameAvailable = false;
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing MediaSource");
+
+ mFrameExtractor = new ShaderProgram(context, mFrameShader);
+ // SurfaceTexture defines (0,0) to be bottom-left. The filter framework
+ // defines (0,0) as top-left, so do the flip here.
+ mFrameExtractor.setSourceRect(0, 1, 1, -1);
+
+ createFormats();
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) {
+ Log.v(TAG, "Opening MediaSource");
+ if (mSelectedIsUrl) {
+ Log.v(TAG, "Current URL is " + mSourceUrl);
+ } else {
+ Log.v(TAG, "Current source is Asset!");
+ }
+ }
+
+ mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(
+ mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+
+ mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
+
+ if (!setupMediaPlayer(mSelectedIsUrl)) {
+ throw new RuntimeException("Error setting up MediaPlayer!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Note: process is synchronized by its caller in the Filter base class
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ if (mMediaPlayer == null) {
+ // Something went wrong in initialization or parameter updates
+ throw new NullPointerException("Unexpected null media player!");
+ }
+
+ if (mCompleted) {
+ // Video playback is done, so close us down
+ closeOutputPort("video");
+ return;
+ }
+
+ if (!mPlaying) {
+ int waitCount = 0;
+ if (mLogVerbose) Log.v(TAG, "Waiting for preparation to complete");
+ while (!mGotSize || !mPrepared) {
+ try {
+ this.wait(PREP_TIMEOUT);
+ } catch (InterruptedException e) {
+ // ignoring
+ }
+ if (mCompleted) {
+ // Video playback is done, so close us down
+ closeOutputPort("video");
+ return;
+ }
+ waitCount++;
+ if (waitCount == PREP_TIMEOUT_REPEAT) {
+ mMediaPlayer.release();
+ throw new RuntimeException("MediaPlayer timed out while preparing!");
+ }
+ }
+ if (mLogVerbose) Log.v(TAG, "Starting playback");
+ mMediaPlayer.start();
+ }
+
+ // Use last frame if paused, unless just starting playback, in which case
+ // we want at least one valid frame before pausing
+ if (!mPaused || !mPlaying) {
+ if (mWaitForNewFrame) {
+ if (mLogVerbose) Log.v(TAG, "Waiting for new frame");
+
+ int waitCount = 0;
+ while (!mNewFrameAvailable) {
+ if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
+ if (mCompleted) {
+ // Video playback is done, so close us down
+ closeOutputPort("video");
+ return;
+ } else {
+ throw new RuntimeException("Timeout waiting for new frame!");
+ }
+ }
+ try {
+ this.wait(NEWFRAME_TIMEOUT);
+ } catch (InterruptedException e) {
+ if (mLogVerbose) Log.v(TAG, "interrupted");
+ // ignoring
+ }
+ waitCount++;
+ }
+ mNewFrameAvailable = false;
+ if (mLogVerbose) Log.v(TAG, "Got new frame");
+ }
+
+ mSurfaceTexture.updateTexImage();
+ mOrientationUpdated = true;
+ }
+ if (mOrientationUpdated) {
+ float[] surfaceTransform = new float[16];
+ mSurfaceTexture.getTransformMatrix(surfaceTransform);
+
+ float[] sourceCoords = new float[16];
+ switch (mOrientation) {
+ default:
+ case 0:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_0, 0);
+ break;
+ case 90:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_90, 0);
+ break;
+ case 180:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_180, 0);
+ break;
+ case 270:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_270, 0);
+ break;
+ }
+ if (mLogVerbose) {
+ Log.v(TAG, "OrientationHint = " + mOrientation);
+ String temp = String.format("SetSourceRegion: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f",
+ sourceCoords[4], sourceCoords[5],sourceCoords[0], sourceCoords[1],
+ sourceCoords[12], sourceCoords[13],sourceCoords[8], sourceCoords[9]);
+ Log.v(TAG, temp);
+ }
+ mFrameExtractor.setSourceRegion(sourceCoords[4], sourceCoords[5],
+ sourceCoords[0], sourceCoords[1],
+ sourceCoords[12], sourceCoords[13],
+ sourceCoords[8], sourceCoords[9]);
+ mOrientationUpdated = false;
+ }
+
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mMediaFrame, output);
+
+ long timestamp = mSurfaceTexture.getTimestamp();
+ if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
+ output.setTimestamp(timestamp);
+
+ pushOutput("video", output);
+ output.release();
+
+ mPlaying = true;
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.stop();
+ }
+ mPrepared = false;
+ mGotSize = false;
+ mPlaying = false;
+ mPaused = false;
+ mCompleted = false;
+ mNewFrameAvailable = false;
+
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ if (mLogVerbose) Log.v(TAG, "MediaSource closed");
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mMediaFrame != null) {
+ mMediaFrame.release();
+ }
+ }
+
+ // When updating the port values of the filter, users can update sourceIsUrl to switch
+ // between using URL objects or Assets.
+ // If updating only sourceUrl/sourceAsset, MediaPlayer gets reset if the current player
+ // uses Url objects/Asset.
+ // Otherwise the new sourceUrl/sourceAsset is stored and will be used when users switch
+ // sourceIsUrl next time.
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Parameter update");
+ if (name.equals("sourceUrl")) {
+ if (isOpen()) {
+ if (mLogVerbose) Log.v(TAG, "Opening new source URL");
+ if (mSelectedIsUrl) {
+ setupMediaPlayer(mSelectedIsUrl);
+ }
+ }
+ } else if (name.equals("sourceAsset") ) {
+ if (isOpen()) {
+ if (mLogVerbose) Log.v(TAG, "Opening new source FD");
+ if (!mSelectedIsUrl) {
+ setupMediaPlayer(mSelectedIsUrl);
+ }
+ }
+ } else if (name.equals("loop")) {
+ if (isOpen()) {
+ mMediaPlayer.setLooping(mLooping);
+ }
+ } else if (name.equals("sourceIsUrl")) {
+ if (isOpen()){
+ if (mSelectedIsUrl){
+ if (mLogVerbose) Log.v(TAG, "Opening new source URL");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Opening new source Asset");
+ }
+ setupMediaPlayer(mSelectedIsUrl);
+ }
+ } else if (name.equals("volume")) {
+ if (isOpen()) {
+ mMediaPlayer.setVolume(mVolume, mVolume);
+ }
+ } else if (name.equals("orientation") && mGotSize) {
+ if (mOrientation == 0 || mOrientation == 180) {
+ mOutputFormat.setDimensions(mWidth, mHeight);
+ } else {
+ mOutputFormat.setDimensions(mHeight, mWidth);
+ }
+ mOrientationUpdated = true;
+ }
+ }
+
+ synchronized public void pauseVideo(boolean pauseState) {
+ if (isOpen()) {
+ if (pauseState && !mPaused) {
+ mMediaPlayer.pause();
+ } else if (!pauseState && mPaused) {
+ mMediaPlayer.start();
+ }
+ }
+ mPaused = pauseState;
+ }
+
+ /** Creates a media player, sets it up, and calls prepare */
+ synchronized private boolean setupMediaPlayer(boolean useUrl) {
+ mPrepared = false;
+ mGotSize = false;
+ mPlaying = false;
+ mPaused = false;
+ mCompleted = false;
+ mNewFrameAvailable = false;
+
+ if (mLogVerbose) Log.v(TAG, "Setting up playback.");
+
+ if (mMediaPlayer != null) {
+ // Clean up existing media players
+ if (mLogVerbose) Log.v(TAG, "Resetting existing MediaPlayer.");
+ mMediaPlayer.reset();
+ } else {
+ // Create new media player
+ if (mLogVerbose) Log.v(TAG, "Creating new MediaPlayer.");
+ mMediaPlayer = new MediaPlayer();
+ }
+
+ if (mMediaPlayer == null) {
+ throw new RuntimeException("Unable to create a MediaPlayer!");
+ }
+
+ // Set up data sources, etc
+ try {
+ if (useUrl) {
+ if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to URI " + mSourceUrl);
+ mMediaPlayer.setDataSource(mSourceUrl);
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to asset " + mSourceAsset);
+ mMediaPlayer.setDataSource(mSourceAsset.getFileDescriptor(), mSourceAsset.getStartOffset(), mSourceAsset.getLength());
+ }
+ } catch(IOException e) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ if (useUrl) {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
+ } else {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
+ }
+ } catch(IllegalArgumentException e) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ if (useUrl) {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
+ } else {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
+ }
+ }
+
+ mMediaPlayer.setLooping(mLooping);
+ mMediaPlayer.setVolume(mVolume, mVolume);
+
+ // Bind it to our media frame
+ Surface surface = new Surface(mSurfaceTexture);
+ mMediaPlayer.setSurface(surface);
+ surface.release();
+
+ // Connect Media Player to callbacks
+
+ mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
+ mMediaPlayer.setOnPreparedListener(onPreparedListener);
+ mMediaPlayer.setOnCompletionListener(onCompletionListener);
+
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onMediaFrameAvailableListener);
+
+ if (mLogVerbose) Log.v(TAG, "Preparing MediaPlayer.");
+ mMediaPlayer.prepareAsync();
+
+ return true;
+ }
+
+ private MediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener =
+ new MediaPlayer.OnVideoSizeChangedListener() {
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ if (mLogVerbose) Log.v(TAG, "MediaPlayer sent dimensions: " + width + " x " + height);
+ if (!mGotSize) {
+ if (mOrientation == 0 || mOrientation == 180) {
+ mOutputFormat.setDimensions(width, height);
+ } else {
+ mOutputFormat.setDimensions(height, width);
+ }
+ mWidth = width;
+ mHeight = height;
+ } else {
+ if (mOutputFormat.getWidth() != width ||
+ mOutputFormat.getHeight() != height) {
+ Log.e(TAG, "Multiple video size change events received!");
+ }
+ }
+ synchronized(MediaSource.this) {
+ mGotSize = true;
+ MediaSource.this.notify();
+ }
+ }
+ };
+
+ private MediaPlayer.OnPreparedListener onPreparedListener =
+ new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ if (mLogVerbose) Log.v(TAG, "MediaPlayer is prepared");
+ synchronized(MediaSource.this) {
+ mPrepared = true;
+ MediaSource.this.notify();
+ }
+ }
+ };
+
+ private MediaPlayer.OnCompletionListener onCompletionListener =
+ new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ if (mLogVerbose) Log.v(TAG, "MediaPlayer has completed playback");
+ synchronized(MediaSource.this) {
+ mCompleted = true;
+ }
+ }
+ };
+
+ private SurfaceTexture.OnFrameAvailableListener onMediaFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from media player");
+ synchronized(MediaSource.this) {
+ if (mLogVerbose) Log.v(TAG, "New frame: notify");
+ mNewFrameAvailable = true;
+ MediaSource.this.notify();
+ if (mLogVerbose) Log.v(TAG, "New frame: notify done");
+ }
+ }
+ };
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
new file mode 100644
index 0000000..37fa242
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videosrc;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.lang.IllegalArgumentException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/** <p>A filter that converts textures from a SurfaceTexture object into frames for
+ * processing in the filter framework.</p>
+ *
+ * <p>To use, connect up the sourceListener callback, and then when executing
+ * the graph, use the SurfaceTexture object passed to the callback to feed
+ * frames into the filter graph. For example, pass the SurfaceTexture into
+ * {#link
+ * android.hardware.Camera.setPreviewTexture(android.graphics.SurfaceTexture)}.
+ * This filter is intended for applications that need for flexibility than the
+ * CameraSource and MediaSource provide. Note that the application needs to
+ * provide width and height information for the SurfaceTextureSource, which it
+ * should obtain from wherever the SurfaceTexture data is coming from to avoid
+ * unnecessary resampling.</p>
+ *
+ * @hide
+ */
+public class SurfaceTextureSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** The callback interface for the sourceListener parameter */
+ public interface SurfaceTextureSourceListener {
+ public void onSurfaceTextureSourceReady(SurfaceTexture source);
+ }
+ /** A callback to send the internal SurfaceTexture object to, once it is
+ * created. This callback will be called when the the filter graph is
+ * preparing to execute, but before any processing has actually taken
+ * place. The SurfaceTexture object passed to this callback is the only way
+ * to feed this filter. When the filter graph is shutting down, this
+ * callback will be called again with null as the source.
+ *
+ * This callback may be called from an arbitrary thread, so it should not
+ * assume it is running in the UI thread in particular.
+ */
+ @GenerateFinalPort(name = "sourceListener")
+ private SurfaceTextureSourceListener mSourceListener;
+
+ /** The width of the output image frame. If the texture width for the
+ * SurfaceTexture source is known, use it here to minimize resampling. */
+ @GenerateFieldPort(name = "width")
+ private int mWidth;
+
+ /** The height of the output image frame. If the texture height for the
+ * SurfaceTexture source is known, use it here to minimize resampling. */
+ @GenerateFieldPort(name = "height")
+ private int mHeight;
+
+ /** Whether the filter will always wait for a new frame from its
+ * SurfaceTexture, or whether it will output an old frame again if a new
+ * frame isn't available. The filter will always wait for the first frame,
+ * to avoid outputting a blank frame. Defaults to true.
+ */
+ @GenerateFieldPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ /** Maximum timeout before signaling error when waiting for a new frame. Set
+ * this to zero to disable the timeout and wait indefinitely. In milliseconds.
+ */
+ @GenerateFieldPort(name = "waitTimeout", hasDefault = true)
+ private int mWaitTimeout = 1000;
+
+ /** Whether a timeout is an exception-causing failure, or just causes the
+ * filter to close.
+ */
+ @GenerateFieldPort(name = "closeOnTimeout", hasDefault = true)
+ private boolean mCloseOnTimeout = false;
+
+ // Variables for input->output conversion
+ private GLFrame mMediaFrame;
+ private ShaderProgram mFrameExtractor;
+ private SurfaceTexture mSurfaceTexture;
+ private MutableFrameFormat mOutputFormat;
+ private ConditionVariable mNewFrameAvailable;
+ private boolean mFirstFrame;
+
+ private float[] mFrameTransform;
+ private float[] mMappedCoords;
+ // These default source coordinates perform the necessary flip
+ // for converting from MFF/Bitmap origin to OpenGL origin.
+ private static final float[] mSourceCoords = { 0, 1, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 0, 0, 1 };
+ // Shader for output
+ private final String mRenderShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ // Variables for logging
+
+ private static final String TAG = "SurfaceTextureSource";
+ private static final boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+ public SurfaceTextureSource(String name) {
+ super(name);
+ mNewFrameAvailable = new ConditionVariable();
+ mFrameTransform = new float[16];
+ mMappedCoords = new float[16];
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing SurfaceTextureSource");
+
+ createFormats();
+
+ // Prepare input
+ mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+
+ // Prepare output
+ mFrameExtractor = new ShaderProgram(context, mRenderShader);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening SurfaceTextureSource");
+ // Create SurfaceTexture anew each time - it can use substantial memory.
+ mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onFrameAvailableListener);
+ // Connect SurfaceTexture to source
+ mSourceListener.onSurfaceTextureSourceReady(mSurfaceTexture);
+ mFirstFrame = true;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ // First, get new frame if available
+ if (mWaitForNewFrame || mFirstFrame) {
+ boolean gotNewFrame;
+ if (mWaitTimeout != 0) {
+ gotNewFrame = mNewFrameAvailable.block(mWaitTimeout);
+ if (!gotNewFrame) {
+ if (!mCloseOnTimeout) {
+ throw new RuntimeException("Timeout waiting for new frame");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Timeout waiting for a new frame. Closing.");
+ closeOutputPort("video");
+ return;
+ }
+ }
+ } else {
+ mNewFrameAvailable.block();
+ }
+ mNewFrameAvailable.close();
+ mFirstFrame = false;
+ }
+
+ mSurfaceTexture.updateTexImage();
+
+ mSurfaceTexture.getTransformMatrix(mFrameTransform);
+ Matrix.multiplyMM(mMappedCoords, 0,
+ mFrameTransform, 0,
+ mSourceCoords, 0);
+ mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
+ mMappedCoords[4], mMappedCoords[5],
+ mMappedCoords[8], mMappedCoords[9],
+ mMappedCoords[12], mMappedCoords[13]);
+ // Next, render to output
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mMediaFrame, output);
+
+ output.setTimestamp(mSurfaceTexture.getTimestamp());
+
+ pushOutput("video", output);
+ output.release();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "SurfaceTextureSource closed");
+ mSourceListener.onSurfaceTextureSourceReady(null);
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mMediaFrame != null) {
+ mMediaFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (name.equals("width") || name.equals("height") ) {
+ mOutputFormat.setDimensions(mWidth, mHeight);
+ }
+ }
+
+ private SurfaceTexture.OnFrameAvailableListener onFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from SurfaceTexture");
+ mNewFrameAvailable.open();
+ }
+ };
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
new file mode 100644
index 0000000..436caab
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2011 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 android.filterpacks.videosrc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterSurfaceView;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class SurfaceTextureTarget extends Filter {
+
+ private final int RENDERMODE_STRETCH = 0;
+ private final int RENDERMODE_FIT = 1;
+ private final int RENDERMODE_FILL_CROP = 2;
+ private final int RENDERMODE_CUSTOMIZE = 3;
+
+ /** Required. Sets the destination surfaceTexture.
+ */
+ @GenerateFinalPort(name = "surfaceTexture")
+ private SurfaceTexture mSurfaceTexture;
+
+ /** Required. Sets the width of the output surfaceTexture images */
+ @GenerateFinalPort(name = "width")
+ private int mScreenWidth;
+
+ /** Required. Sets the height of the output surfaceTexture images */
+ @GenerateFinalPort(name = "height")
+ private int mScreenHeight;
+
+
+ /** Optional. Control how the incoming frames are rendered onto the
+ * output. Default is FIT.
+ * RENDERMODE_STRETCH: Just fill the output surfaceView.
+ * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
+ * have black bars.
+ * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
+ * bars. May crop.
+ */
+ @GenerateFieldPort(name = "renderMode", hasDefault = true)
+ private String mRenderModeString;
+
+ @GenerateFieldPort(name = "sourceQuad", hasDefault = true)
+ private Quad mSourceQuad = new Quad(new Point(0.0f, 1.0f),
+ new Point(1.0f, 1.0f),
+ new Point(0.0f, 0.0f),
+ new Point(1.0f, 0.0f));
+
+ @GenerateFieldPort(name = "targetQuad", hasDefault = true)
+ private Quad mTargetQuad = new Quad(new Point(0.0f, 0.0f),
+ new Point(1.0f, 0.0f),
+ new Point(0.0f, 1.0f),
+ new Point(1.0f, 1.0f));
+
+ private int mSurfaceId;
+
+ private ShaderProgram mProgram;
+ private GLFrame mScreen;
+ private int mRenderMode = RENDERMODE_FIT;
+ private float mAspectRatio = 1.f;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "SurfaceTextureTarget";
+
+ public SurfaceTextureTarget(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Make sure we have a SurfaceView
+ if (mSurfaceTexture == null) {
+ throw new RuntimeException("Null SurfaceTexture passed to SurfaceTextureTarget");
+ }
+
+ // Add input port - will accept anything that's 4-channel.
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ public void updateRenderMode() {
+ if (mRenderModeString != null) {
+ if (mRenderModeString.equals("stretch")) {
+ mRenderMode = RENDERMODE_STRETCH;
+ } else if (mRenderModeString.equals("fit")) {
+ mRenderMode = RENDERMODE_FIT;
+ } else if (mRenderModeString.equals("fill_crop")) {
+ mRenderMode = RENDERMODE_FILL_CROP;
+ } else if (mRenderModeString.equals("customize")) {
+ mRenderMode = RENDERMODE_CUSTOMIZE;
+ } else {
+ throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
+ }
+ }
+ updateTargetRect();
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ // Create identity shader to render, and make sure to render upside-down, as textures
+ // are stored internally bottom-to-top.
+ mProgram = ShaderProgram.createIdentity(context);
+ mProgram.setSourceRect(0, 1, 1, -1);
+ mProgram.setClearColor(0.0f, 0.0f, 0.0f);
+
+ updateRenderMode();
+
+ // Create a frame representing the screen
+ MutableFrameFormat screenFormat = new MutableFrameFormat(FrameFormat.TYPE_BYTE,
+ FrameFormat.TARGET_GPU);
+ screenFormat.setBytesPerSample(4);
+ screenFormat.setDimensions(mScreenWidth, mScreenHeight);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
+ GLFrame.EXISTING_FBO_BINDING,
+ 0);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ // Set up SurfaceTexture internals
+ mSurfaceId = context.getGLEnvironment().registerSurfaceTexture(mSurfaceTexture, mScreenWidth, mScreenHeight);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ GLEnvironment glEnv = context.getGLEnvironment();
+
+ // Get input frame
+ Frame input = pullInput("frame");
+ boolean createdFrame = false;
+
+ float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
+ if (currentAspectRatio != mAspectRatio) {
+ if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
+ mAspectRatio = currentAspectRatio;
+ updateTargetRect();
+ }
+
+ // See if we need to copy to GPU
+ Frame gpuFrame = null;
+ if (mLogVerbose) Log.v("SurfaceTextureTarget", "Got input format: " + input.getFormat());
+
+ int target = input.getFormat().getTarget();
+ if (target != FrameFormat.TARGET_GPU) {
+ gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
+ FrameFormat.TARGET_GPU);
+ createdFrame = true;
+ } else {
+ gpuFrame = input;
+ }
+
+ // Activate our surface
+ glEnv.activateSurfaceWithId(mSurfaceId);
+
+ // Process
+ mProgram.process(gpuFrame, mScreen);
+
+ glEnv.setSurfaceTimestamp(input.getTimestamp());
+
+ // And swap buffers
+ glEnv.swapBuffers();
+
+ if (createdFrame) {
+ gpuFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ updateRenderMode();
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mScreen != null) {
+ mScreen.release();
+ }
+ }
+
+ private void updateTargetRect() {
+ if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
+ float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
+ float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+
+ if (relativeAspectRatio == 1.0f && mRenderMode != RENDERMODE_CUSTOMIZE) {
+ mProgram.setClearsOutput(false);
+ } else {
+ switch (mRenderMode) {
+ case RENDERMODE_STRETCH:
+ mProgram.setTargetRect(0, 0, 1, 1);
+ mTargetQuad.p0.set(0f, 0.0f);
+ mTargetQuad.p1.set(1f, 0.0f);
+ mTargetQuad.p2.set(0f, 1.0f);
+ mTargetQuad.p3.set(1f, 1.0f);
+ mProgram.setClearsOutput(false);
+ break;
+ case RENDERMODE_FIT:
+ if (relativeAspectRatio > 1.0f) {
+ // Screen is wider than the camera, scale down X
+ mTargetQuad.p0.set(0.5f - 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p1.set(0.5f + 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p2.set(0.5f - 0.5f / relativeAspectRatio, 1.0f);
+ mTargetQuad.p3.set(0.5f + 0.5f / relativeAspectRatio, 1.0f);
+
+ } else {
+ // Screen is taller than the camera, scale down Y
+ mTargetQuad.p0.set(0.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p1.set(1.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p2.set(0.0f, 0.5f + 0.5f * relativeAspectRatio);
+ mTargetQuad.p3.set(1.0f, 0.5f + 0.5f * relativeAspectRatio);
+ }
+ mProgram.setClearsOutput(true);
+ break;
+ case RENDERMODE_FILL_CROP:
+ if (relativeAspectRatio > 1) {
+ // Screen is wider than the camera, crop in Y
+ mTargetQuad.p0.set(0.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p1.set(1.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p2.set(0.0f, 0.5f + 0.5f * relativeAspectRatio);
+ mTargetQuad.p3.set(1.0f, 0.5f + 0.5f * relativeAspectRatio);
+ } else {
+ // Screen is taller than the camera, crop in X
+ mTargetQuad.p0.set(0.5f - 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p1.set(0.5f + 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p2.set(0.5f - 0.5f / relativeAspectRatio, 1.0f);
+ mTargetQuad.p3.set(0.5f + 0.5f / relativeAspectRatio, 1.0f);
+ }
+ mProgram.setClearsOutput(true);
+ break;
+ case RENDERMODE_CUSTOMIZE:
+ ((ShaderProgram) mProgram).setSourceRegion(mSourceQuad);
+ break;
+ }
+ ((ShaderProgram) mProgram).setTargetRegion(mTargetQuad);
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java
new file mode 100644
index 0000000..d8fd0bd
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.videosrc;
diff --git a/media/mca/filterpacks/native/base/geometry.cpp b/media/mca/filterpacks/native/base/geometry.cpp
new file mode 100644
index 0000000..7812d50
--- /dev/null
+++ b/media/mca/filterpacks/native/base/geometry.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 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 <cutils/log.h>
+#include <cmath>
+
+#include "geometry.h"
+
+namespace android {
+namespace filterfw {
+
+float Point::Length() const {
+ return std::sqrt(x_ * x_ + y_ * y_);
+}
+
+bool Point::ScaleTo(float new_length) {
+ float length = Length();
+ if (length == 0.0f) {
+ return false;
+ }
+ x_ *= new_length / length;
+ y_ *= new_length / length;
+ return true;
+}
+
+float Point::Distance(const Point& p0, const Point& p1) {
+ Point diff = p1 - p0;
+ return diff.Length();
+}
+
+Point Point::operator+(const Point& other) const {
+ Point out;
+ out.x_ = x_ + other.x_;
+ out.y_ = y_ + other.y_;
+ return out;
+}
+
+Point Point::operator-(const Point& other) const {
+ Point out;
+ out.x_ = x_ - other.x_;
+ out.y_ = y_ - other.y_;
+ return out;
+}
+
+Point Point::operator*(float factor) const {
+ Point out;
+ out.x_ = factor * x_;
+ out.y_ = factor * y_;
+ return out;
+}
+
+void Point::Rotate90Clockwise() {
+ const float x = x_;
+ x_ = y_;
+ y_ = -x;
+}
+
+bool Rect::ExpandToAspectRatio(float ratio) {
+ if (width <= 0.0f || height <= 0.0f || ratio <= 0.0f) {
+ return false;
+ }
+
+ const float current_ratio = width / height;
+ if (current_ratio < ratio) {
+ const float dx = width * (ratio / current_ratio - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ } else {
+ const float dy = height * (current_ratio / ratio - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ }
+ return true;
+}
+
+bool Rect::ExpandToMinLength(float length) {
+ if (width <= 0.0f || height <= 0.0f || length <= 0.0f) {
+ return false;
+ }
+
+ const float current_length = width > height ? width : height;
+ if (length > current_length) {
+ const float dx = width * (length / current_length - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ const float dy = height * (length / current_length - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ }
+ return true;
+}
+
+bool Rect::ScaleWithLengthLimit(float factor, float max_length) {
+ if (width <= 0.0f || height <= 0.0f || factor <= 0.0f) {
+ return false;
+ }
+
+ const float current_length = width > height ? width : height;
+ if (current_length >= max_length) {
+ return true;
+ }
+
+ float f = factor;
+ if (current_length * f > max_length) {
+ f *= max_length / (current_length * f);
+ }
+
+ const float dx = width * (f - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ const float dy = height * (f - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ return true;
+}
+
+const Point& Quad::point(int ix) const {
+ ALOG_ASSERT(ix < static_cast<int>(points_.size()), "Access out of bounds");
+ return points_[ix];
+}
+
+bool SlantedRect::FromCenterAxisAndLengths(const Point& center,
+ const Point& vert_axis,
+ const Point& lengths) {
+ Point dy = vert_axis;
+ if (!dy.ScaleTo(lengths.y() / 2.0f)) {
+ ALOGE("Illegal axis: %f %f", vert_axis.x(), vert_axis.y());
+ return false;
+ }
+
+ Point dx = dy;
+ dx.Rotate90Clockwise();
+ dx.ScaleTo(lengths.x() / 2.0f);
+
+ points_[0] = center - dx - dy;
+ points_[1] = center + dx - dy;
+ points_[2] = center - dx + dy;
+ points_[3] = center + dx + dy;
+
+ width_ = lengths.x();
+ height_ = lengths.y();
+
+ return true;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterpacks/native/base/geometry.h b/media/mca/filterpacks/native/base/geometry.h
new file mode 100644
index 0000000..40a9343
--- /dev/null
+++ b/media/mca/filterpacks/native/base/geometry.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_FILTERFW_FILTERPACKS_BASE_GEOMETRY_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_GEOMETRY_H
+
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+// This is an initial implementation of some geometrical structures. This is
+// likely to grow and become more sophisticated in the future.
+
+class Point {
+ public:
+ Point() : x_(0.0f), y_(0.0f) {}
+ Point(float x, float y) : x_(x), y_(y) {}
+
+ float x() const { return x_; }
+ float y() const { return y_; }
+
+ float Length() const;
+ bool ScaleTo(float new_length);
+ static float Distance(const Point& p0, const Point& p1);
+
+ // Add more of these as needed:
+ Point operator+(const Point& other) const;
+ Point operator-(const Point& other) const;
+ Point operator*(float factor) const;
+
+ void Rotate90Clockwise();
+
+ private:
+ float x_, y_;
+};
+
+class Quad {
+ public:
+ Quad() : points_(4) {}
+ virtual ~Quad() {}
+
+ Quad(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
+ : points_(4) {
+ points_[0] = p0;
+ points_[1] = p1;
+ points_[2] = p2;
+ points_[3] = p3;
+ }
+
+ const std::vector<Point>& points() const { return points_; }
+ const Point& point(int ix) const;
+
+ protected:
+ std::vector<Point> points_;
+};
+
+class SlantedRect : public Quad {
+ public:
+ SlantedRect() : width_(0.0f), height_(0.0f) {}
+ virtual ~SlantedRect() {}
+
+ bool FromCenterAxisAndLengths(const Point& center,
+ const Point& vert_axis,
+ const Point& lenghts);
+
+ float width() const { return width_; }
+ float height() const { return height_; }
+
+ private:
+ float width_;
+ float height_;
+};
+
+struct Rect {
+ float x, y, width, height;
+
+ Rect() {
+ x = y = 0.0f;
+ width = height = 1.0f;
+ }
+
+ Rect(float x, float y, float width, float height) {
+ this->x = x;
+ this->y = y;
+ this->width = width;
+ this->height = height;
+ }
+
+ bool ExpandToAspectRatio(float ratio);
+ bool ExpandToMinLength(float length);
+ bool ScaleWithLengthLimit(float factor, float max_length);
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_GEOMETRY_H
diff --git a/media/mca/filterpacks/native/base/time_util.cpp b/media/mca/filterpacks/native/base/time_util.cpp
new file mode 100644
index 0000000..1a78a95
--- /dev/null
+++ b/media/mca/filterpacks/native/base/time_util.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 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 "time_util.h"
+#include "utilities.h"
+
+#include <cutils/log.h>
+#include <sys/time.h>
+#include <map>
+
+namespace android {
+namespace filterfw {
+
+uint64_t getTimeUs() {
+ static long basesec;
+ struct timeval tv;
+ uint64_t nowtime;
+ gettimeofday(&tv, 0);
+ if (basesec == 0) {
+ basesec = tv.tv_sec;
+ }
+ nowtime = (uint64_t)(tv.tv_sec - basesec) * (uint64_t)1000000 +
+ (uint64_t)tv.tv_usec;
+ return nowtime;
+}
+
+const uint64_t NamedStopWatch::kDefaultLoggingPeriodInFrames = 100;
+
+NamedStopWatch::NamedStopWatch(const std::string& name)
+ : mName(name),
+ mLoggingPeriodInFrames(kDefaultLoggingPeriodInFrames),
+ mStartUSec(0),
+ mNumCalls(0),
+ mTotalUSec(0) {
+}
+
+void NamedStopWatch::Start() {
+ mStartUSec = getTimeUs();
+}
+
+void NamedStopWatch::Stop() {
+ if (!mStartUSec) {
+ return;
+ }
+ uint64_t stopUSec = getTimeUs();
+ if (stopUSec > mStartUSec) {
+ ++mNumCalls;
+ mTotalUSec += stopUSec - mStartUSec;
+ if (mNumCalls % mLoggingPeriodInFrames == 0) {
+ const float mSec = TotalUSec() * 1.0E-3f / NumCalls();
+ ALOGE("%s: %f ms", Name().c_str(), mSec);
+ }
+ }
+ mStartUSec = 0;
+}
+
+namespace {
+static NamedStopWatch* GetWatchForName(const std::string& watch_name) {
+ // TODO: this leaks the NamedStopWatch objects. Replace it with a
+ // singleton to avoid that and make it thread safe.
+ static std::map<std::string, NamedStopWatch*> watches;
+ NamedStopWatch* watch = FindPtrOrNull(watches, watch_name);
+ if (!watch) {
+ watch = new NamedStopWatch(watch_name);
+ watches[watch_name] = watch;
+ }
+ return watch;
+};
+} // namespace
+
+ScopedTimer::ScopedTimer(const std::string& stop_watch_name) {
+ mWatch = GetWatchForName(stop_watch_name);
+ mWatch->Start();
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterpacks/native/base/time_util.h b/media/mca/filterpacks/native/base/time_util.h
new file mode 100644
index 0000000..60d76c6
--- /dev/null
+++ b/media/mca/filterpacks/native/base/time_util.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_FILTERFW_FILTERPACKS_BASE_TIME_UTIL_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_TIME_UTIL_H
+
+#include <string>
+#include <utils/RefBase.h>
+
+#define LOG_MFF_RUNNING_TIMES 0
+
+namespace android {
+namespace filterfw {
+
+uint64_t getTimeUs();
+
+class NamedStopWatch : public RefBase {
+ public:
+ static const uint64_t kDefaultLoggingPeriodInFrames;
+
+ explicit NamedStopWatch(const std::string& name);
+ void Start();
+ void Stop();
+
+ void SetName(const std::string& name) { mName = name; }
+ void SetLoggingPeriodInFrames(uint64_t numFrames) {
+ mLoggingPeriodInFrames = numFrames;
+ }
+
+ const std::string& Name() const { return mName; }
+ uint64_t NumCalls() const { return mNumCalls; }
+ uint64_t TotalUSec() const { return mTotalUSec; }
+
+ private:
+ std::string mName;
+ uint64_t mLoggingPeriodInFrames;
+ uint64_t mStartUSec;
+ uint64_t mNumCalls;
+ uint64_t mTotalUSec;
+};
+
+class ScopedTimer {
+ public:
+ explicit ScopedTimer(const std::string& stop_watch_name);
+ explicit ScopedTimer(NamedStopWatch* watch)
+ : mWatch(watch) { mWatch->Start(); }
+ ~ScopedTimer() { mWatch->Stop(); }
+
+ private:
+ NamedStopWatch* mWatch;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_TIME_UTIL_H
diff --git a/media/mca/filterpacks/native/base/utilities.h b/media/mca/filterpacks/native/base/utilities.h
new file mode 100644
index 0000000..302e177
--- /dev/null
+++ b/media/mca/filterpacks/native/base/utilities.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_FILTERFW_FILTERPACKS_BASE_UTILITIES_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_UTILITIES_H
+
+#include <set>
+#include <utility>
+
+namespace android {
+namespace filterfw {
+
+// Convenience Macro to make copy constructor and assignment operator private
+// (thereby disallowing copying and assigning).
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// STLDeleteContainerPointers()
+// For a range within a container of pointers, calls delete
+// (non-array version) on these pointers.
+// NOTE: for these three functions, we could just implement a DeleteObject
+// functor and then call for_each() on the range and functor, but this
+// requires us to pull in all of algorithm.h, which seems expensive.
+// For hash_[multi]set, it is important that this deletes behind the iterator
+// because the hash_set may call the hash function on the iterator when it is
+// advanced, which could result in the hash function trying to deference a
+// stale pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+// Given an STL container consisting of (key, value) pairs, STLDeleteValues
+// deletes all the "value" components and clears the container. Does nothing
+// in the case it's given a NULL pointer.
+template <class T>
+void STLDeleteValues(T *v) {
+ if (!v) return;
+ for (typename T::iterator i = v->begin(); i != v->end(); ++i) {
+ delete i->second;
+ }
+ v->clear();
+}
+
+// Perform a lookup in a map or hash_map.
+// If the key is present a const pointer to the associated value is returned,
+// otherwise a NULL pointer is returned.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+// A simple class that gives checklist functionality: There are essemtially two
+// operations defined on a CheckList:
+// - Adding a new (unchecked) item.
+// - Checking off an item.
+// When checking off the last remaining item CheckItem() returns true.
+template<typename T>
+class CheckList {
+ public:
+ // Add a new unchecked item. Does nothing if item is already in checklist.
+ void AddItem(const T& item);
+
+ // Check off an item in the checklist. Returns true if all items have been
+ // checked.
+ bool CheckItem(const T& item);
+
+ // Clear the checklist.
+ void Clear() {
+ items_.clear();
+ }
+
+ private:
+ std::set<T> items_;
+};
+
+template<typename T>
+void CheckList<T>::AddItem(const T& item) {
+ if (!ContainsKey(items_, item))
+ items_.insert(item);
+}
+
+template<typename T>
+bool CheckList<T>::CheckItem(const T& item) {
+ typename std::set<T>::iterator iter = items_.find(item);
+ if (iter != items_.end())
+ items_.erase(iter);
+ return items_.empty();
+}
+
+// Perform a lookup in a map or hash_map whose values are pointers.
+// If the key is present a const pointer to the associated value is returned,
+// otherwise a NULL pointer is returned.
+// This function does not distinguish between a missing key and a key mapped
+// to a NULL value.
+template <class Collection>
+const typename Collection::value_type::second_type
+FindPtrOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+// Test to see if a set, map, hash_set or hash_map contains a particular key.
+// Returns true if the key is in the collection.
+template <typename Collection, typename Key>
+bool ContainsKey(const Collection& collection, const Key& key) {
+ return collection.find(key) != collection.end();
+}
+
+// Insert a new key and value into a map or hash_map.
+// If the key is not present in the map the key and value are
+// inserted, otherwise nothing happens. True indicates that an insert
+// took place, false indicates the key was already present.
+template <class Collection, class Key, class Value>
+bool InsertIfNotPresent(Collection * const collection,
+ const Key& key, const Value& value) {
+ std::pair<typename Collection::iterator, bool> ret =
+ collection->insert(typename Collection::value_type(key, value));
+ return ret.second;
+}
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_UTILITIES_H
diff --git a/media/mca/filterpacks/native/base/vec_types.h b/media/mca/filterpacks/native/base/vec_types.h
new file mode 100644
index 0000000..65967c9
--- /dev/null
+++ b/media/mca/filterpacks/native/base/vec_types.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_FILTERFW_FILTERPACKS_BASE_VEC_TYPES_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_VEC_TYPES_H
+
+namespace android {
+namespace filterfw {
+
+template < class T, int dim>
+class VecBase {
+ public:
+ T data[dim];
+ VecBase() {}
+ VecBase<T,dim>& operator = (const VecBase<T, dim> &x) {
+ memcpy(data, x.data, sizeof(T)*dim);
+ return *this;
+ }
+ T & operator [] (int i) {
+ // out of boundary not checked
+ return data[i];
+ }
+ const T & operator [] (int i) const {
+ // out of boundary not checked
+ return data[i];
+ }
+ T Length() {
+ double sum = 0;
+ for (int i = 0; i < dim; ++i)
+ sum += static_cast<double> (data[i] * data[i]);
+ return static_cast<T>(sqrt(sum));
+ }
+};
+
+template < class T, int dim>
+class Vec : public VecBase<T,dim> {
+ public:
+ Vec() {}
+ Vec<T,dim>& operator = (const Vec<T, dim> &x) {
+ memcpy(this->data, x.data, sizeof(T)*dim);
+ return *this;
+ }
+};
+
+template <class T, int dim>
+Vec<T, dim> operator + (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] + y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator - (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] - y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator * (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] * y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator / (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] / y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+T dot(const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ T out = 0;
+ for (int i = 0; i < dim; i++)
+ out += x.data[i] * y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator * (const Vec<T,dim> &x, T scale) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] * scale;
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator / (const Vec<T,dim> &x, T scale) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] / scale;
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator + (const Vec<T,dim> &x, T val) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] + val;
+ return out;
+}
+
+// specialization for vec2, vec3, vec4 float
+template<>
+class Vec<float, 2> : public VecBase<float, 2> {
+public:
+ Vec() {}
+ Vec(float x, float y) {
+ data[0] = x;
+ data[1] = y;
+ }
+ Vec<float, 2>& operator = (const Vec<float, 2> &x) {
+ memcpy(data, x.data, sizeof(float)*2);
+ return *this;
+ }
+};
+
+template<>
+class Vec<float, 3> {
+public:
+ float data[3];
+ Vec() {}
+ Vec(float x, float y, float z) {
+ data[0] = x;
+ data[1] = y;
+ data[2] = z;
+ }
+ Vec<float, 3>& operator = (const Vec<float, 3> &x) {
+ memcpy(data, x.data, sizeof(float)*3);
+ return *this;
+ }
+};
+
+template<>
+class Vec<float, 4> {
+public:
+ float data[4];
+ Vec() {}
+ Vec(float x, float y, float z, float w) {
+ data[0] = x;
+ data[1] = y;
+ data[2] = z;
+ data[3] = w;
+ }
+ Vec<float, 4>& operator = (const Vec<float, 4> &x) {
+ memcpy(data, x.data, sizeof(float)*4);
+ return *this;
+ }
+};
+
+typedef Vec<float,2> Vec2f;
+typedef Vec<float,3> Vec3f;
+typedef Vec<float,4> Vec4f;
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_VEC_TYPES_H
diff --git a/media/mca/filterpacks/native/imageproc/brightness.c b/media/mca/filterpacks/native/imageproc/brightness.c
new file mode 100644
index 0000000..f4addf1
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/brightness.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 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 <android/log.h>
+#include <stdlib.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MCA", __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MCA", __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MCA", __VA_ARGS__)
+
+typedef struct {
+ float brightness;
+} BrightnessParameters;
+
+typedef union {
+ int value;
+ char rgba[4];
+} Pixel;
+
+void brightness_init(void** user_data) {
+ (*user_data) = malloc(sizeof(BrightnessParameters));
+}
+
+void brightness_teardown(void* user_data) {
+ free(user_data);
+}
+
+void brightness_setvalue(const char* key, const char* value, void* user_data) {
+ if (strcmp(key, "brightness") == 0)
+ ((BrightnessParameters*)user_data)->brightness = atof(value);
+ else
+ LOGE("Unknown parameter: %s!", key);
+}
+
+int brightness_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1) {
+ LOGE("Brightness: Incorrect input count! Expected 1 but got %d!", input_count);
+ return 0;
+ }
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size) {
+ LOGE("Brightness: Input-output sizes do not match up. %d vs. %d!", input_sizes[0], output_size);
+ return 0;
+ }
+
+ // Get the input and output pointers
+ const int* input_ptr = (int*)inputs[0];
+ int* output_ptr = (int*)output;
+ const int* end_ptr = input_ptr + (output_size / 4);
+ if (!input_ptr || !output_ptr) {
+ LOGE("Brightness: No input or output pointer found!");
+ return 0;
+ }
+
+ // Get the parameters
+ BrightnessParameters* params = (BrightnessParameters*)user_data;
+ const float brightness = params->brightness;
+
+ // Run the brightness adjustment
+ const int factor = (int)(brightness * 255.0f);
+ Pixel pixel;
+ while (input_ptr < end_ptr) {
+ pixel.value = *(input_ptr++);
+
+ const short r = (pixel.rgba[0] * factor) / 255;
+ const short g = (pixel.rgba[1] * factor) / 255;
+ const short b = (pixel.rgba[2] * factor) / 255;
+
+ *(output_ptr++) = (r > 255 ? 255 : r)
+ | ((g > 255 ? 255 : g) << 8)
+ | ((b > 255 ? 255 : b) << 16)
+ | (pixel.rgba[3] << 24);
+ }
+
+ return 1;
+}
+
diff --git a/media/mca/filterpacks/native/imageproc/contrast.c b/media/mca/filterpacks/native/imageproc/contrast.c
new file mode 100644
index 0000000..ea8c8d2
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/contrast.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 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 <android/log.h>
+#include <stdlib.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MCA", __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MCA", __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MCA", __VA_ARGS__)
+
+typedef struct {
+ float contrast;
+} ContrastParameters;
+
+void contrast_init(void** user_data) {
+ (*user_data) = malloc(sizeof(ContrastParameters));
+}
+
+void contrast_teardown(void* user_data) {
+ free(user_data);
+}
+
+void contrast_setvalue(const char* key, const char* value, void* user_data) {
+ if (strcmp(key, "contrast") == 0)
+ ((ContrastParameters*)user_data)->contrast = atof(value);
+ else
+ LOGE("Unknown parameter: %s!", key);
+}
+
+int contrast_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1) {
+ LOGE("Contrast: Incorrect input count! Expected 1 but got %d!", input_count);
+ return 0;
+ }
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size) {
+ LOGE("Contrast: Input-output sizes do not match up. %d vs. %d!", input_sizes[0], output_size);
+ return 0;
+ }
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr) {
+ LOGE("Contrast: No input or output pointer found!");
+ return 0;
+ }
+
+ // Get the parameters
+ ContrastParameters* params = (ContrastParameters*)user_data;
+ const float contrast = params->contrast;
+
+ // Run the contrast adjustment
+ int i;
+ for (i = 0; i < output_size; ++i) {
+ float px = *(input_ptr++) / 255.0;
+ px -= 0.5;
+ px *= contrast;
+ px += 0.5;
+ *(output_ptr++) = (char)(px > 1.0 ? 255.0 : (px < 0.0 ? 0.0 : px * 255.0));
+ }
+
+ return 1;
+}
+
diff --git a/media/mca/filterpacks/native/imageproc/invert.c b/media/mca/filterpacks/native/imageproc/invert.c
new file mode 100644
index 0000000..5938aac
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/invert.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 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 <android/log.h>
+
+int invert_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the inversion
+ int i;
+ for (i = 0; i < output_size; ++i)
+ *(output_ptr++) = 255 - *(input_ptr++);
+
+ return 1;
+}
+
diff --git a/media/mca/filterpacks/native/imageproc/to_rgba.c b/media/mca/filterpacks/native/imageproc/to_rgba.c
new file mode 100644
index 0000000..bf4db2a
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/to_rgba.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 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 <stdlib.h>
+
+int gray_to_rgb_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size/3)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < input_sizes[0]; ++i) {
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr++);
+ }
+
+ return 1;
+}
+
+int rgba_to_rgb_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0]/4 != output_size/3)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < input_sizes[0] / 4; ++i) {
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ ++input_ptr;
+ }
+
+ return 1;
+}
+
+int gray_to_rgba_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size/4)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < input_sizes[0]; ++i) {
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = 255;
+ }
+
+ return 1;
+}
+
+int rgb_to_rgba_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0]/3 != output_size/4)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < output_size / 4; ++i) {
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = 255;
+ }
+
+ return 1;
+}
+