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.ImageLoader;
32 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
33 import com.android.gallery3d.filtershow.filters.FiltersManager;
34 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
35 import com.android.gallery3d.filtershow.imageshow.MasterImage;
37 import java.util.Vector;
39 public class CachingPipeline implements PipelineInterface {
40 private static final String LOGTAG = "CachingPipeline";
41 private boolean DEBUG = false;
43 private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
45 private static volatile RenderScript sRS = null;
47 private FiltersManager mFiltersManager = null;
48 private volatile Bitmap mOriginalBitmap = null;
49 private volatile Bitmap mResizedOriginalBitmap = null;
51 private FilterEnvironment mEnvironment = new FilterEnvironment();
52 private CacheProcessing mCachedProcessing = new CacheProcessing();
55 private volatile Allocation mOriginalAllocation = null;
56 private volatile Allocation mFiltersOnlyOriginalAllocation = null;
58 protected volatile Allocation mInPixelsAllocation;
59 protected volatile Allocation mOutPixelsAllocation;
60 private volatile int mWidth = 0;
61 private volatile int mHeight = 0;
63 private volatile float mPreviewScaleFactor = 1.0f;
64 private volatile float mHighResPreviewScaleFactor = 1.0f;
65 private volatile String mName = "";
67 public CachingPipeline(FiltersManager filtersManager, String name) {
68 mFiltersManager = filtersManager;
72 public static synchronized RenderScript getRenderScriptContext() {
76 public static synchronized void createRenderscriptContext(Context context) {
78 Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
79 destroyRenderScriptContext();
81 sRS = RenderScript.create(context);
84 public static synchronized void destroyRenderScriptContext() {
92 mEnvironment.setStop(true);
95 public synchronized void reset() {
96 synchronized (CachingPipeline.class) {
97 if (getRenderScriptContext() == null) {
100 mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
101 if (mResizedOriginalBitmap != null) {
102 mResizedOriginalBitmap.recycle();
103 mResizedOriginalBitmap = null;
105 if (mOriginalAllocation != null) {
106 mOriginalAllocation.destroy();
107 mOriginalAllocation = null;
109 if (mFiltersOnlyOriginalAllocation != null) {
110 mFiltersOnlyOriginalAllocation.destroy();
111 mFiltersOnlyOriginalAllocation = null;
113 mPreviewScaleFactor = 1.0f;
114 mHighResPreviewScaleFactor = 1.0f;
116 destroyPixelAllocations();
120 public Resources getResources() {
121 return sRS.getApplicationContext().getResources();
124 private synchronized void destroyPixelAllocations() {
126 Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
128 if (mInPixelsAllocation != null) {
129 mInPixelsAllocation.destroy();
130 mInPixelsAllocation = null;
132 if (mOutPixelsAllocation != null) {
133 mOutPixelsAllocation.destroy();
134 mOutPixelsAllocation = null;
140 private String getType(RenderingRequest request) {
141 if (request.getType() == RenderingRequest.ICON_RENDERING) {
142 return "ICON_RENDERING";
144 if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
145 return "FILTERS_RENDERING";
147 if (request.getType() == RenderingRequest.FULL_RENDERING) {
148 return "FULL_RENDERING";
150 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
151 return "GEOMETRY_RENDERING";
153 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
154 return "PARTIAL_RENDERING";
156 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
157 return "HIGHRES_RENDERING";
159 return "UNKNOWN TYPE!";
162 private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
163 mEnvironment.setPipeline(this);
164 mEnvironment.setFiltersManager(mFiltersManager);
165 mEnvironment.setBitmapCache(MasterImage.getImage().getBitmapCache());
166 if (highResPreview) {
167 mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
169 mEnvironment.setScaleFactor(mPreviewScaleFactor);
171 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
172 mEnvironment.setImagePreset(preset);
173 mEnvironment.setStop(false);
176 public void setOriginal(Bitmap bitmap) {
177 mOriginalBitmap = bitmap;
178 Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
179 ImagePreset preset = MasterImage.getImage().getPreset();
180 setupEnvironment(preset, false);
181 updateOriginalAllocation(preset);
184 private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
185 if (preset == null) {
188 Bitmap originalBitmap = mOriginalBitmap;
190 if (originalBitmap == null) {
194 RenderScript RS = getRenderScriptContext();
196 Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
197 mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
198 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
199 if (filtersOnlyOriginalAllocation != null) {
200 filtersOnlyOriginalAllocation.destroy();
203 Allocation originalAllocation = mOriginalAllocation;
204 mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
205 mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
206 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
207 if (originalAllocation != null) {
208 originalAllocation.destroy();
214 public void renderHighres(RenderingRequest request) {
215 synchronized (CachingPipeline.class) {
216 if (getRenderScriptContext() == null) {
219 ImagePreset preset = request.getImagePreset();
220 setupEnvironment(preset, false);
221 Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
222 if (bitmap == null) {
225 bitmap = mEnvironment.getBitmapCopy(bitmap);
226 bitmap = preset.applyGeometry(bitmap, mEnvironment);
228 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
229 Bitmap bmp = preset.apply(bitmap, mEnvironment);
230 if (!mEnvironment.needsStop()) {
231 request.setBitmap(bmp);
233 mEnvironment.cache(bmp);
235 mFiltersManager.freeFilterResources(preset);
239 public void renderGeometry(RenderingRequest request) {
240 synchronized (CachingPipeline.class) {
241 if (getRenderScriptContext() == null) {
244 ImagePreset preset = request.getImagePreset();
245 setupEnvironment(preset, false);
246 Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
247 if (bitmap == null) {
250 bitmap = mEnvironment.getBitmapCopy(bitmap);
251 bitmap = preset.applyGeometry(bitmap, mEnvironment);
252 if (!mEnvironment.needsStop()) {
253 request.setBitmap(bitmap);
255 mEnvironment.cache(bitmap);
257 mFiltersManager.freeFilterResources(preset);
261 public void renderFilters(RenderingRequest request) {
262 synchronized (CachingPipeline.class) {
263 if (getRenderScriptContext() == null) {
266 ImagePreset preset = request.getImagePreset();
267 setupEnvironment(preset, false);
268 Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
269 if (bitmap == null) {
272 bitmap = mEnvironment.getBitmapCopy(bitmap);
273 bitmap = preset.apply(bitmap, mEnvironment);
274 if (!mEnvironment.needsStop()) {
275 request.setBitmap(bitmap);
277 mEnvironment.cache(bitmap);
279 mFiltersManager.freeFilterResources(preset);
283 public synchronized void render(RenderingRequest request) {
284 // TODO: cleanup/remove GEOMETRY / FILTERS paths
285 synchronized (CachingPipeline.class) {
286 if (getRenderScriptContext() == null) {
289 if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
290 && request.getType() != RenderingRequest.ICON_RENDERING
291 && request.getBitmap() == null)
292 || request.getImagePreset() == null) {
297 Log.v(LOGTAG, "render image of type " + getType(request));
300 Bitmap bitmap = request.getBitmap();
301 ImagePreset preset = request.getImagePreset();
302 setupEnvironment(preset, true);
303 mFiltersManager.freeFilterResources(preset);
305 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
306 MasterImage master = MasterImage.getImage();
307 bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(),
309 master.getUri(), request.getBounds(),
310 request.getDestination());
311 if (bitmap == null) {
312 Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
317 if (request.getType() == RenderingRequest.FULL_RENDERING
318 || request.getType() == RenderingRequest.GEOMETRY_RENDERING
319 || request.getType() == RenderingRequest.FILTERS_RENDERING) {
320 updateOriginalAllocation(preset);
323 if (DEBUG && bitmap != null) {
324 Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
325 + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
326 + mResizedOriginalBitmap.getHeight());
329 if (request.getType() == RenderingRequest.FULL_RENDERING
330 || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
331 mOriginalAllocation.copyTo(bitmap);
332 } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
333 mFiltersOnlyOriginalAllocation.copyTo(bitmap);
336 if (request.getType() == RenderingRequest.FULL_RENDERING
337 || request.getType() == RenderingRequest.FILTERS_RENDERING
338 || request.getType() == RenderingRequest.ICON_RENDERING
339 || request.getType() == RenderingRequest.PARTIAL_RENDERING
340 || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
342 if (request.getType() == RenderingRequest.ICON_RENDERING) {
343 mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
345 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
348 if (request.getType() == RenderingRequest.ICON_RENDERING) {
349 Rect iconBounds = request.getIconBounds();
350 Bitmap source = MasterImage.getImage().getThumbnailBitmap();
351 if (iconBounds.width() > source.getWidth() * 2) {
352 source = MasterImage.getImage().getLargeThumbnailBitmap();
354 if (iconBounds != null) {
355 bitmap = mEnvironment.getBitmap(iconBounds.width(), iconBounds.height());
356 Canvas canvas = new Canvas(bitmap);
357 Matrix m = new Matrix();
358 float minSize = Math.min(source.getWidth(), source.getHeight());
359 float maxSize = Math.max(iconBounds.width(), iconBounds.height());
360 float scale = maxSize / minSize;
361 m.setScale(scale, scale);
362 float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f;
363 float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f;
364 m.postTranslate(dx, dy);
365 canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG));
367 bitmap = mEnvironment.getBitmapCopy(source);
370 Bitmap bmp = preset.apply(bitmap, mEnvironment);
371 if (!mEnvironment.needsStop()) {
372 request.setBitmap(bmp);
374 mFiltersManager.freeFilterResources(preset);
379 public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
380 synchronized (CachingPipeline.class) {
381 if (getRenderScriptContext() == null) {
384 setupEnvironment(preset, false);
385 mFiltersManager.freeFilterResources(preset);
386 preset.applyFilters(-1, -1, in, out, mEnvironment);
387 boolean copyOut = false;
388 if (preset.nbFilters() > 0) {
391 preset.applyBorder(in, out, copyOut, mEnvironment);
395 public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
396 synchronized (CachingPipeline.class) {
397 if (getRenderScriptContext() == null) {
400 setupEnvironment(preset, false);
401 mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL);
402 mEnvironment.setScaleFactor(1.0f);
403 mFiltersManager.freeFilterResources(preset);
404 bitmap = preset.applyGeometry(bitmap, mEnvironment);
405 bitmap = preset.apply(bitmap, mEnvironment);
410 public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
411 return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap);
414 public void compute(SharedBuffer buffer, ImagePreset preset, int type) {
415 if (getRenderScriptContext() == null) {
418 setupEnvironment(preset, false);
419 Vector<FilterRepresentation> filters = preset.getFilters();
420 Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment);
421 buffer.setProducer(result);
424 public synchronized void computeOld(SharedBuffer buffer, ImagePreset preset, int type) {
425 synchronized (CachingPipeline.class) {
426 if (getRenderScriptContext() == null) {
430 Log.v(LOGTAG, "compute preset " + preset);
431 preset.showFilters();
434 String thread = Thread.currentThread().getName();
435 long time = System.currentTimeMillis();
436 setupEnvironment(preset, false);
437 mFiltersManager.freeFilterResources(preset);
439 Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
440 if (updateOriginalAllocation(preset) || buffer.getProducer() == null) {
441 resizedOriginalBitmap = mResizedOriginalBitmap;
442 buffer.setProducer(resizedOriginalBitmap);
443 mEnvironment.cache(buffer.getProducer());
446 Bitmap bitmap = buffer.getProducer().getBitmap();
447 long time2 = System.currentTimeMillis();
449 if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
450 || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
451 mEnvironment.cache(buffer.getProducer());
452 buffer.setProducer(resizedOriginalBitmap);
453 bitmap = buffer.getProducer().getBitmap();
455 mOriginalAllocation.copyTo(bitmap);
457 Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
458 if (tmpbitmap != bitmap) {
459 mEnvironment.cache(buffer.getProducer());
460 buffer.setProducer(tmpbitmap);
463 mFiltersManager.freeFilterResources(preset);
465 time = System.currentTimeMillis() - time;
466 time2 = System.currentTimeMillis() - time2;
468 Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
469 + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
470 + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
475 public boolean needsRepaint() {
476 SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
477 return buffer.checkRepaintNeeded();
480 public void setPreviewScaleFactor(float previewScaleFactor) {
481 mPreviewScaleFactor = previewScaleFactor;
484 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
485 mHighResPreviewScaleFactor = highResPreviewScaleFactor;
488 public synchronized boolean isInitialized() {
489 return getRenderScriptContext() != null && mOriginalBitmap != null;
492 public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
493 RenderScript RS = getRenderScriptContext();
494 boolean needsUpdate = false;
495 if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
496 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
497 destroyPixelAllocations();
498 Bitmap bitmapBuffer = bitmap;
499 if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
500 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
502 mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
503 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
504 mInPixelsAllocation = Allocation.createTyped(RS,
505 mOutPixelsAllocation.getType());
509 mInPixelsAllocation.copyFrom(bitmap);
511 if (bitmap.getWidth() != mWidth
512 || bitmap.getHeight() != mHeight) {
513 mWidth = bitmap.getWidth();
514 mHeight = bitmap.getHeight();
518 Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
523 public synchronized Allocation getInPixelsAllocation() {
524 return mInPixelsAllocation;
527 public synchronized Allocation getOutPixelsAllocation() {
528 return mOutPixelsAllocation;
531 public String getName() {
535 public RenderScript getRSContext() {
536 return CachingPipeline.getRenderScriptContext();