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.support.v8.renderscript.*; 22 import android.util.Log; 23 import android.content.res.Resources; 24 import com.android.gallery3d.R; 25 import com.android.gallery3d.filtershow.cache.CachingPipeline; 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 33 public static boolean PERF_LOGGING = false; 34 35 private static ScriptC_grey mGreyConvert = null; 36 private static RenderScript mRScache = null; 37 38 private volatile boolean mResourcesLoaded = false; 39 40 protected abstract void createFilter(android.content.res.Resources res, 41 float scaleFactor, int quality); 42 43 protected void createFilter(android.content.res.Resources res, 44 float scaleFactor, int quality, Allocation in) {} 45 protected void bindScriptValues(Allocation in) {} 46 47 protected abstract void runFilter(); 48 49 protected void update(Bitmap bitmap) { 50 getOutPixelsAllocation().copyTo(bitmap); 51 } 52 53 protected RenderScript getRenderScriptContext() { 54 return CachingPipeline.getRenderScriptContext(); 55 } 56 57 protected Allocation getInPixelsAllocation() { 58 CachingPipeline pipeline = getEnvironment().getCachingPipeline(); 59 return pipeline.getInPixelsAllocation(); 60 } 61 62 protected Allocation getOutPixelsAllocation() { 63 CachingPipeline pipeline = getEnvironment().getCachingPipeline(); 64 return pipeline.getOutPixelsAllocation(); 65 } 66 67 @Override 68 public void apply(Allocation in, Allocation out) { 69 long startOverAll = System.nanoTime(); 70 long startFilter = 0; 71 long endFilter = 0; 72 if (!mResourcesLoaded) { 73 CachingPipeline pipeline = getEnvironment().getCachingPipeline(); 74 createFilter(pipeline.getResources(), getEnvironment().getScaleFactor(), 75 getEnvironment().getQuality(), in); 76 mResourcesLoaded = true; 77 } 78 startFilter = System.nanoTime(); 79 bindScriptValues(in); 80 run(in, out); 81 if (PERF_LOGGING) { 82 getRenderScriptContext().finish(); 83 endFilter = System.nanoTime(); 84 long endOverAll = System.nanoTime(); 85 String msg = String.format("%s; image size %dx%d; ", getName(), 86 in.getType().getX(), in.getType().getY()); 87 long timeOverAll = (endOverAll - startOverAll) / 1000; 88 long timeFilter = (endFilter - startFilter) / 1000; 89 msg += String.format("over all %.2f ms (%.2f FPS); ", 90 timeOverAll / 1000.f, 1000000.f / timeOverAll); 91 msg += String.format("run filter %.2f ms (%.2f FPS)", 92 timeFilter / 1000.f, 1000000.f / timeFilter); 93 Log.i(LOGTAG, msg); 94 } 95 } 96 97 protected void run(Allocation in, Allocation out) {} 98 99 @Override 100 public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { 101 if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { 102 return bitmap; 103 } 104 try { 105 CachingPipeline pipeline = getEnvironment().getCachingPipeline(); 106 if (DEBUG) { 107 Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName()); 108 } 109 Resources rsc = pipeline.getResources(); 110 boolean sizeChanged = false; 111 if (getInPixelsAllocation() != null 112 && ((getInPixelsAllocation().getType().getX() != mLastInputWidth) 113 || (getInPixelsAllocation().getType().getY() != mLastInputHeight))) { 114 sizeChanged = true; 115 } 116 if (pipeline.prepareRenderscriptAllocations(bitmap) 117 || !isResourcesLoaded() || sizeChanged) { 118 freeResources(); 119 createFilter(rsc, scaleFactor, quality); 120 setResourcesLoaded(true); 121 mLastInputWidth = getInPixelsAllocation().getType().getX(); 122 mLastInputHeight = getInPixelsAllocation().getType().getY(); 123 } 124 bindScriptValues(); 125 runFilter(); 126 update(bitmap); 127 if (DEBUG) { 128 Log.v(LOGTAG, "DONE apply filter " + getName() + " in pipeline " + pipeline.getName()); 129 } 130 } catch (android.renderscript.RSIllegalArgumentException e) { 131 Log.e(LOGTAG, "Illegal argument? " + e); 132 } catch (android.renderscript.RSRuntimeException e) { 133 Log.e(LOGTAG, "RS runtime exception ? " + e); 134 } catch (java.lang.OutOfMemoryError e) { 135 // Many of the renderscript filters allocated large (>16Mb resources) in order to apply. 136 System.gc(); 137 displayLowMemoryToast(); 138 Log.e(LOGTAG, "not enough memory for filter " + getName(), e); 139 } 140 141 return bitmap; 142 } 143 144 protected static Allocation convertBitmap(Bitmap bitmap) { 145 return Allocation.createFromBitmap(CachingPipeline.getRenderScriptContext(), bitmap, 146 Allocation.MipmapControl.MIPMAP_NONE, 147 Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE); 148 } 149 150 private static Allocation convertRGBAtoA(Bitmap bitmap) { 151 RenderScript RS = CachingPipeline.getRenderScriptContext(); 152 if (RS != mRScache || mGreyConvert == null) { 153 mGreyConvert = new ScriptC_grey(RS, RS.getApplicationContext().getResources(), 154 R.raw.grey); 155 mRScache = RS; 156 } 157 158 Type.Builder tb_a8 = new Type.Builder(RS, Element.A_8(RS)); 159 160 Allocation bitmapTemp = convertBitmap(bitmap); 161 if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(RS))) { 162 return bitmapTemp; 163 } 164 165 tb_a8.setX(bitmapTemp.getType().getX()); 166 tb_a8.setY(bitmapTemp.getType().getY()); 167 Allocation bitmapAlloc = Allocation.createTyped(RS, tb_a8.create(), 168 Allocation.MipmapControl.MIPMAP_NONE, 169 Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE); 170 mGreyConvert.forEach_RGBAtoA(bitmapTemp, bitmapAlloc); 171 bitmapTemp.destroy(); 172 return bitmapAlloc; 173 } 174 175 public Allocation loadScaledResourceAlpha(int resource, int inSampleSize) { 176 Resources res = CachingPipeline.getResources(); 177 final BitmapFactory.Options options = new BitmapFactory.Options(); 178 options.inPreferredConfig = Bitmap.Config.ALPHA_8; 179 options.inSampleSize = inSampleSize; 180 Bitmap bitmap = BitmapFactory.decodeResource( 181 res, 182 resource, options); 183 Allocation ret = convertRGBAtoA(bitmap); 184 bitmap.recycle(); 185 return ret; 186 } 187 188 public Allocation loadScaledResourceAlpha(int resource, int w, int h, int inSampleSize) { 189 Resources res = CachingPipeline.getResources(); 190 final BitmapFactory.Options options = new BitmapFactory.Options(); 191 options.inPreferredConfig = Bitmap.Config.ALPHA_8; 192 options.inSampleSize = inSampleSize; 193 Bitmap bitmap = BitmapFactory.decodeResource( 194 res, 195 resource, options); 196 Bitmap resizeBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); 197 Allocation ret = convertRGBAtoA(resizeBitmap); 198 resizeBitmap.recycle(); 199 bitmap.recycle(); 200 return ret; 201 } 202 203 public Allocation loadResourceAlpha(int resource) { 204 return loadScaledResourceAlpha(resource, 1); 205 } 206 207 public Allocation loadResource(int resource) { 208 Resources res = CachingPipeline.getResources(); 209 final BitmapFactory.Options options = new BitmapFactory.Options(); 210 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 211 Bitmap bitmap = BitmapFactory.decodeResource( 212 res, 213 resource, options); 214 Allocation ret = convertBitmap(bitmap); 215 bitmap.recycle(); 216 return ret; 217 } 218 219 private boolean isResourcesLoaded() { 220 return mResourcesLoaded; 221 } 222 223 private void setResourcesLoaded(boolean resourcesLoaded) { 224 mResourcesLoaded = resourcesLoaded; 225 } 226 227 /** 228 * Bitmaps and RS Allocations should be cleared here 229 */ 230 abstract protected void resetAllocations(); 231 232 /** 233 * RS Script objects (and all other RS objects) should be cleared here 234 */ 235 abstract protected void resetScripts(); 236 237 /** 238 * Scripts values should be bound here 239 */ 240 abstract protected void bindScriptValues(); 241 242 public void freeResources() { 243 if (!isResourcesLoaded()) { 244 return; 245 } 246 resetAllocations(); 247 setResourcesLoaded(false); 248 } 249 } 250