1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.gallery3d.filtershow.filters; 18 19 import android.graphics.Bitmap; 20 import android.graphics.BitmapFactory; 21 import android.renderscript.*; 22 import android.util.Log; 23 import android.content.res.Resources; 24 import com.android.gallery3d.R; 25 import com.android.gallery3d.filtershow.pipeline.PipelineInterface; 26 27 public abstract class ImageFilterRS extends ImageFilter { 28 private static final String LOGTAG = "ImageFilterRS"; 29 private boolean DEBUG = false; 30 private int mLastInputWidth = 0; 31 private int mLastInputHeight = 0; 32 private long mLastTimeCalled; 33 34 public static boolean PERF_LOGGING = false; 35 36 private static ScriptC_grey mGreyConvert = null; 37 private static RenderScript mRScache = null; 38 39 private volatile boolean mResourcesLoaded = false; 40 41 protected abstract void createFilter(android.content.res.Resources res, 42 float scaleFactor, int quality); 43 44 protected void createFilter(android.content.res.Resources res, 45 float scaleFactor, int quality, Allocation in) {} 46 protected void bindScriptValues(Allocation in) {} 47 48 protected abstract void runFilter(); 49 50 protected void update(Bitmap bitmap) { 51 getOutPixelsAllocation().copyTo(bitmap); 52 } 53 54 protected RenderScript getRenderScriptContext() { 55 PipelineInterface pipeline = getEnvironment().getPipeline(); 56 return pipeline.getRSContext(); 57 } 58 59 protected Allocation getInPixelsAllocation() { 60 PipelineInterface pipeline = getEnvironment().getPipeline(); 61 return pipeline.getInPixelsAllocation(); 62 } 63 64 protected Allocation getOutPixelsAllocation() { 65 PipelineInterface pipeline = getEnvironment().getPipeline(); 66 return pipeline.getOutPixelsAllocation(); 67 } 68 69 @Override 70 public void apply(Allocation in, Allocation out) { 71 long startOverAll = System.nanoTime(); 72 if (PERF_LOGGING) { 73 long delay = (startOverAll - mLastTimeCalled) / 1000; 74 String msg = String.format("%s; image size %dx%d; ", getName(), 75 in.getType().getX(), in.getType().getY()); 76 msg += String.format("called after %.2f ms (%.2f FPS); ", 77 delay / 1000.f, 1000000.f / delay); 78 Log.i(LOGTAG, msg); 79 } 80 mLastTimeCalled = startOverAll; 81 long startFilter = 0; 82 long endFilter = 0; 83 if (!mResourcesLoaded) { 84 PipelineInterface pipeline = getEnvironment().getPipeline(); 85 createFilter(pipeline.getResources(), getEnvironment().getScaleFactor(), 86 getEnvironment().getQuality(), in); 87 mResourcesLoaded = true; 88 } 89 startFilter = System.nanoTime(); 90 bindScriptValues(in); 91 run(in, out); 92 if (PERF_LOGGING) { 93 getRenderScriptContext().finish(); 94 endFilter = System.nanoTime(); 95 long endOverAll = System.nanoTime(); 96 String msg = String.format("%s; image size %dx%d; ", getName(), 97 in.getType().getX(), in.getType().getY()); 98 long timeOverAll = (endOverAll - startOverAll) / 1000; 99 long timeFilter = (endFilter - startFilter) / 1000; 100 msg += String.format("over all %.2f ms (%.2f FPS); ", 101 timeOverAll / 1000.f, 1000000.f / timeOverAll); 102 msg += String.format("run filter %.2f ms (%.2f FPS)", 103 timeFilter / 1000.f, 1000000.f / timeFilter); 104 Log.i(LOGTAG, msg); 105 } 106 } 107 108 protected void run(Allocation in, Allocation out) {} 109 110 @Override 111 public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { 112 if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { 113 return bitmap; 114 } 115 try { 116 PipelineInterface pipeline = getEnvironment().getPipeline(); 117 if (DEBUG) { 118 Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName()); 119 } 120 Resources rsc = pipeline.getResources(); 121 boolean sizeChanged = false; 122 if (getInPixelsAllocation() != null 123 && ((getInPixelsAllocation().getType().getX() != mLastInputWidth) 124 || (getInPixelsAllocation().getType().getY() != mLastInputHeight))) { 125 sizeChanged = true; 126 } 127 if (pipeline.prepareRenderscriptAllocations(bitmap) 128 || !isResourcesLoaded() || sizeChanged) { 129 freeResources(); 130 createFilter(rsc, scaleFactor, quality); 131 setResourcesLoaded(true); 132 mLastInputWidth = getInPixelsAllocation().getType().getX(); 133 mLastInputHeight = getInPixelsAllocation().getType().getY(); 134 } 135 bindScriptValues(); 136 runFilter(); 137 update(bitmap); 138 if (DEBUG) { 139 Log.v(LOGTAG, "DONE apply filter " + getName() + " in pipeline " + pipeline.getName()); 140 } 141 } catch (android.renderscript.RSIllegalArgumentException e) { 142 Log.e(LOGTAG, "Illegal argument? " + e); 143 } catch (android.renderscript.RSRuntimeException e) { 144 Log.e(LOGTAG, "RS runtime exception ? " + e); 145 } catch (java.lang.OutOfMemoryError e) { 146 // Many of the renderscript filters allocated large (>16Mb resources) in order to apply. 147 System.gc(); 148 displayLowMemoryToast(); 149 Log.e(LOGTAG, "not enough memory for filter " + getName(), e); 150 } 151 return bitmap; 152 } 153 154 protected static Allocation convertBitmap(RenderScript RS, Bitmap bitmap) { 155 return Allocation.createFromBitmap(RS, bitmap, 156 Allocation.MipmapControl.MIPMAP_NONE, 157 Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE); 158 } 159 160 private static Allocation convertRGBAtoA(RenderScript RS, Bitmap bitmap) { 161 if (RS != mRScache || mGreyConvert == null) { 162 mGreyConvert = new ScriptC_grey(RS); 163 mRScache = RS; 164 } 165 166 Type.Builder tb_a8 = new Type.Builder(RS, Element.A_8(RS)); 167 168 Allocation bitmapTemp = convertBitmap(RS, bitmap); 169 if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(RS))) { 170 return bitmapTemp; 171 } 172 173 tb_a8.setX(bitmapTemp.getType().getX()); 174 tb_a8.setY(bitmapTemp.getType().getY()); 175 Allocation bitmapAlloc = Allocation.createTyped(RS, tb_a8.create(), 176 Allocation.MipmapControl.MIPMAP_NONE, 177 Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE); 178 mGreyConvert.forEach_RGBAtoA(bitmapTemp, bitmapAlloc); 179 bitmapTemp.destroy(); 180 return bitmapAlloc; 181 } 182 183 public Allocation loadScaledResourceAlpha(int resource, int inSampleSize) { 184 Resources res = getEnvironment().getPipeline().getResources(); 185 final BitmapFactory.Options options = new BitmapFactory.Options(); 186 options.inSampleSize = inSampleSize; 187 Bitmap bitmap = BitmapFactory.decodeResource( 188 res, 189 resource, options); 190 Allocation ret = convertRGBAtoA(getRenderScriptContext(), bitmap); 191 bitmap.recycle(); 192 return ret; 193 } 194 195 public Allocation loadScaledResourceAlpha(int resource, int w, int h, int inSampleSize) { 196 Resources res = getEnvironment().getPipeline().getResources(); 197 final BitmapFactory.Options options = new BitmapFactory.Options(); 198 options.inSampleSize = inSampleSize; 199 Bitmap bitmap = BitmapFactory.decodeResource( 200 res, 201 resource, options); 202 Bitmap resizeBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); 203 Allocation ret = convertRGBAtoA(getRenderScriptContext(), resizeBitmap); 204 resizeBitmap.recycle(); 205 bitmap.recycle(); 206 return ret; 207 } 208 209 public Allocation loadResourceAlpha(int resource) { 210 return loadScaledResourceAlpha(resource, 1); 211 } 212 213 public Allocation loadResource(int resource) { 214 Resources res = getEnvironment().getPipeline().getResources(); 215 final BitmapFactory.Options options = new BitmapFactory.Options(); 216 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 217 Bitmap bitmap = BitmapFactory.decodeResource( 218 res, 219 resource, options); 220 Allocation ret = convertBitmap(getRenderScriptContext(), bitmap); 221 bitmap.recycle(); 222 return ret; 223 } 224 225 private boolean isResourcesLoaded() { 226 return mResourcesLoaded; 227 } 228 229 private void setResourcesLoaded(boolean resourcesLoaded) { 230 mResourcesLoaded = resourcesLoaded; 231 } 232 233 /** 234 * Bitmaps and RS Allocations should be cleared here 235 */ 236 abstract protected void resetAllocations(); 237 238 /** 239 * RS Script objects (and all other RS objects) should be cleared here 240 */ 241 public abstract void resetScripts(); 242 243 /** 244 * Scripts values should be bound here 245 */ 246 abstract protected void bindScriptValues(); 247 248 public void freeResources() { 249 if (!isResourcesLoaded()) { 250 return; 251 } 252 resetAllocations(); 253 mLastInputWidth = 0; 254 mLastInputHeight = 0; 255 setResourcesLoaded(false); 256 } 257 } 258