Home | History | Annotate | Download | only in filters
      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