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.graphics.Bitmap;
21 import android.os.Process;
22 import android.support.v8.renderscript.*;
23 import android.util.Log;
25 import com.android.gallery3d.filtershow.filters.ImageFilterRS;
26 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
27 import com.android.gallery3d.filtershow.imageshow.MasterImage;
28 import com.android.gallery3d.filtershow.presets.ImagePreset;
30 public class FilteringPipeline implements Handler.Callback {
32 private final static FilteringPipeline gPipeline = new FilteringPipeline();
33 private static final String LOGTAG = "FilteringPipeline";
34 private ImagePreset mPreviousPreset = null;
35 private ImagePreset mPreviousGeometryPreset = null;
36 private ImagePreset mPreviousFiltersPreset = null;
37 private GeometryMetadata mPreviousGeometry = null;
39 private Bitmap mOriginalBitmap = null;
40 private Bitmap mResizedOriginalBitmap = null;
42 private boolean DEBUG = false;
44 private HandlerThread mHandlerThread = null;
45 private final static int NEW_PRESET = 0;
46 private final static int COMPUTE_PRESET = 1;
47 private final static int COMPUTE_GEOMETRY_PRESET = 2;
48 private final static int COMPUTE_FILTERS_PRESET = 3;
50 private boolean mProcessing = false;
52 private Handler mProcessingHandler = null;
53 private final Handler mUIHandler = new Handler() {
55 public void handleMessage(Message msg) {
58 MasterImage.getImage().notifyObservers();
67 public boolean handleMessage(Message msg) {
69 case COMPUTE_PRESET: {
70 ImagePreset preset = MasterImage.getImage().getPreset();
71 TripleBufferBitmap buffer = MasterImage.getImage().getDoubleBuffer();
72 compute(buffer, preset, COMPUTE_PRESET);
73 Message uimsg = mUIHandler.obtainMessage(NEW_PRESET);
74 mUIHandler.sendMessage(uimsg);
77 case COMPUTE_GEOMETRY_PRESET: {
78 ImagePreset preset = MasterImage.getImage().getGeometryPreset();
79 TripleBufferBitmap buffer = MasterImage.getImage().getGeometryOnlyBuffer();
80 compute(buffer, preset, COMPUTE_GEOMETRY_PRESET);
81 Message uimsg = mUIHandler.obtainMessage(NEW_PRESET);
82 mUIHandler.sendMessage(uimsg);
85 case COMPUTE_FILTERS_PRESET: {
86 ImagePreset preset = MasterImage.getImage().getFiltersOnlyPreset();
87 TripleBufferBitmap buffer = MasterImage.getImage().getFiltersOnlyBuffer();
88 compute(buffer, preset, COMPUTE_FILTERS_PRESET);
89 Message uimsg = mUIHandler.obtainMessage(NEW_PRESET);
90 mUIHandler.sendMessage(uimsg);
97 private static float RESIZE_FACTOR = 0.8f;
98 private static float MAX_PROCESS_TIME = 100; // in ms
99 private float mResizeFactor = 1.0f;
100 private long mResizeTime = 0;
102 private Allocation mOriginalBitmapAllocation = null;
103 private Allocation mOriginalAllocation = null;
104 private Allocation mFiltersOnlyOriginalAllocation = null;
106 private FilteringPipeline() {
107 mHandlerThread = new HandlerThread("FilteringPipeline",
108 Process.THREAD_PRIORITY_FOREGROUND);
109 mHandlerThread.start();
110 mProcessingHandler = new Handler(mHandlerThread.getLooper(), this);
113 public static FilteringPipeline getPipeline() {
117 public synchronized void setOriginal(Bitmap bitmap) {
118 mOriginalBitmap = bitmap;
119 Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
120 updateOriginalAllocation(MasterImage.getImage().getPreset());
123 public synchronized boolean updateOriginalAllocation(ImagePreset preset) {
124 if (mOriginalBitmap == null) {
128 //FIXME: turn back on the on-the-fly resize.
129 int w = (int) (mOriginalBitmap.getWidth() * mResizeFactor);
130 int h = (int) (mOriginalBitmap.getHeight() * mResizeFactor);
131 if (!needsGeometryRepaint() && mResizedOriginalBitmap != null && w == mResizedOriginalBitmap.getWidth()) {
134 mResizedOriginalBitmap = Bitmap.createScaledBitmap(mOriginalBitmap, w, h, true);
136 GeometryMetadata geometry = preset.getGeometry();
137 if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) {
140 RenderScript RS = ImageFilterRS.getRenderScriptContext();
141 if (mFiltersOnlyOriginalAllocation != null) {
142 mFiltersOnlyOriginalAllocation.destroy();
144 mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, mOriginalBitmap,
145 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
146 if (mOriginalAllocation != null) {
147 mOriginalAllocation.destroy();
149 mResizedOriginalBitmap = preset.applyGeometry(mOriginalBitmap);
150 mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
151 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
152 mPreviousGeometry = new GeometryMetadata(geometry);
156 public synchronized void updatePreviewBuffer() {
157 if (mOriginalAllocation == null) {
163 if (mProcessingHandler.hasMessages(COMPUTE_PRESET)) {
166 if (!needsRepaint()) {
169 Message msg = mProcessingHandler.obtainMessage(COMPUTE_PRESET);
170 mProcessingHandler.sendMessage(msg);
174 public void updateGeometryOnlyPreviewBuffer() {
175 if (mOriginalAllocation == null) {
181 if (mProcessingHandler.hasMessages(COMPUTE_GEOMETRY_PRESET)) {
184 if (!needsGeometryRepaint()) {
187 Message msg = mProcessingHandler.obtainMessage(COMPUTE_GEOMETRY_PRESET);
188 mProcessingHandler.sendMessage(msg);
192 public void updateFiltersOnlyPreviewBuffer() {
193 if (mOriginalAllocation == null) {
199 if (mProcessingHandler.hasMessages(COMPUTE_FILTERS_PRESET)) {
202 if (!needsFiltersRepaint()) {
205 Message msg = mProcessingHandler.obtainMessage(COMPUTE_FILTERS_PRESET);
206 mProcessingHandler.sendMessage(msg);
210 private void compute(TripleBufferBitmap buffer, ImagePreset preset, int type) {
211 String thread = Thread.currentThread().getName();
212 long time = System.currentTimeMillis();
213 if (updateOriginalAllocation(preset)) {
214 buffer.updateBitmaps(mResizedOriginalBitmap);
216 Bitmap bitmap = buffer.getProducer();
217 long time2 = System.currentTimeMillis();
219 if (type != COMPUTE_FILTERS_PRESET) {
220 if (bitmap == null || (bitmap.getWidth() != mResizedOriginalBitmap.getWidth())
221 || (bitmap.getHeight() != mResizedOriginalBitmap.getHeight())) {
222 buffer.updateBitmaps(mResizedOriginalBitmap);
223 bitmap = buffer.getProducer();
225 mOriginalAllocation.copyTo(bitmap);
227 if (bitmap == null || (bitmap.getWidth() != mOriginalBitmap.getWidth())
228 || (bitmap.getHeight() != mOriginalBitmap.getHeight())) {
229 buffer.updateBitmaps(mOriginalBitmap);
230 bitmap = buffer.getProducer();
232 mFiltersOnlyOriginalAllocation.copyTo(bitmap);
235 if (mOriginalAllocation == null || bitmap == null) {
236 Log.v(LOGTAG, "exiting compute because mOriginalAllocation: " + mOriginalAllocation + " or bitmap: " + bitmap);
240 if (type != COMPUTE_GEOMETRY_PRESET) {
241 bitmap = preset.apply(bitmap);
244 buffer.swapProducer();
245 time = System.currentTimeMillis() - time;
246 time2 = System.currentTimeMillis() - time2;
248 Log.v(LOGTAG, "Applying " + type + " filters to bitmap " + bitmap + " took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
250 if (type == COMPUTE_PRESET) {
251 mPreviousPreset = new ImagePreset(preset);
252 if (mResizeFactor > 0.6 && time > MAX_PROCESS_TIME && (System.currentTimeMillis() + 1000 > mResizeTime)) {
253 mResizeTime = System.currentTimeMillis();
254 mResizeFactor *= RESIZE_FACTOR;
256 } else if (type == COMPUTE_GEOMETRY_PRESET) {
257 mPreviousGeometryPreset = new ImagePreset(preset);
258 } else if (type == COMPUTE_FILTERS_PRESET) {
259 mPreviousFiltersPreset = new ImagePreset(preset);
263 private synchronized boolean needsRepaint() {
264 ImagePreset preset = MasterImage.getImage().getPreset();
265 if (preset == null || mPreviousPreset == null) {
268 if (preset.equals(mPreviousPreset)) {
274 private synchronized boolean needsGeometryRepaint() {
275 ImagePreset preset = MasterImage.getImage().getPreset();
276 if (preset == null || mPreviousGeometry == null || mPreviousGeometryPreset == null) {
279 GeometryMetadata geometry = preset.getGeometry();
280 if (geometry.equals(mPreviousGeometryPreset.getGeometry())) {
286 private synchronized boolean needsFiltersRepaint() {
287 ImagePreset preset = MasterImage.getImage().getPreset();
288 if (preset == null || mPreviousFiltersPreset == null) {
291 if (preset.equals(mPreviousFiltersPreset)) {