OSDN Git Service

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