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.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.cache.ImageLoader;
27 import com.android.gallery3d.filtershow.filters.FiltersManager;
28 import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
29 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
30 import com.android.gallery3d.filtershow.imageshow.MasterImage;
32 public class CachingPipeline implements PipelineInterface {
33 private static final String LOGTAG = "CachingPipeline";
34 private boolean DEBUG = false;
36 private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
38 private static volatile RenderScript sRS = null;
39 private static volatile Resources sResources = null;
41 private FiltersManager mFiltersManager = null;
42 private volatile Bitmap mOriginalBitmap = null;
43 private volatile Bitmap mResizedOriginalBitmap = null;
45 private FilterEnvironment mEnvironment = new FilterEnvironment();
47 private volatile Allocation mOriginalAllocation = null;
48 private volatile Allocation mFiltersOnlyOriginalAllocation = null;
50 protected volatile Allocation mInPixelsAllocation;
51 protected volatile Allocation mOutPixelsAllocation;
52 private volatile int mWidth = 0;
53 private volatile int mHeight = 0;
55 private volatile GeometryMetadata mPreviousGeometry = null;
56 private volatile float mPreviewScaleFactor = 1.0f;
57 private volatile float mHighResPreviewScaleFactor = 1.0f;
58 private volatile String mName = "";
60 private ImageFilterGeometry mGeometry = null;
62 public CachingPipeline(FiltersManager filtersManager, String name) {
63 mFiltersManager = filtersManager;
67 public static synchronized RenderScript getRenderScriptContext() {
71 public static synchronized void createRenderscriptContext(Activity context) {
73 Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
74 destroyRenderScriptContext();
76 sRS = RenderScript.create(context);
77 sResources = context.getResources();
80 public static synchronized void destroyRenderScriptContext() {
89 mEnvironment.setStop(true);
92 public synchronized void reset() {
93 synchronized (CachingPipeline.class) {
94 if (getRenderScriptContext() == null) {
97 mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
98 if (mResizedOriginalBitmap != null) {
99 mResizedOriginalBitmap.recycle();
100 mResizedOriginalBitmap = null;
102 if (mOriginalAllocation != null) {
103 mOriginalAllocation.destroy();
104 mOriginalAllocation = null;
106 if (mFiltersOnlyOriginalAllocation != null) {
107 mFiltersOnlyOriginalAllocation.destroy();
108 mFiltersOnlyOriginalAllocation = null;
110 mPreviousGeometry = null;
111 mPreviewScaleFactor = 1.0f;
112 mHighResPreviewScaleFactor = 1.0f;
114 destroyPixelAllocations();
118 public Resources getResources() {
119 return sRS.getApplicationContext().getResources();
122 private synchronized void destroyPixelAllocations() {
124 Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
126 if (mInPixelsAllocation != null) {
127 mInPixelsAllocation.destroy();
128 mInPixelsAllocation = null;
130 if (mOutPixelsAllocation != null) {
131 mOutPixelsAllocation.destroy();
132 mOutPixelsAllocation = null;
138 private String getType(RenderingRequest request) {
139 if (request.getType() == RenderingRequest.ICON_RENDERING) {
140 return "ICON_RENDERING";
142 if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
143 return "FILTERS_RENDERING";
145 if (request.getType() == RenderingRequest.FULL_RENDERING) {
146 return "FULL_RENDERING";
148 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
149 return "GEOMETRY_RENDERING";
151 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
152 return "PARTIAL_RENDERING";
154 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
155 return "HIGHRES_RENDERING";
157 return "UNKNOWN TYPE!";
160 private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
161 mEnvironment.setPipeline(this);
162 mEnvironment.setFiltersManager(mFiltersManager);
163 if (highResPreview) {
164 mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
166 mEnvironment.setScaleFactor(mPreviewScaleFactor);
168 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
169 mEnvironment.setImagePreset(preset);
170 mEnvironment.setStop(false);
173 public void setOriginal(Bitmap bitmap) {
174 mOriginalBitmap = bitmap;
175 Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
176 ImagePreset preset = MasterImage.getImage().getPreset();
177 setupEnvironment(preset, false);
178 updateOriginalAllocation(preset);
181 private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
182 Bitmap originalBitmap = mOriginalBitmap;
184 if (originalBitmap == null) {
188 GeometryMetadata geometry = preset.getGeometry();
189 if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) {
194 Log.v(LOGTAG, "geometry has changed");
197 RenderScript RS = getRenderScriptContext();
199 Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
200 mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
201 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
202 if (filtersOnlyOriginalAllocation != null) {
203 filtersOnlyOriginalAllocation.destroy();
206 Allocation originalAllocation = mOriginalAllocation;
207 mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
208 mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
209 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
210 if (originalAllocation != null) {
211 originalAllocation.destroy();
214 mPreviousGeometry = new GeometryMetadata(geometry);
218 public synchronized void render(RenderingRequest request) {
219 synchronized (CachingPipeline.class) {
220 if (getRenderScriptContext() == null) {
223 if (((request.getType() != RenderingRequest.PARTIAL_RENDERING
224 && request.getType() != RenderingRequest.HIGHRES_RENDERING)
225 && request.getBitmap() == null)
226 || request.getImagePreset() == null) {
231 Log.v(LOGTAG, "render image of type " + getType(request));
234 Bitmap bitmap = request.getBitmap();
235 ImagePreset preset = request.getImagePreset();
236 setupEnvironment(preset,
237 request.getType() != RenderingRequest.HIGHRES_RENDERING);
238 mFiltersManager.freeFilterResources(preset);
240 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
241 ImageLoader loader = MasterImage.getImage().getImageLoader();
242 if (loader == null) {
243 Log.w(LOGTAG, "loader not yet setup, cannot handle: " + getType(request));
246 bitmap = loader.getScaleOneImageForPreset(request.getBounds(),
247 request.getDestination());
248 if (bitmap == null) {
249 Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
254 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
255 ImageLoader loader = MasterImage.getImage().getImageLoader();
256 bitmap = loader.getOriginalBitmapHighres();
257 if (bitmap != null) {
258 bitmap = preset.applyGeometry(bitmap, mEnvironment);
262 if (request.getType() == RenderingRequest.FULL_RENDERING
263 || request.getType() == RenderingRequest.GEOMETRY_RENDERING
264 || request.getType() == RenderingRequest.FILTERS_RENDERING) {
265 updateOriginalAllocation(preset);
269 Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
270 + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
271 + mResizedOriginalBitmap.getHeight());
274 if (request.getType() == RenderingRequest.FULL_RENDERING
275 || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
276 mOriginalAllocation.copyTo(bitmap);
277 } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
278 mFiltersOnlyOriginalAllocation.copyTo(bitmap);
281 if (request.getType() == RenderingRequest.FULL_RENDERING
282 || request.getType() == RenderingRequest.FILTERS_RENDERING
283 || request.getType() == RenderingRequest.ICON_RENDERING
284 || request.getType() == RenderingRequest.PARTIAL_RENDERING
285 || request.getType() == RenderingRequest.HIGHRES_RENDERING
286 || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
288 if (request.getType() == RenderingRequest.ICON_RENDERING) {
289 mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
291 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
294 Bitmap bmp = preset.apply(bitmap, mEnvironment);
295 if (!mEnvironment.needsStop()) {
296 request.setBitmap(bmp);
298 mFiltersManager.freeFilterResources(preset);
303 public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
304 synchronized (CachingPipeline.class) {
305 if (getRenderScriptContext() == null) {
308 setupEnvironment(preset, false);
309 mFiltersManager.freeFilterResources(preset);
310 preset.applyFilters(-1, -1, in, out, mEnvironment);
311 boolean copyOut = false;
312 if (preset.nbFilters() > 0) {
315 preset.applyBorder(in, out, copyOut, mEnvironment);
319 public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
320 synchronized (CachingPipeline.class) {
321 if (getRenderScriptContext() == null) {
324 setupEnvironment(preset, false);
325 mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL);
326 mEnvironment.setScaleFactor(1.0f);
327 mFiltersManager.freeFilterResources(preset);
328 bitmap = preset.applyGeometry(bitmap, mEnvironment);
329 bitmap = preset.apply(bitmap, mEnvironment);
334 public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
335 // Called by RenderRequest on the main thread
336 // TODO: change this -- we should reuse a pool of bitmaps instead...
337 if (mGeometry == null) {
338 mGeometry = new ImageFilterGeometry();
340 mGeometry.useRepresentation(preset.getGeometry());
341 return mGeometry.apply(bitmap, mPreviewScaleFactor,
342 FilterEnvironment.QUALITY_PREVIEW);
345 public synchronized void compute(SharedBuffer buffer, ImagePreset preset, int type) {
346 synchronized (CachingPipeline.class) {
347 if (getRenderScriptContext() == null) {
351 Log.v(LOGTAG, "compute preset " + preset);
352 preset.showFilters();
355 String thread = Thread.currentThread().getName();
356 long time = System.currentTimeMillis();
357 setupEnvironment(preset, false);
358 mFiltersManager.freeFilterResources(preset);
360 Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
361 if (updateOriginalAllocation(preset) || buffer.getProducer() == null) {
362 resizedOriginalBitmap = mResizedOriginalBitmap;
363 buffer.setProducer(resizedOriginalBitmap);
364 mEnvironment.cache(buffer.getProducer());
367 Bitmap bitmap = buffer.getProducer().getBitmap();
368 long time2 = System.currentTimeMillis();
370 if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
371 || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
372 mEnvironment.cache(buffer.getProducer());
373 buffer.setProducer(resizedOriginalBitmap);
374 bitmap = buffer.getProducer().getBitmap();
376 mOriginalAllocation.copyTo(bitmap);
378 Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
379 if (tmpbitmap != bitmap) {
380 mEnvironment.cache(buffer.getProducer());
381 buffer.setProducer(tmpbitmap);
384 mFiltersManager.freeFilterResources(preset);
386 time = System.currentTimeMillis() - time;
387 time2 = System.currentTimeMillis() - time2;
389 Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
390 + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
391 + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
396 public boolean needsRepaint() {
397 SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
398 return buffer.checkRepaintNeeded();
401 public void setPreviewScaleFactor(float previewScaleFactor) {
402 mPreviewScaleFactor = previewScaleFactor;
405 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
406 mHighResPreviewScaleFactor = highResPreviewScaleFactor;
409 public synchronized boolean isInitialized() {
410 return getRenderScriptContext() != null && mOriginalBitmap != null;
413 public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
414 RenderScript RS = getRenderScriptContext();
415 boolean needsUpdate = false;
416 if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
417 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
418 destroyPixelAllocations();
419 Bitmap bitmapBuffer = bitmap;
420 if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
421 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
423 mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
424 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
425 mInPixelsAllocation = Allocation.createTyped(RS,
426 mOutPixelsAllocation.getType());
430 mInPixelsAllocation.copyFrom(bitmap);
432 if (bitmap.getWidth() != mWidth
433 || bitmap.getHeight() != mHeight) {
434 mWidth = bitmap.getWidth();
435 mHeight = bitmap.getHeight();
439 Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
444 public synchronized Allocation getInPixelsAllocation() {
445 return mInPixelsAllocation;
448 public synchronized Allocation getOutPixelsAllocation() {
449 return mOutPixelsAllocation;
452 public String getName() {
456 public RenderScript getRSContext() {
457 return CachingPipeline.getRenderScriptContext();