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.graphics.Canvas;
23 import android.graphics.Matrix;
24 import android.graphics.Paint;
25 import android.graphics.Rect;
26 import android.graphics.RectF;
27 import android.renderscript.Allocation;
28 import android.renderscript.RenderScript;
29 import android.util.Log;
30 
31 import com.android.gallery3d.filtershow.cache.BitmapCache;
32 import com.android.gallery3d.filtershow.cache.ImageLoader;
33 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
34 import com.android.gallery3d.filtershow.filters.FiltersManager;
35 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
36 import com.android.gallery3d.filtershow.imageshow.PrimaryImage;
37 
38 import java.util.Vector;
39 
40 public 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 
CachingPipeline(FiltersManager filtersManager, String name)68     public CachingPipeline(FiltersManager filtersManager, String name) {
69         mFiltersManager = filtersManager;
70         mName = name;
71     }
72 
getRenderScriptContext()73     public static synchronized RenderScript getRenderScriptContext() {
74         return sRS;
75     }
76 
createRenderscriptContext(Context context)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 
destroyRenderScriptContext()85     public static synchronized void destroyRenderScriptContext() {
86         if (sRS != null) {
87             sRS.destroy();
88         }
89         sRS = null;
90     }
91 
stop()92     public void stop() {
93         mEnvironment.setStop(true);
94     }
95 
reset()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 
getResources()121     public Resources getResources() {
122         return sRS.getApplicationContext().getResources();
123     }
124 
destroyPixelAllocations()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 
getType(RenderingRequest request)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 
setupEnvironment(ImagePreset preset, boolean highResPreview)163     private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
164         mEnvironment.setPipeline(this);
165         mEnvironment.setFiltersManager(mFiltersManager);
166         mEnvironment.setBitmapCache(PrimaryImage.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 
setOriginal(Bitmap bitmap)177     public void setOriginal(Bitmap bitmap) {
178         mOriginalBitmap = bitmap;
179         Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
180         ImagePreset preset = PrimaryImage.getImage().getPreset();
181         setupEnvironment(preset, false);
182         updateOriginalAllocation(preset);
183     }
184 
updateOriginalAllocation(ImagePreset preset)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 
renderHighres(RenderingRequest request)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 = PrimaryImage.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 
renderGeometry(RenderingRequest request)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 = PrimaryImage.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 
renderFilters(RenderingRequest request)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 = PrimaryImage.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 
render(RenderingRequest request)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                 PrimaryImage primary = PrimaryImage.getImage();
308                 bitmap = ImageLoader.getScaleOneImageForPreset(primary.getActivity(),
309                         mEnvironment.getBimapCache(),
310                         primary.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"
326                         + bitmap.getHeight() + " ? resizeOriginal ("
327                         + mResizedOriginalBitmap.getWidth() + "x"
328                         + mResizedOriginalBitmap.getHeight());
329             }
330 
331             if (request.getType() == RenderingRequest.FULL_RENDERING
332                     || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
333                 mOriginalAllocation.copyTo(bitmap);
334             } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
335                 mFiltersOnlyOriginalAllocation.copyTo(bitmap);
336             }
337 
338             if (request.getType() == RenderingRequest.FULL_RENDERING
339                     || request.getType() == RenderingRequest.FILTERS_RENDERING
340                     || request.getType() == RenderingRequest.ICON_RENDERING
341                     || request.getType() == RenderingRequest.PARTIAL_RENDERING
342                     || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
343 
344                 if (request.getType() == RenderingRequest.ICON_RENDERING) {
345                     mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
346                 } else {
347                     mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
348                 }
349 
350                 if (request.getType() == RenderingRequest.ICON_RENDERING) {
351                     Rect iconBounds = request.getIconBounds();
352                     Bitmap source = PrimaryImage.getImage().getThumbnailBitmap();
353                     if (iconBounds.width() > source.getWidth() * 2) {
354                         source = PrimaryImage.getImage().getLargeThumbnailBitmap();
355                     }
356                     if (iconBounds != null) {
357                         bitmap = mEnvironment.getBitmap(iconBounds.width(),
358                                 iconBounds.height(), BitmapCache.ICON);
359                         Canvas canvas = new Canvas(bitmap);
360                         Matrix m = new Matrix();
361                         float minSize = Math.min(source.getWidth(), source.getHeight());
362                         float maxSize = Math.max(iconBounds.width(), iconBounds.height());
363                         float scale = maxSize / minSize;
364                         m.setScale(scale, scale);
365                         float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f;
366                         float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f;
367                         m.postTranslate(dx, dy);
368                         canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG));
369                     } else {
370                         bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON);
371                     }
372                 }
373                 Bitmap bmp = preset.apply(bitmap, mEnvironment);
374                 if (!mEnvironment.needsStop()) {
375                     request.setBitmap(bmp);
376                 }
377                 mFiltersManager.freeFilterResources(preset);
378             }
379         }
380     }
381 
renderImage(ImagePreset preset, Allocation in, Allocation out)382     public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
383         synchronized (CachingPipeline.class) {
384             if (getRenderScriptContext() == null) {
385                 return;
386             }
387             setupEnvironment(preset, false);
388             mFiltersManager.freeFilterResources(preset);
389             preset.applyFilters(-1, -1, in, out, mEnvironment);
390             boolean copyOut = false;
391             if (preset.nbFilters() > 0) {
392                 copyOut = true;
393             }
394             preset.applyBorder(in, out, copyOut, mEnvironment);
395         }
396     }
397 
renderFinalImage(Bitmap bitmap, ImagePreset preset)398     public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
399         synchronized (CachingPipeline.class) {
400             if (getRenderScriptContext() == null) {
401                 return bitmap;
402             }
403             setupEnvironment(preset, false);
404             mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL);
405             mEnvironment.setScaleFactor(1.0f);
406             mFiltersManager.freeFilterResources(preset);
407             bitmap = preset.applyGeometry(bitmap, mEnvironment);
408             bitmap = preset.apply(bitmap, mEnvironment);
409             return bitmap;
410         }
411     }
412 
renderGeometryIcon(Bitmap bitmap, ImagePreset preset)413     public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
414         return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap);
415     }
416 
compute(SharedBuffer buffer, ImagePreset preset, int type)417     public void compute(SharedBuffer buffer, ImagePreset preset, int type) {
418         if (getRenderScriptContext() == null) {
419             return;
420         }
421         setupEnvironment(preset, false);
422         Vector<FilterRepresentation> filters = preset.getFilters();
423         Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment);
424         buffer.setProducer(result);
425         mEnvironment.cache(result);
426     }
427 
needsRepaint()428     public boolean needsRepaint() {
429         SharedBuffer buffer = PrimaryImage.getImage().getPreviewBuffer();
430         return buffer.checkRepaintNeeded();
431     }
432 
setPreviewScaleFactor(float previewScaleFactor)433     public void setPreviewScaleFactor(float previewScaleFactor) {
434         mPreviewScaleFactor = previewScaleFactor;
435     }
436 
setHighResPreviewScaleFactor(float highResPreviewScaleFactor)437     public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
438         mHighResPreviewScaleFactor = highResPreviewScaleFactor;
439     }
440 
isInitialized()441     public synchronized boolean isInitialized() {
442         return getRenderScriptContext() != null && mOriginalBitmap != null;
443     }
444 
prepareRenderscriptAllocations(Bitmap bitmap)445     public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
446         RenderScript RS = getRenderScriptContext();
447         boolean needsUpdate = false;
448         if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
449                 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
450             destroyPixelAllocations();
451             Bitmap bitmapBuffer = bitmap;
452             if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
453                 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
454             }
455             mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
456                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
457             mInPixelsAllocation = Allocation.createTyped(RS,
458                     mOutPixelsAllocation.getType());
459             needsUpdate = true;
460         }
461         if (RS != null) {
462             mInPixelsAllocation.copyFrom(bitmap);
463         }
464         if (bitmap.getWidth() != mWidth
465                 || bitmap.getHeight() != mHeight) {
466             mWidth = bitmap.getWidth();
467             mHeight = bitmap.getHeight();
468             needsUpdate = true;
469         }
470         if (DEBUG) {
471             Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
472         }
473         return needsUpdate;
474     }
475 
getInPixelsAllocation()476     public synchronized Allocation getInPixelsAllocation() {
477         return mInPixelsAllocation;
478     }
479 
getOutPixelsAllocation()480     public synchronized Allocation getOutPixelsAllocation() {
481         return mOutPixelsAllocation;
482     }
483 
getName()484     public String getName() {
485         return mName;
486     }
487 
getRSContext()488     public RenderScript getRSContext() {
489         return CachingPipeline.getRenderScriptContext();
490     }
491 }
492