CachingPipeline.java revision bed4ef971605f343785c3f4c244a1ada51d764cd
1/*
2 * Copyright (C) 2013 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.pipeline;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.graphics.Bitmap;
22import android.graphics.Canvas;
23import android.graphics.Matrix;
24import android.graphics.Paint;
25import android.graphics.Rect;
26import android.graphics.RectF;
27import android.renderscript.Allocation;
28import android.renderscript.RenderScript;
29import android.util.Log;
30
31import com.android.gallery3d.filtershow.cache.BitmapCache;
32import com.android.gallery3d.filtershow.cache.ImageLoader;
33import com.android.gallery3d.filtershow.filters.FilterRepresentation;
34import com.android.gallery3d.filtershow.filters.FiltersManager;
35import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
36import com.android.gallery3d.filtershow.imageshow.MasterImage;
37
38import java.util.Vector;
39
40public class CachingPipeline implements PipelineInterface {
41    private static final String LOGTAG = "CachingPipeline";
42    private boolean DEBUG = false;
43
44    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
45
46    private static volatile RenderScript sRS = null;
47
48    private FiltersManager mFiltersManager = null;
49    private volatile Bitmap mOriginalBitmap = null;
50    private volatile Bitmap mResizedOriginalBitmap = null;
51
52    private FilterEnvironment mEnvironment = new FilterEnvironment();
53    private CacheProcessing mCachedProcessing = new CacheProcessing();
54
55
56    private volatile Allocation mOriginalAllocation = null;
57    private volatile Allocation mFiltersOnlyOriginalAllocation =  null;
58
59    protected volatile Allocation mInPixelsAllocation;
60    protected volatile Allocation mOutPixelsAllocation;
61    private volatile int mWidth = 0;
62    private volatile int mHeight = 0;
63
64    private volatile float mPreviewScaleFactor = 1.0f;
65    private volatile float mHighResPreviewScaleFactor = 1.0f;
66    private volatile String mName = "";
67
68    public CachingPipeline(FiltersManager filtersManager, String name) {
69        mFiltersManager = filtersManager;
70        mName = name;
71    }
72
73    public static synchronized RenderScript getRenderScriptContext() {
74        return sRS;
75    }
76
77    public static synchronized void createRenderscriptContext(Context context) {
78        if (sRS != null) {
79            Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
80            destroyRenderScriptContext();
81        }
82        sRS = RenderScript.create(context);
83    }
84
85    public static synchronized void destroyRenderScriptContext() {
86        if (sRS != null) {
87            sRS.destroy();
88        }
89        sRS = null;
90    }
91
92    public void stop() {
93        mEnvironment.setStop(true);
94    }
95
96    public synchronized void reset() {
97        synchronized (CachingPipeline.class) {
98            if (getRenderScriptContext() == null) {
99                return;
100            }
101            mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
102            if (mResizedOriginalBitmap != null) {
103                mResizedOriginalBitmap.recycle();
104                mResizedOriginalBitmap = null;
105            }
106            if (mOriginalAllocation != null) {
107                mOriginalAllocation.destroy();
108                mOriginalAllocation = null;
109            }
110            if (mFiltersOnlyOriginalAllocation != null) {
111                mFiltersOnlyOriginalAllocation.destroy();
112                mFiltersOnlyOriginalAllocation = null;
113            }
114            mPreviewScaleFactor = 1.0f;
115            mHighResPreviewScaleFactor = 1.0f;
116
117            destroyPixelAllocations();
118        }
119    }
120
121    public Resources getResources() {
122        return sRS.getApplicationContext().getResources();
123    }
124
125    private synchronized void destroyPixelAllocations() {
126        if (DEBUG) {
127            Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
128        }
129        if (mInPixelsAllocation != null) {
130            mInPixelsAllocation.destroy();
131            mInPixelsAllocation = null;
132        }
133        if (mOutPixelsAllocation != null) {
134            mOutPixelsAllocation.destroy();
135            mOutPixelsAllocation = null;
136        }
137        mWidth = 0;
138        mHeight = 0;
139    }
140
141    private String getType(RenderingRequest request) {
142        if (request.getType() == RenderingRequest.ICON_RENDERING) {
143            return "ICON_RENDERING";
144        }
145        if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
146            return "FILTERS_RENDERING";
147        }
148        if (request.getType() == RenderingRequest.FULL_RENDERING) {
149            return "FULL_RENDERING";
150        }
151        if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
152            return "GEOMETRY_RENDERING";
153        }
154        if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
155            return "PARTIAL_RENDERING";
156        }
157        if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
158            return "HIGHRES_RENDERING";
159        }
160        return "UNKNOWN TYPE!";
161    }
162
163    private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
164        mEnvironment.setPipeline(this);
165        mEnvironment.setFiltersManager(mFiltersManager);
166        mEnvironment.setBitmapCache(MasterImage.getImage().getBitmapCache());
167        if (highResPreview) {
168            mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
169        } else {
170            mEnvironment.setScaleFactor(mPreviewScaleFactor);
171        }
172        mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
173        mEnvironment.setImagePreset(preset);
174        mEnvironment.setStop(false);
175    }
176
177    public void setOriginal(Bitmap bitmap) {
178        mOriginalBitmap = bitmap;
179        Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
180        ImagePreset preset = MasterImage.getImage().getPreset();
181        setupEnvironment(preset, false);
182        updateOriginalAllocation(preset);
183    }
184
185    private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
186        if (preset == null) {
187            return false;
188        }
189        Bitmap originalBitmap = mOriginalBitmap;
190
191        if (originalBitmap == null) {
192            return false;
193        }
194
195        RenderScript RS = getRenderScriptContext();
196
197        Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
198        mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
199                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
200        if (filtersOnlyOriginalAllocation != null) {
201            filtersOnlyOriginalAllocation.destroy();
202        }
203
204        Allocation originalAllocation = mOriginalAllocation;
205        mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
206        mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
207                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
208        if (originalAllocation != null) {
209            originalAllocation.destroy();
210        }
211
212        return true;
213    }
214
215    public void renderHighres(RenderingRequest request) {
216        synchronized (CachingPipeline.class) {
217            if (getRenderScriptContext() == null) {
218                return;
219            }
220            ImagePreset preset = request.getImagePreset();
221            setupEnvironment(preset, false);
222            Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
223            if (bitmap == null) {
224                return;
225            }
226            bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.HIGHRES);
227            bitmap = preset.applyGeometry(bitmap, mEnvironment);
228
229            mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
230            Bitmap bmp = preset.apply(bitmap, mEnvironment);
231            if (!mEnvironment.needsStop()) {
232                request.setBitmap(bmp);
233            } else {
234                mEnvironment.cache(bmp);
235            }
236            mFiltersManager.freeFilterResources(preset);
237        }
238    }
239
240    public void renderGeometry(RenderingRequest request) {
241        synchronized (CachingPipeline.class) {
242            if (getRenderScriptContext() == null) {
243                return;
244            }
245            ImagePreset preset = request.getImagePreset();
246            setupEnvironment(preset, false);
247            Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
248            if (bitmap == null) {
249                return;
250            }
251            bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.GEOMETRY);
252            bitmap = preset.applyGeometry(bitmap, mEnvironment);
253            if (!mEnvironment.needsStop()) {
254                request.setBitmap(bitmap);
255            } else {
256                mEnvironment.cache(bitmap);
257            }
258            mFiltersManager.freeFilterResources(preset);
259        }
260    }
261
262    public void renderFilters(RenderingRequest request) {
263        synchronized (CachingPipeline.class) {
264            if (getRenderScriptContext() == null) {
265                return;
266            }
267            ImagePreset preset = request.getImagePreset();
268            setupEnvironment(preset, false);
269            Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
270            if (bitmap == null) {
271                return;
272            }
273            bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.FILTERS);
274            bitmap = preset.apply(bitmap, mEnvironment);
275            if (!mEnvironment.needsStop()) {
276                request.setBitmap(bitmap);
277            } else {
278                mEnvironment.cache(bitmap);
279            }
280            mFiltersManager.freeFilterResources(preset);
281        }
282    }
283
284    public synchronized void render(RenderingRequest request) {
285        // TODO: cleanup/remove GEOMETRY / FILTERS paths
286        synchronized (CachingPipeline.class) {
287            if (getRenderScriptContext() == null) {
288                return;
289            }
290            if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
291                  && request.getType() != RenderingRequest.ICON_RENDERING
292                    && request.getBitmap() == null)
293                    || request.getImagePreset() == null) {
294                return;
295            }
296
297            if (DEBUG) {
298                Log.v(LOGTAG, "render image of type " + getType(request));
299            }
300
301            Bitmap bitmap = request.getBitmap();
302            ImagePreset preset = request.getImagePreset();
303            setupEnvironment(preset, true);
304            mFiltersManager.freeFilterResources(preset);
305
306            if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
307                MasterImage master = MasterImage.getImage();
308                bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(),
309                        mEnvironment.getBimapCache(),
310                        master.getUri(), request.getBounds(),
311                        request.getDestination());
312                if (bitmap == null) {
313                    Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
314                    return;
315                }
316            }
317
318            if (request.getType() == RenderingRequest.FULL_RENDERING
319                    || request.getType() == RenderingRequest.GEOMETRY_RENDERING
320                    || request.getType() == RenderingRequest.FILTERS_RENDERING) {
321                updateOriginalAllocation(preset);
322            }
323
324            if (DEBUG && bitmap != null) {
325                Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
326                        + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
327                        + mResizedOriginalBitmap.getHeight());
328            }
329
330            if (request.getType() == RenderingRequest.FULL_RENDERING
331                    || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
332                mOriginalAllocation.copyTo(bitmap);
333            } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
334                mFiltersOnlyOriginalAllocation.copyTo(bitmap);
335            }
336
337            if (request.getType() == RenderingRequest.FULL_RENDERING
338                    || request.getType() == RenderingRequest.FILTERS_RENDERING
339                    || request.getType() == RenderingRequest.ICON_RENDERING
340                    || request.getType() == RenderingRequest.PARTIAL_RENDERING
341                    || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
342
343                if (request.getType() == RenderingRequest.ICON_RENDERING) {
344                    mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
345                } else {
346                    mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
347                }
348
349                if (request.getType() == RenderingRequest.ICON_RENDERING) {
350                    Rect iconBounds = request.getIconBounds();
351                    Bitmap source = MasterImage.getImage().getThumbnailBitmap();
352                    if (iconBounds.width() > source.getWidth() * 2) {
353                        source = MasterImage.getImage().getLargeThumbnailBitmap();
354                    }
355                    if (iconBounds != null) {
356                        bitmap = mEnvironment.getBitmap(iconBounds.width(),
357                                iconBounds.height(), BitmapCache.ICON);
358                        Canvas canvas = new Canvas(bitmap);
359                        Matrix m = new Matrix();
360                        float minSize = Math.min(source.getWidth(), source.getHeight());
361                        float maxSize = Math.max(iconBounds.width(), iconBounds.height());
362                        float scale = maxSize / minSize;
363                        m.setScale(scale, scale);
364                        float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f;
365                        float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f;
366                        m.postTranslate(dx, dy);
367                        canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG));
368                    } else {
369                        bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON);
370                    }
371                }
372                Bitmap bmp = preset.apply(bitmap, mEnvironment);
373                if (!mEnvironment.needsStop()) {
374                    request.setBitmap(bmp);
375                }
376                mFiltersManager.freeFilterResources(preset);
377            }
378        }
379    }
380
381    public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
382        synchronized (CachingPipeline.class) {
383            if (getRenderScriptContext() == null) {
384                return;
385            }
386            setupEnvironment(preset, false);
387            mFiltersManager.freeFilterResources(preset);
388            preset.applyFilters(-1, -1, in, out, mEnvironment);
389            boolean copyOut = false;
390            if (preset.nbFilters() > 0) {
391                copyOut = true;
392            }
393            preset.applyBorder(in, out, copyOut, mEnvironment);
394        }
395    }
396
397    public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
398        synchronized (CachingPipeline.class) {
399            if (getRenderScriptContext() == null) {
400                return bitmap;
401            }
402            setupEnvironment(preset, false);
403            mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL);
404            mEnvironment.setScaleFactor(1.0f);
405            mFiltersManager.freeFilterResources(preset);
406            bitmap = preset.applyGeometry(bitmap, mEnvironment);
407            bitmap = preset.apply(bitmap, mEnvironment);
408            return bitmap;
409        }
410    }
411
412    public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
413        return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap);
414    }
415
416    public void compute(SharedBuffer buffer, ImagePreset preset, int type) {
417        if (getRenderScriptContext() == null) {
418            return;
419        }
420        setupEnvironment(preset, false);
421        Vector<FilterRepresentation> filters = preset.getFilters();
422        Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment);
423        buffer.setProducer(result);
424        mEnvironment.cache(result);
425    }
426
427    public boolean needsRepaint() {
428        SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
429        return buffer.checkRepaintNeeded();
430    }
431
432    public void setPreviewScaleFactor(float previewScaleFactor) {
433        mPreviewScaleFactor = previewScaleFactor;
434    }
435
436    public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
437        mHighResPreviewScaleFactor = highResPreviewScaleFactor;
438    }
439
440    public synchronized boolean isInitialized() {
441        return getRenderScriptContext() != null && mOriginalBitmap != null;
442    }
443
444    public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
445        RenderScript RS = getRenderScriptContext();
446        boolean needsUpdate = false;
447        if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
448                bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
449            destroyPixelAllocations();
450            Bitmap bitmapBuffer = bitmap;
451            if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
452                bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
453            }
454            mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
455                    Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
456            mInPixelsAllocation = Allocation.createTyped(RS,
457                    mOutPixelsAllocation.getType());
458            needsUpdate = true;
459        }
460        if (RS != null) {
461            mInPixelsAllocation.copyFrom(bitmap);
462        }
463        if (bitmap.getWidth() != mWidth
464                || bitmap.getHeight() != mHeight) {
465            mWidth = bitmap.getWidth();
466            mHeight = bitmap.getHeight();
467            needsUpdate = true;
468        }
469        if (DEBUG) {
470            Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
471        }
472        return needsUpdate;
473    }
474
475    public synchronized Allocation getInPixelsAllocation() {
476        return mInPixelsAllocation;
477    }
478
479    public synchronized Allocation getOutPixelsAllocation() {
480        return mOutPixelsAllocation;
481    }
482
483    public String getName() {
484        return mName;
485    }
486
487    public RenderScript getRSContext() {
488        return CachingPipeline.getRenderScriptContext();
489    }
490}
491