ImageFilterRS.java revision 180277924bebf6b600ee5ce5adf9ff807a038570
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
17package com.android.gallery3d.filtershow.filters;
18
19import android.app.Activity;
20import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.support.v8.renderscript.*;
23import android.util.Log;
24import android.content.res.Resources;
25import com.android.gallery3d.R;
26import com.android.gallery3d.filtershow.cache.CachingPipeline;
27
28public abstract class ImageFilterRS extends ImageFilter {
29    private static final String LOGTAG = "ImageFilterRS";
30    private boolean DEBUG = false;
31
32    private static volatile RenderScript sRS = null;
33    private static volatile Resources sResources = null;
34    private volatile boolean mResourcesLoaded = false;
35
36    // This must be used inside block synchronized on ImageFilterRS class object
37    protected abstract void createFilter(android.content.res.Resources res,
38            float scaleFactor, int quality);
39
40    // This must be used inside block synchronized on ImageFilterRS class object
41    protected abstract void runFilter();
42
43    // This must be used inside block synchronized on ImageFilterRS class object
44    protected void update(Bitmap bitmap) {
45        getOutPixelsAllocation().copyTo(bitmap);
46    }
47
48    protected Allocation getInPixelsAllocation() {
49        CachingPipeline pipeline = getEnvironment().getCachingPipeline();
50        return pipeline.getInPixelsAllocation();
51    }
52
53    protected Allocation getOutPixelsAllocation() {
54        CachingPipeline pipeline = getEnvironment().getCachingPipeline();
55        return pipeline.getOutPixelsAllocation();
56    }
57
58    @Override
59    public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) {
60        if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) {
61            return bitmap;
62        }
63        try {
64            synchronized(ImageFilterRS.class) {
65                if (sRS == null)  {
66                    Log.w(LOGTAG, "Cannot apply before calling createRenderScriptContext");
67                    return bitmap;
68                }
69                CachingPipeline pipeline = getEnvironment().getCachingPipeline();
70                if (DEBUG) {
71                    Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName());
72                }
73                boolean needsUpdate = pipeline.prepareRenderscriptAllocations(bitmap);
74                if (needsUpdate || !isResourcesLoaded()) {
75                    // the allocations changed size
76                    freeResources();
77                    createFilter(sResources, scaleFactor, quality);
78                    setResourcesLoaded(true);
79                }
80                runFilter();
81                update(bitmap);
82                if (DEBUG) {
83                    Log.v(LOGTAG, "DONE apply filter " + getName() + " in pipeline " + pipeline.getName());
84                }
85            }
86        } catch (android.renderscript.RSIllegalArgumentException e) {
87            Log.e(LOGTAG, "Illegal argument? " + e);
88        } catch (android.renderscript.RSRuntimeException e) {
89            Log.e(LOGTAG, "RS runtime exception ? " + e);
90        } catch (java.lang.OutOfMemoryError e) {
91            // Many of the renderscript filters allocated large (>16Mb resources) in order to apply.
92            System.gc();
93            displayLowMemoryToast();
94            Log.e(LOGTAG, "not enough memory for filter " + getName(), e);
95        }
96        return bitmap;
97    }
98
99    public static synchronized RenderScript getRenderScriptContext() {
100        return sRS;
101    }
102
103    public static synchronized void createRenderscriptContext(Activity context) {
104        if( sRS != null) {
105            Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
106            destroyRenderScriptContext();
107        }
108        sRS = RenderScript.create(context);
109        sResources = context.getResources();
110    }
111
112    public static synchronized void destroyRenderScriptContext() {
113        sRS.destroy();
114        sRS = null;
115        sResources = null;
116    }
117
118    private static synchronized Allocation convertBitmap(Bitmap bitmap) {
119        return Allocation.createFromBitmap(sRS, bitmap,
120                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
121    }
122
123    private static synchronized Allocation convertRGBAtoA(Bitmap bitmap) {
124        Type.Builder tb_a8 = new Type.Builder(sRS, Element.A_8(sRS));
125        ScriptC_grey greyConvert = new ScriptC_grey(sRS,
126                sRS.getApplicationContext().getResources(), R.raw.grey);
127
128        Allocation bitmapTemp = convertBitmap(bitmap);
129        if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(sRS))) {
130            return bitmapTemp;
131        }
132
133        tb_a8.setX(bitmapTemp.getType().getX());
134        tb_a8.setY(bitmapTemp.getType().getY());
135        Allocation bitmapAlloc = Allocation.createTyped(sRS, tb_a8.create());
136        greyConvert.forEach_RGBAtoA(bitmapTemp, bitmapAlloc);
137
138        return bitmapAlloc;
139    }
140
141    public Allocation loadScaledResourceAlpha(int resource, int inSampleSize) {
142        Resources res = null;
143        synchronized(ImageFilterRS.class) {
144            res = sRS.getApplicationContext().getResources();
145        }
146        final BitmapFactory.Options options = new BitmapFactory.Options();
147        options.inPreferredConfig = Bitmap.Config.ALPHA_8;
148        options.inSampleSize      = inSampleSize;
149        Bitmap bitmap = BitmapFactory.decodeResource(
150                res,
151                resource, options);
152        Allocation ret = convertRGBAtoA(bitmap);
153        bitmap.recycle();
154        return ret;
155    }
156
157    public Allocation loadResourceAlpha(int resource) {
158        return loadScaledResourceAlpha(resource, 1);
159    }
160
161    public Allocation loadResource(int resource) {
162        Resources res = null;
163        synchronized(ImageFilterRS.class) {
164            res = sRS.getApplicationContext().getResources();
165        }
166        final BitmapFactory.Options options = new BitmapFactory.Options();
167        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
168        Bitmap bitmap = BitmapFactory.decodeResource(
169                res,
170                resource, options);
171        Allocation ret = convertBitmap(bitmap);
172        bitmap.recycle();
173        return ret;
174    }
175
176    private boolean isResourcesLoaded() {
177        return mResourcesLoaded;
178    }
179
180    private void setResourcesLoaded(boolean resourcesLoaded) {
181        mResourcesLoaded = resourcesLoaded;
182    }
183
184    /**
185     *  Bitmaps and RS Allocations should be cleared here
186     */
187    abstract protected void resetAllocations();
188
189    /**
190     * RS Script objects (and all other RS objects) should be cleared here
191     */
192    abstract protected void resetScripts();
193
194    public void freeResources() {
195        if (!isResourcesLoaded()) {
196            return;
197        }
198        synchronized(ImageFilterRS.class) {
199            resetAllocations();
200            setResourcesLoaded(false);
201        }
202    }
203}
204