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.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