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.cache;
19 import android.app.Activity;
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;
26 import com.android.gallery3d.filtershow.filters.FiltersManager;
27 import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
28 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
29 import com.android.gallery3d.filtershow.imageshow.MasterImage;
30 import com.android.gallery3d.filtershow.pipeline.SharedBuffer;
31 import com.android.gallery3d.filtershow.presets.FilterEnvironment;
32 import com.android.gallery3d.filtershow.presets.ImagePreset;
33 import com.android.gallery3d.filtershow.presets.PipelineInterface;
35 public class CachingPipeline implements PipelineInterface {
36 private static final String LOGTAG = "CachingPipeline";
37 private boolean DEBUG = false;
39 private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
41 private static volatile RenderScript sRS = null;
42 private static volatile Resources sResources = null;
44 private FiltersManager mFiltersManager = null;
45 private volatile Bitmap mOriginalBitmap = null;
46 private volatile Bitmap mResizedOriginalBitmap = null;
48 private FilterEnvironment mEnvironment = new FilterEnvironment();
50 private volatile Allocation mOriginalAllocation = null;
51 private volatile Allocation mFiltersOnlyOriginalAllocation = null;
53 protected volatile Allocation mInPixelsAllocation;
54 protected volatile Allocation mOutPixelsAllocation;
55 private volatile int mWidth = 0;
56 private volatile int mHeight = 0;
58 private volatile GeometryMetadata mPreviousGeometry = null;
59 private volatile float mPreviewScaleFactor = 1.0f;
60 private volatile float mHighResPreviewScaleFactor = 1.0f;
61 private volatile String mName = "";
63 private ImageFilterGeometry mGeometry = null;
65 public CachingPipeline(FiltersManager filtersManager, String name) {
66 mFiltersManager = filtersManager;
70 public static synchronized RenderScript getRenderScriptContext() {
74 public static synchronized void createRenderscriptContext(Activity context) {
76 Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
77 destroyRenderScriptContext();
79 sRS = RenderScript.create(context);
80 sResources = context.getResources();
83 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 mPreviousGeometry = 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 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 Bitmap originalBitmap = mOriginalBitmap;
187 if (originalBitmap == null) {
191 GeometryMetadata geometry = preset.getGeometry();
192 if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) {
197 Log.v(LOGTAG, "geometry has changed");
200 RenderScript RS = getRenderScriptContext();
202 Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
203 mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
204 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
205 if (filtersOnlyOriginalAllocation != null) {
206 filtersOnlyOriginalAllocation.destroy();
209 Allocation originalAllocation = mOriginalAllocation;
210 mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
211 mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
212 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
213 if (originalAllocation != null) {
214 originalAllocation.destroy();
217 mPreviousGeometry = new GeometryMetadata(geometry);
221 public synchronized void render(RenderingRequest request) {
222 synchronized (CachingPipeline.class) {
223 if (getRenderScriptContext() == null) {
226 if (((request.getType() != RenderingRequest.PARTIAL_RENDERING
227 && request.getType() != RenderingRequest.HIGHRES_RENDERING)
228 && request.getBitmap() == null)
229 || request.getImagePreset() == null) {
234 Log.v(LOGTAG, "render image of type " + getType(request));
237 Bitmap bitmap = request.getBitmap();
238 ImagePreset preset = request.getImagePreset();
239 setupEnvironment(preset,
240 request.getType() != RenderingRequest.HIGHRES_RENDERING);
241 mFiltersManager.freeFilterResources(preset);
243 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
244 ImageLoader loader = MasterImage.getImage().getImageLoader();
245 if (loader == null) {
246 Log.w(LOGTAG, "loader not yet setup, cannot handle: " + getType(request));
249 bitmap = loader.getScaleOneImageForPreset(request.getBounds(),
250 request.getDestination());
251 if (bitmap == null) {
252 Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
257 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
258 ImageLoader loader = MasterImage.getImage().getImageLoader();
259 bitmap = loader.getOriginalBitmapHighres();
260 bitmap = preset.applyGeometry(bitmap, mEnvironment);
263 if (request.getType() == RenderingRequest.FULL_RENDERING
264 || request.getType() == RenderingRequest.GEOMETRY_RENDERING
265 || request.getType() == RenderingRequest.FILTERS_RENDERING) {
266 updateOriginalAllocation(preset);
270 Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
271 + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
272 + mResizedOriginalBitmap.getHeight());
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);
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) {
289 if (request.getType() == RenderingRequest.ICON_RENDERING) {
290 mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
292 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
295 Bitmap bmp = preset.apply(bitmap, mEnvironment);
296 if (!mEnvironment.needsStop()) {
297 request.setBitmap(bmp);
299 mFiltersManager.freeFilterResources(preset);
304 public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
305 synchronized (CachingPipeline.class) {
306 if (getRenderScriptContext() == null) {
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) {
316 preset.applyBorder(in, out, copyOut, mEnvironment);
320 public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
321 synchronized (CachingPipeline.class) {
322 if (getRenderScriptContext() == null) {
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);
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();
341 mGeometry.useRepresentation(preset.getGeometry());
342 return mGeometry.apply(bitmap, mPreviewScaleFactor,
343 FilterEnvironment.QUALITY_PREVIEW);
346 public synchronized void compute(SharedBuffer buffer, ImagePreset preset, int type) {
347 synchronized (CachingPipeline.class) {
348 if (getRenderScriptContext() == null) {
352 Log.v(LOGTAG, "compute preset " + preset);
353 preset.showFilters();
356 String thread = Thread.currentThread().getName();
357 long time = System.currentTimeMillis();
358 setupEnvironment(preset, false);
359 mFiltersManager.freeFilterResources(preset);
361 Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
362 if (updateOriginalAllocation(preset)) {
363 resizedOriginalBitmap = mResizedOriginalBitmap;
364 mEnvironment.cache(buffer.getProducer());
365 buffer.setProducer(resizedOriginalBitmap);
368 Bitmap bitmap = null;
369 if (buffer.getProducer() != null) {
370 bitmap = buffer.getProducer().getBitmap();
372 long time2 = System.currentTimeMillis();
374 if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
375 || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
376 mEnvironment.cache(buffer.getProducer());
377 buffer.setProducer(resizedOriginalBitmap);
378 bitmap = buffer.getProducer().getBitmap();
380 mOriginalAllocation.copyTo(bitmap);
382 Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
383 if (tmpbitmap != bitmap) {
384 mEnvironment.cache(buffer.getProducer());
385 buffer.setProducer(tmpbitmap);
388 mFiltersManager.freeFilterResources(preset);
390 time = System.currentTimeMillis() - time;
391 time2 = System.currentTimeMillis() - time2;
393 Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
394 + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
395 + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
400 public boolean needsRepaint() {
401 SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
402 return buffer.checkRepaintNeeded();
405 public void setPreviewScaleFactor(float previewScaleFactor) {
406 mPreviewScaleFactor = previewScaleFactor;
409 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
410 mHighResPreviewScaleFactor = highResPreviewScaleFactor;
413 public synchronized boolean isInitialized() {
414 return getRenderScriptContext() != null && mOriginalBitmap != null;
417 public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
418 RenderScript RS = getRenderScriptContext();
419 boolean needsUpdate = false;
420 if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
421 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
422 destroyPixelAllocations();
423 Bitmap bitmapBuffer = bitmap;
424 if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
425 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
427 mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
428 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
429 mInPixelsAllocation = Allocation.createTyped(RS,
430 mOutPixelsAllocation.getType());
434 mInPixelsAllocation.copyFrom(bitmap);
436 if (bitmap.getWidth() != mWidth
437 || bitmap.getHeight() != mHeight) {
438 mWidth = bitmap.getWidth();
439 mHeight = bitmap.getHeight();
443 Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
448 public synchronized Allocation getInPixelsAllocation() {
449 return mInPixelsAllocation;
452 public synchronized Allocation getOutPixelsAllocation() {
453 return mOutPixelsAllocation;
456 public String getName() {
460 public RenderScript getRSContext() {
461 return CachingPipeline.getRenderScriptContext();