ImageFilterRS.java revision 0333247337b910bb981c6ba17ca0cc76d5b614ff
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.graphics.Bitmap;
20import android.graphics.BitmapFactory;
21import android.support.v8.renderscript.*;
22import android.util.Log;
23import android.content.res.Resources;
24import com.android.gallery3d.R;
25import com.android.gallery3d.filtershow.cache.CachingPipeline;
26
27public 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