blob: 3c54456b69fe5b51b793f449773823c50696ebde [file] [log] [blame]
/*
* 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.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);
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.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.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);
}
}