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.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.FilterRepresentation;
28 import com.android.gallery3d.filtershow.filters.FiltersManager;
29 import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
30 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
31 import com.android.gallery3d.filtershow.imageshow.MasterImage;
33 import java.util.Vector;
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();
49 private CacheProcessing mCachedProcessing = new CacheProcessing();
52 private volatile Allocation mOriginalAllocation = null;
53 private volatile Allocation mFiltersOnlyOriginalAllocation = null;
55 protected volatile Allocation mInPixelsAllocation;
56 protected volatile Allocation mOutPixelsAllocation;
57 private volatile int mWidth = 0;
58 private volatile int mHeight = 0;
60 private volatile GeometryMetadata mPreviousGeometry = null;
61 private volatile float mPreviewScaleFactor = 1.0f;
62 private volatile float mHighResPreviewScaleFactor = 1.0f;
63 private volatile String mName = "";
65 private ImageFilterGeometry mGeometry = null;
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);
82 sResources = context.getResources();
85 public static synchronized void destroyRenderScriptContext() {
94 mEnvironment.setStop(true);
97 public synchronized void reset() {
98 synchronized (CachingPipeline.class) {
99 if (getRenderScriptContext() == null) {
102 mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
103 if (mResizedOriginalBitmap != null) {
104 mResizedOriginalBitmap.recycle();
105 mResizedOriginalBitmap = null;
107 if (mOriginalAllocation != null) {
108 mOriginalAllocation.destroy();
109 mOriginalAllocation = null;
111 if (mFiltersOnlyOriginalAllocation != null) {
112 mFiltersOnlyOriginalAllocation.destroy();
113 mFiltersOnlyOriginalAllocation = null;
115 mPreviousGeometry = null;
116 mPreviewScaleFactor = 1.0f;
117 mHighResPreviewScaleFactor = 1.0f;
119 destroyPixelAllocations();
123 public Resources getResources() {
124 return sRS.getApplicationContext().getResources();
127 private synchronized void destroyPixelAllocations() {
129 Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
131 if (mInPixelsAllocation != null) {
132 mInPixelsAllocation.destroy();
133 mInPixelsAllocation = null;
135 if (mOutPixelsAllocation != null) {
136 mOutPixelsAllocation.destroy();
137 mOutPixelsAllocation = null;
143 private String getType(RenderingRequest request) {
144 if (request.getType() == RenderingRequest.ICON_RENDERING) {
145 return "ICON_RENDERING";
147 if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
148 return "FILTERS_RENDERING";
150 if (request.getType() == RenderingRequest.FULL_RENDERING) {
151 return "FULL_RENDERING";
153 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
154 return "GEOMETRY_RENDERING";
156 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
157 return "PARTIAL_RENDERING";
159 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
160 return "HIGHRES_RENDERING";
162 return "UNKNOWN TYPE!";
165 private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
166 mEnvironment.setPipeline(this);
167 mEnvironment.setFiltersManager(mFiltersManager);
168 if (highResPreview) {
169 mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
171 mEnvironment.setScaleFactor(mPreviewScaleFactor);
173 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
174 mEnvironment.setImagePreset(preset);
175 mEnvironment.setStop(false);
178 public void setOriginal(Bitmap bitmap) {
179 mOriginalBitmap = bitmap;
180 Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
181 ImagePreset preset = MasterImage.getImage().getPreset();
182 setupEnvironment(preset, false);
183 updateOriginalAllocation(preset);
186 private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
187 Bitmap originalBitmap = mOriginalBitmap;
189 if (originalBitmap == null) {
193 GeometryMetadata geometry = preset.getGeometry();
194 if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) {
199 Log.v(LOGTAG, "geometry has changed");
202 RenderScript RS = getRenderScriptContext();
204 Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
205 mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
206 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
207 if (filtersOnlyOriginalAllocation != null) {
208 filtersOnlyOriginalAllocation.destroy();
211 Allocation originalAllocation = mOriginalAllocation;
212 mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
213 mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
214 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
215 if (originalAllocation != null) {
216 originalAllocation.destroy();
219 mPreviousGeometry = new GeometryMetadata(geometry);
223 public synchronized void render(RenderingRequest request) {
224 synchronized (CachingPipeline.class) {
225 if (getRenderScriptContext() == null) {
228 if (((request.getType() != RenderingRequest.PARTIAL_RENDERING
229 && request.getType() != RenderingRequest.HIGHRES_RENDERING)
230 && request.getBitmap() == null)
231 || request.getImagePreset() == null) {
236 Log.v(LOGTAG, "render image of type " + getType(request));
239 Bitmap bitmap = request.getBitmap();
240 ImagePreset preset = request.getImagePreset();
241 setupEnvironment(preset,
242 request.getType() != RenderingRequest.HIGHRES_RENDERING);
243 mFiltersManager.freeFilterResources(preset);
245 if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
246 MasterImage master = MasterImage.getImage();
247 bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(),
248 master.getUri(), request.getBounds(),
249 request.getDestination());
250 if (bitmap == null) {
251 Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
256 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
257 bitmap = MasterImage.getImage().getOriginalBitmapHighres();
258 if (bitmap != null) {
259 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 void compute(SharedBuffer buffer, ImagePreset preset, int type) {
347 if (getRenderScriptContext() == null) {
350 setupEnvironment(preset, false);
351 Vector<FilterRepresentation> filters = preset.getFilters();
352 Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment);
353 buffer.setProducer(result);
356 public synchronized void computeOld(SharedBuffer buffer, ImagePreset preset, int type) {
357 synchronized (CachingPipeline.class) {
358 if (getRenderScriptContext() == null) {
362 Log.v(LOGTAG, "compute preset " + preset);
363 preset.showFilters();
366 String thread = Thread.currentThread().getName();
367 long time = System.currentTimeMillis();
368 setupEnvironment(preset, false);
369 mFiltersManager.freeFilterResources(preset);
371 Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
372 if (updateOriginalAllocation(preset) || buffer.getProducer() == null) {
373 resizedOriginalBitmap = mResizedOriginalBitmap;
374 buffer.setProducer(resizedOriginalBitmap);
375 mEnvironment.cache(buffer.getProducer());
378 Bitmap bitmap = buffer.getProducer().getBitmap();
379 long time2 = System.currentTimeMillis();
381 if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
382 || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
383 mEnvironment.cache(buffer.getProducer());
384 buffer.setProducer(resizedOriginalBitmap);
385 bitmap = buffer.getProducer().getBitmap();
387 mOriginalAllocation.copyTo(bitmap);
389 Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
390 if (tmpbitmap != bitmap) {
391 mEnvironment.cache(buffer.getProducer());
392 buffer.setProducer(tmpbitmap);
395 mFiltersManager.freeFilterResources(preset);
397 time = System.currentTimeMillis() - time;
398 time2 = System.currentTimeMillis() - time2;
400 Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
401 + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
402 + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
407 public boolean needsRepaint() {
408 SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
409 return buffer.checkRepaintNeeded();
412 public void setPreviewScaleFactor(float previewScaleFactor) {
413 mPreviewScaleFactor = previewScaleFactor;
416 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
417 mHighResPreviewScaleFactor = highResPreviewScaleFactor;
420 public synchronized boolean isInitialized() {
421 return getRenderScriptContext() != null && mOriginalBitmap != null;
424 public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
425 RenderScript RS = getRenderScriptContext();
426 boolean needsUpdate = false;
427 if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
428 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
429 destroyPixelAllocations();
430 Bitmap bitmapBuffer = bitmap;
431 if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
432 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
434 mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
435 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
436 mInPixelsAllocation = Allocation.createTyped(RS,
437 mOutPixelsAllocation.getType());
441 mInPixelsAllocation.copyFrom(bitmap);
443 if (bitmap.getWidth() != mWidth
444 || bitmap.getHeight() != mHeight) {
445 mWidth = bitmap.getWidth();
446 mHeight = bitmap.getHeight();
450 Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
455 public synchronized Allocation getInPixelsAllocation() {
456 return mInPixelsAllocation;
459 public synchronized Allocation getOutPixelsAllocation() {
460 return mOutPixelsAllocation;
463 public String getName() {
467 public RenderScript getRSContext() {
468 return CachingPipeline.getRenderScriptContext();