2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.filtershow.pipeline;
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.support.v8.renderscript.Allocation;
28 import android.support.v8.renderscript.RenderScript;
29 import android.util.Log;
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.MasterImage;
38 import java.util.Vector;
40 public class CachingPipeline implements PipelineInterface {
41 private static final String LOGTAG = "CachingPipeline";
42 private boolean DEBUG = false;
44 private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
46 private static volatile RenderScript sRS = null;
48 private FiltersManager mFiltersManager = null;
49 private volatile Bitmap mOriginalBitmap = null;
50 private volatile Bitmap mResizedOriginalBitmap = null;
52 private FilterEnvironment mEnvironment = new FilterEnvironment();
53 private CacheProcessing mCachedProcessing = new CacheProcessing();
56 private volatile Allocation mOriginalAllocation = null;
57 private volatile Allocation mFiltersOnlyOriginalAllocation = null;
59 protected volatile Allocation mInPixelsAllocation;
60 protected volatile Allocation mOutPixelsAllocation;
61 private volatile int mWidth = 0;
62 private volatile int mHeight = 0;
64 private volatile float mPreviewScaleFactor = 1.0f;
65 private volatile float mHighResPreviewScaleFactor = 1.0f;
66 private volatile String mName = "";
68 public CachingPipeline(FiltersManager filtersManager, String name) {
69 mFiltersManager = filtersManager;
73 public static synchronized RenderScript getRenderScriptContext() {
77 public static synchronized void createRenderscriptContext(Context context) {
79 Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
80 destroyRenderScriptContext();
82 sRS = RenderScript.create(context);
85 public static synchronized void destroyRenderScriptContext() {
93 mEnvironment.setStop(true);
96 public synchronized void reset() {
97 synchronized (CachingPipeline.class) {
98 if (getRenderScriptContext() == null) {
101 mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
102 if (mResizedOriginalBitmap != null) {
103 mResizedOriginalBitmap.recycle();
104 mResizedOriginalBitmap = null;
106 if (mOriginalAllocation != null) {
107 mOriginalAllocation.destroy();
108 mOriginalAllocation = null;
110 if (mFiltersOnlyOriginalAllocation != null) {
111 mFiltersOnlyOriginalAllocation.destroy();
112 mFiltersOnlyOriginalAllocation = null;
114 mPreviewScaleFactor = 1.0f;
115 mHighResPreviewScaleFactor = 1.0f;
117 destroyPixelAllocations();
121 public Resources getResources() {
122 return sRS.getApplicationContext().getResources();
125 private synchronized void destroyPixelAllocations() {
127 Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
129 if (mInPixelsAllocation != null) {
130 mInPixelsAllocation.destroy();
131 mInPixelsAllocation = null;
133 if (mOutPixelsAllocation != null) {
134 mOutPixelsAllocation.destroy();
135 mOutPixelsAllocation = null;
141 private String getType(RenderingRequest request) {
142 if (request.getType() == RenderingRequest.ICON_RENDERING) {
143 return "ICON_RENDERING";
145 if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
146 return "FILTERS_RENDERING";
148 if (request.getType() == RenderingRequest.FULL_RENDERING) {
149 return "FULL_RENDERING";
151 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
152 return "GEOMETRY_RENDERING";
154 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
155 return "PARTIAL_RENDERING";
157 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
158 return "HIGHRES_RENDERING";
160 return "UNKNOWN TYPE!";
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);
170 mEnvironment.setScaleFactor(mPreviewScaleFactor);
172 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
173 mEnvironment.setImagePreset(preset);
174 mEnvironment.setStop(false);
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);
185 private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
186 if (preset == null) {
189 Bitmap originalBitmap = mOriginalBitmap;
191 if (originalBitmap == null) {
195 RenderScript RS = getRenderScriptContext();
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();
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();
215 public void renderHighres(RenderingRequest request) {
216 synchronized (CachingPipeline.class) {
217 if (getRenderScriptContext() == null) {
220 ImagePreset preset = request.getImagePreset();
221 setupEnvironment(preset, false);
222 Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
223 if (bitmap == null) {
226 bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.HIGHRES);
227 bitmap = preset.applyGeometry(bitmap, mEnvironment);
229 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
230 Bitmap bmp = preset.apply(bitmap, mEnvironment);
231 if (!mEnvironment.needsStop()) {
232 request.setBitmap(bmp);
234 mEnvironment.cache(bmp);
236 mFiltersManager.freeFilterResources(preset);
240 public void renderGeometry(RenderingRequest request) {
241 synchronized (CachingPipeline.class) {
242 if (getRenderScriptContext() == null) {
245 ImagePreset preset = request.getImagePreset();
246 setupEnvironment(preset, false);
247 Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
248 if (bitmap == null) {
251 bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.GEOMETRY);
252 bitmap = preset.applyGeometry(bitmap, mEnvironment);
253 if (!mEnvironment.needsStop()) {
254 request.setBitmap(bitmap);
256 mEnvironment.cache(bitmap);
258 mFiltersManager.freeFilterResources(preset);
262 public void renderFilters(RenderingRequest request) {
263 synchronized (CachingPipeline.class) {
264 if (getRenderScriptContext() == null) {
267 ImagePreset preset = request.getImagePreset();
268 setupEnvironment(preset, false);
269 Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
270 if (bitmap == null) {
273 bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.FILTERS);
274 bitmap = preset.apply(bitmap, mEnvironment);
275 if (!mEnvironment.needsStop()) {
276 request.setBitmap(bitmap);
278 mEnvironment.cache(bitmap);
280 mFiltersManager.freeFilterResources(preset);
284 public synchronized void render(RenderingRequest request) {
285 // TODO: cleanup/remove GEOMETRY / FILTERS paths
286 synchronized (CachingPipeline.class) {
287 if (getRenderScriptContext() == null) {
290 if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
291 && request.getType() != RenderingRequest.ICON_RENDERING
292 && request.getBitmap() == null)
293 || request.getImagePreset() == null) {
298 Log.v(LOGTAG, "render image of type " + getType(request));
301 Bitmap bitmap = request.getBitmap();
302 ImagePreset preset = request.getImagePreset();
303 setupEnvironment(preset, true);
304 mFiltersManager.freeFilterResources(preset);
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));
318 if (request.getType() == RenderingRequest.FULL_RENDERING
319 || request.getType() == RenderingRequest.GEOMETRY_RENDERING
320 || request.getType() == RenderingRequest.FILTERS_RENDERING) {
321 updateOriginalAllocation(preset);
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());
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);
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) {
343 if (request.getType() == RenderingRequest.ICON_RENDERING) {
344 mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
346 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
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();
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));
369 bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON);
372 Bitmap bmp = preset.apply(bitmap, mEnvironment);
373 if (!mEnvironment.needsStop()) {
374 request.setBitmap(bmp);
376 mFiltersManager.freeFilterResources(preset);
381 public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
382 synchronized (CachingPipeline.class) {
383 if (getRenderScriptContext() == null) {
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) {
393 preset.applyBorder(in, out, copyOut, mEnvironment);
397 public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
398 synchronized (CachingPipeline.class) {
399 if (getRenderScriptContext() == null) {
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);
412 public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
413 return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap);
416 public void compute(SharedBuffer buffer, ImagePreset preset, int type) {
417 if (getRenderScriptContext() == null) {
420 setupEnvironment(preset, false);
421 Vector<FilterRepresentation> filters = preset.getFilters();
422 buffer.removeProducer();
423 Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment);
424 buffer.setProducer(result);
427 public synchronized void computeOld(SharedBuffer buffer, ImagePreset preset, int type) {
428 synchronized (CachingPipeline.class) {
429 if (getRenderScriptContext() == null) {
433 Log.v(LOGTAG, "compute preset " + preset);
434 preset.showFilters();
437 String thread = Thread.currentThread().getName();
438 long time = System.currentTimeMillis();
439 setupEnvironment(preset, false);
440 mFiltersManager.freeFilterResources(preset);
442 Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
443 if (updateOriginalAllocation(preset) || buffer.getProducer() == null) {
444 resizedOriginalBitmap = mResizedOriginalBitmap;
445 buffer.setProducer(resizedOriginalBitmap);
446 mEnvironment.cache(buffer.getProducer());
449 Bitmap bitmap = buffer.getProducer().getBitmap();
450 long time2 = System.currentTimeMillis();
452 if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
453 || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
454 mEnvironment.cache(buffer.getProducer());
455 buffer.setProducer(resizedOriginalBitmap);
456 bitmap = buffer.getProducer().getBitmap();
458 mOriginalAllocation.copyTo(bitmap);
460 Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
461 if (tmpbitmap != bitmap) {
462 mEnvironment.cache(buffer.getProducer());
463 buffer.setProducer(tmpbitmap);
466 mFiltersManager.freeFilterResources(preset);
468 time = System.currentTimeMillis() - time;
469 time2 = System.currentTimeMillis() - time2;
471 Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
472 + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
473 + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
478 public boolean needsRepaint() {
479 SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
480 return buffer.checkRepaintNeeded();
483 public void setPreviewScaleFactor(float previewScaleFactor) {
484 mPreviewScaleFactor = previewScaleFactor;
487 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
488 mHighResPreviewScaleFactor = highResPreviewScaleFactor;
491 public synchronized boolean isInitialized() {
492 return getRenderScriptContext() != null && mOriginalBitmap != null;
495 public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
496 RenderScript RS = getRenderScriptContext();
497 boolean needsUpdate = false;
498 if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
499 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
500 destroyPixelAllocations();
501 Bitmap bitmapBuffer = bitmap;
502 if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
503 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
505 mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
506 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
507 mInPixelsAllocation = Allocation.createTyped(RS,
508 mOutPixelsAllocation.getType());
512 mInPixelsAllocation.copyFrom(bitmap);
514 if (bitmap.getWidth() != mWidth
515 || bitmap.getHeight() != mHeight) {
516 mWidth = bitmap.getWidth();
517 mHeight = bitmap.getHeight();
521 Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
526 public synchronized Allocation getInPixelsAllocation() {
527 return mInPixelsAllocation;
530 public synchronized Allocation getOutPixelsAllocation() {
531 return mOutPixelsAllocation;
534 public String getName() {
538 public RenderScript getRSContext() {
539 return CachingPipeline.getRenderScriptContext();