summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java')
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
new file mode 100644
index 000000000..5695ef53e
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.filtershow.filters;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.content.res.Resources;
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.pipeline.PipelineInterface;
+
+public abstract class ImageFilterRS extends ImageFilter {
+ private static final String LOGTAG = "ImageFilterRS";
+ private boolean DEBUG = false;
+ private int mLastInputWidth = 0;
+ private int mLastInputHeight = 0;
+ private long mLastTimeCalled;
+
+ public static boolean PERF_LOGGING = false;
+
+ private static ScriptC_grey mGreyConvert = null;
+ private static RenderScript mRScache = null;
+
+ private volatile boolean mResourcesLoaded = false;
+
+ protected abstract void createFilter(android.content.res.Resources res,
+ float scaleFactor, int quality);
+
+ protected void createFilter(android.content.res.Resources res,
+ float scaleFactor, int quality, Allocation in) {}
+ protected void bindScriptValues(Allocation in) {}
+
+ protected abstract void runFilter();
+
+ protected void update(Bitmap bitmap) {
+ getOutPixelsAllocation().copyTo(bitmap);
+ }
+
+ protected RenderScript getRenderScriptContext() {
+ PipelineInterface pipeline = getEnvironment().getPipeline();
+ return pipeline.getRSContext();
+ }
+
+ protected Allocation getInPixelsAllocation() {
+ PipelineInterface pipeline = getEnvironment().getPipeline();
+ return pipeline.getInPixelsAllocation();
+ }
+
+ protected Allocation getOutPixelsAllocation() {
+ PipelineInterface pipeline = getEnvironment().getPipeline();
+ return pipeline.getOutPixelsAllocation();
+ }
+
+ @Override
+ public void apply(Allocation in, Allocation out) {
+ long startOverAll = System.nanoTime();
+ if (PERF_LOGGING) {
+ long delay = (startOverAll - mLastTimeCalled) / 1000;
+ String msg = String.format("%s; image size %dx%d; ", getName(),
+ in.getType().getX(), in.getType().getY());
+ msg += String.format("called after %.2f ms (%.2f FPS); ",
+ delay / 1000.f, 1000000.f / delay);
+ Log.i(LOGTAG, msg);
+ }
+ mLastTimeCalled = startOverAll;
+ long startFilter = 0;
+ long endFilter = 0;
+ if (!mResourcesLoaded) {
+ PipelineInterface pipeline = getEnvironment().getPipeline();
+ createFilter(pipeline.getResources(), getEnvironment().getScaleFactor(),
+ getEnvironment().getQuality(), in);
+ mResourcesLoaded = true;
+ }
+ startFilter = System.nanoTime();
+ bindScriptValues(in);
+ run(in, out);
+ if (PERF_LOGGING) {
+ getRenderScriptContext().finish();
+ endFilter = System.nanoTime();
+ long endOverAll = System.nanoTime();
+ String msg = String.format("%s; image size %dx%d; ", getName(),
+ in.getType().getX(), in.getType().getY());
+ long timeOverAll = (endOverAll - startOverAll) / 1000;
+ long timeFilter = (endFilter - startFilter) / 1000;
+ msg += String.format("over all %.2f ms (%.2f FPS); ",
+ timeOverAll / 1000.f, 1000000.f / timeOverAll);
+ msg += String.format("run filter %.2f ms (%.2f FPS)",
+ timeFilter / 1000.f, 1000000.f / timeFilter);
+ Log.i(LOGTAG, msg);
+ }
+ }
+
+ protected void run(Allocation in, Allocation out) {}
+
+ @Override
+ public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) {
+ if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) {
+ return bitmap;
+ }
+ try {
+ PipelineInterface pipeline = getEnvironment().getPipeline();
+ if (DEBUG) {
+ Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName());
+ }
+ Resources rsc = pipeline.getResources();
+ boolean sizeChanged = false;
+ if (getInPixelsAllocation() != null
+ && ((getInPixelsAllocation().getType().getX() != mLastInputWidth)
+ || (getInPixelsAllocation().getType().getY() != mLastInputHeight))) {
+ sizeChanged = true;
+ }
+ if (pipeline.prepareRenderscriptAllocations(bitmap)
+ || !isResourcesLoaded() || sizeChanged) {
+ freeResources();
+ createFilter(rsc, scaleFactor, quality);
+ setResourcesLoaded(true);
+ mLastInputWidth = getInPixelsAllocation().getType().getX();
+ mLastInputHeight = getInPixelsAllocation().getType().getY();
+ }
+ bindScriptValues();
+ runFilter();
+ update(bitmap);
+ if (DEBUG) {
+ Log.v(LOGTAG, "DONE apply filter " + getName() + " in pipeline " + pipeline.getName());
+ }
+ } catch (android.renderscript.RSIllegalArgumentException e) {
+ Log.e(LOGTAG, "Illegal argument? " + e);
+ } catch (android.renderscript.RSRuntimeException e) {
+ Log.e(LOGTAG, "RS runtime exception ? " + e);
+ } catch (java.lang.OutOfMemoryError e) {
+ // Many of the renderscript filters allocated large (>16Mb resources) in order to apply.
+ System.gc();
+ displayLowMemoryToast();
+ Log.e(LOGTAG, "not enough memory for filter " + getName(), e);
+ }
+ return bitmap;
+ }
+
+ protected static Allocation convertBitmap(RenderScript RS, Bitmap bitmap) {
+ return Allocation.createFromBitmap(RS, bitmap,
+ Allocation.MipmapControl.MIPMAP_NONE,
+ Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE);
+ }
+
+ private static Allocation convertRGBAtoA(RenderScript RS, Bitmap bitmap) {
+ if (RS != mRScache || mGreyConvert == null) {
+ mGreyConvert = new ScriptC_grey(RS, RS.getApplicationContext().getResources(),
+ R.raw.grey);
+ mRScache = RS;
+ }
+
+ Type.Builder tb_a8 = new Type.Builder(RS, Element.A_8(RS));
+
+ Allocation bitmapTemp = convertBitmap(RS, bitmap);
+ if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(RS))) {
+ return bitmapTemp;
+ }
+
+ tb_a8.setX(bitmapTemp.getType().getX());
+ tb_a8.setY(bitmapTemp.getType().getY());
+ Allocation bitmapAlloc = Allocation.createTyped(RS, tb_a8.create(),
+ Allocation.MipmapControl.MIPMAP_NONE,
+ Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE);
+ mGreyConvert.forEach_RGBAtoA(bitmapTemp, bitmapAlloc);
+ bitmapTemp.destroy();
+ return bitmapAlloc;
+ }
+
+ public Allocation loadScaledResourceAlpha(int resource, int inSampleSize) {
+ Resources res = getEnvironment().getPipeline().getResources();
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ALPHA_8;
+ options.inSampleSize = inSampleSize;
+ Bitmap bitmap = BitmapFactory.decodeResource(
+ res,
+ resource, options);
+ Allocation ret = convertRGBAtoA(getRenderScriptContext(), bitmap);
+ bitmap.recycle();
+ return ret;
+ }
+
+ public Allocation loadScaledResourceAlpha(int resource, int w, int h, int inSampleSize) {
+ Resources res = getEnvironment().getPipeline().getResources();
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ALPHA_8;
+ options.inSampleSize = inSampleSize;
+ Bitmap bitmap = BitmapFactory.decodeResource(
+ res,
+ resource, options);
+ Bitmap resizeBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+ Allocation ret = convertRGBAtoA(getRenderScriptContext(), resizeBitmap);
+ resizeBitmap.recycle();
+ bitmap.recycle();
+ return ret;
+ }
+
+ public Allocation loadResourceAlpha(int resource) {
+ return loadScaledResourceAlpha(resource, 1);
+ }
+
+ public Allocation loadResource(int resource) {
+ Resources res = getEnvironment().getPipeline().getResources();
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap bitmap = BitmapFactory.decodeResource(
+ res,
+ resource, options);
+ Allocation ret = convertBitmap(getRenderScriptContext(), bitmap);
+ bitmap.recycle();
+ return ret;
+ }
+
+ private boolean isResourcesLoaded() {
+ return mResourcesLoaded;
+ }
+
+ private void setResourcesLoaded(boolean resourcesLoaded) {
+ mResourcesLoaded = resourcesLoaded;
+ }
+
+ /**
+ * Bitmaps and RS Allocations should be cleared here
+ */
+ abstract protected void resetAllocations();
+
+ /**
+ * RS Script objects (and all other RS objects) should be cleared here
+ */
+ public abstract void resetScripts();
+
+ /**
+ * Scripts values should be bound here
+ */
+ abstract protected void bindScriptValues();
+
+ public void freeResources() {
+ if (!isResourcesLoaded()) {
+ return;
+ }
+ resetAllocations();
+ mLastInputWidth = 0;
+ mLastInputHeight = 0;
+ setResourcesLoaded(false);
+ }
+}