From 57eedfd99676913f575b1f76e3a874026d1ddf7c Mon Sep 17 00:00:00 2001 From: nicolasroard Date: Tue, 15 Jan 2013 19:57:49 -0800 Subject: [PATCH] Better caching for RS filters Change-Id: I78eaa90e408059cf1c59fc06920f5aef82ae2c0d --- .../gallery3d/filtershow/FilterShowActivity.java | 17 +- .../gallery3d/filtershow/PanelController.java | 3 +- .../filtershow/cache/DelayedPresetCache.java | 75 ------ .../filtershow/cache/DirectPresetCache.java | 183 ------------- .../filtershow/cache/FilteringPipeline.java | 298 +++++++++++++++++++++ .../gallery3d/filtershow/cache/ImageLoader.java | 61 +---- .../filtershow/cache/TripleBufferBitmap.java | 71 +++++ .../gallery3d/filtershow/filters/ImageFilter.java | 10 + .../filtershow/filters/ImageFilterRS.java | 44 ++- .../filtershow/imageshow/ImageGeometry.java | 1 - .../gallery3d/filtershow/imageshow/ImageShow.java | 16 +- .../gallery3d/filtershow/imageshow/ImageZoom.java | 1 - .../filtershow/imageshow/MasterImage.java | 94 ++++--- .../gallery3d/filtershow/presets/ImagePreset.java | 87 +++++- 14 files changed, 562 insertions(+), 399 deletions(-) delete mode 100644 src/com/android/gallery3d/filtershow/cache/DelayedPresetCache.java delete mode 100644 src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java create mode 100644 src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java create mode 100644 src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index 376538086..1e2bad34e 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -220,9 +220,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageViews.add(mImageFlip); mImageViews.add(mImageTinyPlanet); mImageViews.add(mImageRedEyes); - for (ImageShow imageShow : mImageViews) { - mImageLoader.addCacheListener(imageShow); - } mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer)); EditorManager.addEditors(mEditorPlaceHolder); @@ -961,14 +958,18 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, if (mMasterImage.getCurrentFilter() == filter) { return; } - mMasterImage.setCurrentFilter(filter); - ImagePreset oldPreset = mImageShow.getImagePreset(); + ImagePreset oldPreset = mMasterImage.getPreset(); ImagePreset copy = new ImagePreset(oldPreset); + mMasterImage.setPreset(copy, true); // TODO: use a numerical constant instead. - copy.add(filter); - - mMasterImage.setPreset(copy, true); + ImagePreset current = mMasterImage.getPreset(); + ImageFilter existingFilter = current.getFilter(filter.getName()); + if (existingFilter == null) { + current.add(filter); + } + existingFilter = current.getFilter(filter.getName()); + mMasterImage.setCurrentFilter(existingFilter); invalidateViews(); } diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java index 7f99218de..a0b13fb84 100644 --- a/src/com/android/gallery3d/filtershow/PanelController.java +++ b/src/com/android/gallery3d/filtershow/PanelController.java @@ -483,7 +483,8 @@ public class PanelController implements OnClickListener { mUtilityPanel.setEffectName(ename); mUtilityPanel.setShowParameter(filter.showParameterValue()); - mMasterImage.setCurrentFilter(filter); + ImageFilter currentFilter = mMasterImage.getPreset().getFilter(filter.getName()); + mMasterImage.setCurrentFilter(currentFilter); mCurrentImage.select(); if (mCurrentEditor != null) { mCurrentEditor.reflectCurrentFilter(); diff --git a/src/com/android/gallery3d/filtershow/cache/DelayedPresetCache.java b/src/com/android/gallery3d/filtershow/cache/DelayedPresetCache.java deleted file mode 100644 index 408ba5912..000000000 --- a/src/com/android/gallery3d/filtershow/cache/DelayedPresetCache.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.filtershow.cache; - -import android.os.Handler; -import android.os.Handler.Callback; -import android.os.HandlerThread; -import android.os.Message; -import android.os.Process; - -public class DelayedPresetCache extends DirectPresetCache implements Callback { - private HandlerThread mHandlerThread = null; - - private final static int NEW_PRESET = 0; - private final static int COMPUTE_PRESET = 1; - - private Handler mProcessingHandler = null; - private final Handler mUIHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case NEW_PRESET: { - CachedPreset cache = (CachedPreset) msg.obj; - didCompute(cache); - break; - } - } - } - }; - - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case COMPUTE_PRESET: { - CachedPreset cache = (CachedPreset) msg.obj; - compute(cache); - Message uimsg = mUIHandler.obtainMessage(NEW_PRESET, cache); - mUIHandler.sendMessage(uimsg); - break; - } - } - return false; - } - - public DelayedPresetCache(ImageLoader loader, int size) { - super(loader, size); - mHandlerThread = new HandlerThread("ImageProcessing", Process.THREAD_PRIORITY_BACKGROUND); - mHandlerThread.start(); - mProcessingHandler = new Handler(mHandlerThread.getLooper(), this); - } - - @Override - protected void willCompute(CachedPreset cache) { - if (cache == null) { - return; - } - cache.setBusy(true); - Message msg = mProcessingHandler.obtainMessage(COMPUTE_PRESET, cache); - mProcessingHandler.sendMessage(msg); - } -} diff --git a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java deleted file mode 100644 index d58e953c2..000000000 --- a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.filtershow.cache; - -import android.graphics.Bitmap; - -import com.android.gallery3d.filtershow.imageshow.ImageShow; -import com.android.gallery3d.filtershow.imageshow.MasterImage; -import com.android.gallery3d.filtershow.presets.ImagePreset; - -import java.util.Vector; - -public class DirectPresetCache implements Cache { - - private static final String LOGTAG = "DirectPresetCache"; - private Bitmap mOriginalBitmap = null; - private final Vector mObservers = new Vector(); - private final Vector mCache = new Vector(); - private int mCacheSize = 1; - private final Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; - private long mGlobalAge = 0; - private ImageLoader mLoader = null; - - protected class CachedPreset { - private Bitmap mBitmap = null; - private ImagePreset mPreset = null; - private long mAge = 0; - private boolean mBusy = false; - - public void setBusy(boolean value) { - mBusy = value; - } - - public boolean busy() { - return mBusy; - } - } - - public DirectPresetCache(ImageLoader loader, int size) { - mLoader = loader; - mCacheSize = size; - } - - @Override - public void setOriginalBitmap(Bitmap bitmap) { - mOriginalBitmap = bitmap; - notifyObservers(); - } - - public void notifyObservers() { - mLoader.getActivity().runOnUiThread(mNotifyObserversRunnable); - } - - private final Runnable mNotifyObserversRunnable = new Runnable() { - @Override - public void run() { - for (int i = 0; i < mObservers.size(); i++) { - ImageShow imageShow = mObservers.elementAt(i); - // FIXME: need to replace the observer from ImageShow to - // MasterImage - if (imageShow != null) { - imageShow.invalidate(); - imageShow.updateImage(); - } - MasterImage.getImage().updatedCache(); - } - } - }; - - @Override - public void addObserver(ImageShow observer) { - if (!mObservers.contains(observer)) { - mObservers.add(observer); - } - } - - private CachedPreset getCachedPreset(ImagePreset preset) { - for (int i = 0; i < mCache.size(); i++) { - CachedPreset cache = mCache.elementAt(i); - if (cache.mPreset == preset) { - return cache; - } - } - return null; - } - - @Override - public Bitmap get(ImagePreset preset) { - CachedPreset cache = getCachedPreset(preset); - if (cache != null && !cache.mBusy) { - return cache.mBitmap; - } - return null; - } - - @Override - public void reset(ImagePreset preset) { - CachedPreset cache = getCachedPreset(preset); - if (cache != null && !cache.mBusy) { - cache.mBitmap = null; - willCompute(cache); - } - } - - private CachedPreset getOldestCachedPreset() { - CachedPreset found = null; - for (int i = 0; i < mCache.size(); i++) { - CachedPreset cache = mCache.elementAt(i); - if (cache.mBusy) { - continue; - } - if (found == null) { - found = cache; - } else { - if (found.mAge > cache.mAge) { - found = cache; - } - } - } - return found; - } - - protected void willCompute(CachedPreset cache) { - if (cache == null) { - return; - } - cache.mBusy = true; - compute(cache); - didCompute(cache); - } - - protected void didCompute(CachedPreset cache) { - cache.mBusy = false; - notifyObservers(); - } - - protected void compute(CachedPreset cache) { - cache.mBitmap = null; - cache.mBitmap = mOriginalBitmap.copy(mBitmapConfig, true); - float scaleFactor = (float) cache.mBitmap.getWidth() / (float) mLoader.getOriginalBounds().width(); - if (scaleFactor < 1.0f) { - cache.mPreset.setIsHighQuality(false); - } - cache.mPreset.setScaleFactor(scaleFactor); - cache.mBitmap = cache.mPreset.apply(cache.mBitmap); - cache.mAge = mGlobalAge++; - } - - @Override - public void prepare(ImagePreset preset) { - CachedPreset cache = getCachedPreset(preset); - if (cache == null || (cache.mBitmap == null && !cache.mBusy)) { - if (cache == null) { - if (mCache.size() < mCacheSize) { - cache = new CachedPreset(); - mCache.add(cache); - } else { - cache = getOldestCachedPreset(); - } - } - if (cache != null) { - cache.mPreset = preset; - willCompute(cache); - } - } - - } - -} diff --git a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java new file mode 100644 index 000000000..a1dbe8e52 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.cache; + +import android.graphics.Bitmap; +import android.os.*; +import android.os.Process; +import android.renderscript.Allocation; +import android.renderscript.Allocation.MipmapControl; +import android.renderscript.RenderScript; +import android.util.Log; + +import com.android.gallery3d.filtershow.filters.ImageFilterRS; +import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.presets.ImagePreset; + +public class FilteringPipeline implements Handler.Callback { + + private final static FilteringPipeline gPipeline = new FilteringPipeline(); + private static final String LOGTAG = "FilteringPipeline"; + private ImagePreset mPreviousPreset = null; + private ImagePreset mPreviousGeometryPreset = null; + private ImagePreset mPreviousFiltersPreset = null; + private GeometryMetadata mPreviousGeometry = null; + + private Bitmap mOriginalBitmap = null; + private Bitmap mResizedOriginalBitmap = null; + + private boolean DEBUG = false; + + private HandlerThread mHandlerThread = null; + private final static int NEW_PRESET = 0; + private final static int COMPUTE_PRESET = 1; + private final static int COMPUTE_GEOMETRY_PRESET = 2; + private final static int COMPUTE_FILTERS_PRESET = 3; + + private boolean mProcessing = false; + + private Handler mProcessingHandler = null; + private final Handler mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case NEW_PRESET: { + MasterImage.getImage().notifyObservers(); + mProcessing = false; + break; + } + } + } + }; + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case COMPUTE_PRESET: { + ImagePreset preset = MasterImage.getImage().getPreset(); + TripleBufferBitmap buffer = MasterImage.getImage().getDoubleBuffer(); + compute(buffer, preset, COMPUTE_PRESET); + Message uimsg = mUIHandler.obtainMessage(NEW_PRESET); + mUIHandler.sendMessage(uimsg); + break; + } + case COMPUTE_GEOMETRY_PRESET: { + ImagePreset preset = MasterImage.getImage().getGeometryPreset(); + TripleBufferBitmap buffer = MasterImage.getImage().getGeometryOnlyBuffer(); + compute(buffer, preset, COMPUTE_GEOMETRY_PRESET); + Message uimsg = mUIHandler.obtainMessage(NEW_PRESET); + mUIHandler.sendMessage(uimsg); + break; + } + case COMPUTE_FILTERS_PRESET: { + ImagePreset preset = MasterImage.getImage().getFiltersOnlyPreset(); + TripleBufferBitmap buffer = MasterImage.getImage().getFiltersOnlyBuffer(); + compute(buffer, preset, COMPUTE_FILTERS_PRESET); + Message uimsg = mUIHandler.obtainMessage(NEW_PRESET); + mUIHandler.sendMessage(uimsg); + break; + } + } + return false; + } + + private static float RESIZE_FACTOR = 0.8f; + private static float MAX_PROCESS_TIME = 100; // in ms + private float mResizeFactor = 1.0f; + private long mResizeTime = 0; + + private Allocation mOriginalBitmapAllocation = null; + private Allocation mOriginalAllocation = null; + private Allocation mFiltersOnlyOriginalAllocation = null; + + private FilteringPipeline() { + mHandlerThread = new HandlerThread("FilteringPipeline", + Process.THREAD_PRIORITY_FOREGROUND); + mHandlerThread.start(); + mProcessingHandler = new Handler(mHandlerThread.getLooper(), this); + } + + public static FilteringPipeline getPipeline() { + return gPipeline; + } + + public synchronized void setOriginal(Bitmap bitmap) { + mOriginalBitmap = bitmap; + Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight()); + updateOriginalAllocation(MasterImage.getImage().getPreset()); + } + + public synchronized boolean updateOriginalAllocation(ImagePreset preset) { + if (mOriginalBitmap == null) { + return false; + } + /* + //FIXME: turn back on the on-the-fly resize. + int w = (int) (mOriginalBitmap.getWidth() * mResizeFactor); + int h = (int) (mOriginalBitmap.getHeight() * mResizeFactor); + if (!needsGeometryRepaint() && mResizedOriginalBitmap != null && w == mResizedOriginalBitmap.getWidth()) { + return false; + } + mResizedOriginalBitmap = Bitmap.createScaledBitmap(mOriginalBitmap, w, h, true); + */ + GeometryMetadata geometry = preset.getGeometry(); + if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) { + return false; + } + RenderScript RS = ImageFilterRS.getRenderScriptContext(); + if (mFiltersOnlyOriginalAllocation != null) { + mFiltersOnlyOriginalAllocation.destroy(); + } + mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, mOriginalBitmap, + MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + if (mOriginalAllocation != null) { + mOriginalAllocation.destroy(); + } + mResizedOriginalBitmap = preset.applyGeometry(mOriginalBitmap); + mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap, + MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + mPreviousGeometry = new GeometryMetadata(geometry); + return true; + } + + public synchronized void updatePreviewBuffer() { + if (mOriginalAllocation == null) { + return; + } + if (mProcessing) { + return; + } + if (mProcessingHandler.hasMessages(COMPUTE_PRESET)) { + return; + } + if (!needsRepaint()) { + return; + } + Message msg = mProcessingHandler.obtainMessage(COMPUTE_PRESET); + mProcessingHandler.sendMessage(msg); + mProcessing = true; + } + + public void updateGeometryOnlyPreviewBuffer() { + if (mOriginalAllocation == null) { + return; + } + if (mProcessing) { + return; + } + if (mProcessingHandler.hasMessages(COMPUTE_GEOMETRY_PRESET)) { + return; + } + if (!needsGeometryRepaint()) { + return; + } + Message msg = mProcessingHandler.obtainMessage(COMPUTE_GEOMETRY_PRESET); + mProcessingHandler.sendMessage(msg); + mProcessing = true; + } + + public void updateFiltersOnlyPreviewBuffer() { + if (mOriginalAllocation == null) { + return; + } + if (mProcessing) { + return; + } + if (mProcessingHandler.hasMessages(COMPUTE_FILTERS_PRESET)) { + return; + } + if (!needsFiltersRepaint()) { + return; + } + Message msg = mProcessingHandler.obtainMessage(COMPUTE_FILTERS_PRESET); + mProcessingHandler.sendMessage(msg); + mProcessing = true; + } + + private void compute(TripleBufferBitmap buffer, ImagePreset preset, int type) { + String thread = Thread.currentThread().getName(); + long time = System.currentTimeMillis(); + if (updateOriginalAllocation(preset)) { + buffer.updateBitmaps(mResizedOriginalBitmap); + } + Bitmap bitmap = buffer.getProducer(); + long time2 = System.currentTimeMillis(); + + if (type != COMPUTE_FILTERS_PRESET) { + if (bitmap == null || (bitmap.getWidth() != mResizedOriginalBitmap.getWidth()) + || (bitmap.getHeight() != mResizedOriginalBitmap.getHeight())) { + buffer.updateBitmaps(mResizedOriginalBitmap); + bitmap = buffer.getProducer(); + } + mOriginalAllocation.copyTo(bitmap); + } else { + if (bitmap == null || (bitmap.getWidth() != mOriginalBitmap.getWidth()) + || (bitmap.getHeight() != mOriginalBitmap.getHeight())) { + buffer.updateBitmaps(mOriginalBitmap); + bitmap = buffer.getProducer(); + } + mFiltersOnlyOriginalAllocation.copyTo(bitmap); + } + + if (mOriginalAllocation == null || bitmap == null) { + Log.v(LOGTAG, "exiting compute because mOriginalAllocation: " + mOriginalAllocation + " or bitmap: " + bitmap); + return; + } + + if (type != COMPUTE_GEOMETRY_PRESET) { + bitmap = preset.apply(bitmap); + } + + buffer.swapProducer(); + time = System.currentTimeMillis() - time; + time2 = System.currentTimeMillis() - time2; + if (DEBUG) { + Log.v(LOGTAG, "Applying " + type + " filters to bitmap " + bitmap + " took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread); + } + if (type == COMPUTE_PRESET) { + mPreviousPreset = new ImagePreset(preset); + if (mResizeFactor > 0.6 && time > MAX_PROCESS_TIME && (System.currentTimeMillis() + 1000 > mResizeTime)) { + mResizeTime = System.currentTimeMillis(); + mResizeFactor *= RESIZE_FACTOR; + } + } else if (type == COMPUTE_GEOMETRY_PRESET) { + mPreviousGeometryPreset = new ImagePreset(preset); + } else if (type == COMPUTE_FILTERS_PRESET) { + mPreviousFiltersPreset = new ImagePreset(preset); + } + } + + private synchronized boolean needsRepaint() { + ImagePreset preset = MasterImage.getImage().getPreset(); + if (preset == null || mPreviousPreset == null) { + return true; + } + if (preset.equals(mPreviousPreset)) { + return false; + } + return true; + } + + private synchronized boolean needsGeometryRepaint() { + ImagePreset preset = MasterImage.getImage().getPreset(); + if (preset == null || mPreviousGeometry == null || mPreviousGeometryPreset == null) { + return true; + } + GeometryMetadata geometry = preset.getGeometry(); + if (geometry.equals(mPreviousGeometryPreset.getGeometry())) { + return false; + } + return true; + } + + private synchronized boolean needsFiltersRepaint() { + ImagePreset preset = MasterImage.getImage().getPreset(); + if (preset == null || mPreviousFiltersPreset == null) { + return true; + } + if (preset.equals(mPreviousFiltersPreset)) { + return false; + } + return true; + } +} diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index 2c5bc1df9..85d0977e7 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -70,8 +70,6 @@ public class ImageLoader { private Bitmap mOriginalBitmapLarge = null; private Bitmap mBackgroundBitmap = null; - private Cache mCache = null; - private Cache mHiresCache = null; private final ZoomCache mZoomCache = new ZoomCache(); private int mOrientation = 0; @@ -104,8 +102,6 @@ public class ImageLoader { public ImageLoader(FilterShowActivity activity, Context context) { mActivity = activity; mContext = context; - mCache = new DelayedPresetCache(this, 30); - mHiresCache = new DelayedPresetCache(this, 3); } public static int getZoomOrientation() { @@ -216,8 +212,7 @@ public class ImageLoader { mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge, mOrientation); } mZoomOrientation = mOrientation; - mCache.setOriginalBitmap(mOriginalBitmapSmall); - mHiresCache.setOriginalBitmap(mOriginalBitmapLarge); + FilteringPipeline.getPipeline().setOriginal(mOriginalBitmapLarge); warnListeners(); } @@ -350,7 +345,6 @@ public class ImageLoader { if (!mListeners.contains(imageShow)) { mListeners.add(imageShow); } - mHiresCache.addObserver(imageShow); mLoadingLock.unlock(); } @@ -369,9 +363,8 @@ public class ImageLoader { } }; - // TODO: this currently does the loading + filtering on the UI thread -- - // need to - // move this to a background thread. + // FIXME: this currently does the loading + filtering on the UI thread -- + // need to move this to a background thread. public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds, boolean force) { mLoadingLock.lock(); @@ -394,48 +387,6 @@ public class ImageLoader { return bmp; } - // Caching method - public Bitmap getImageForPreset(ImageShow caller, ImagePreset imagePreset, - boolean hiRes) { - mLoadingLock.lock(); - if (mOriginalBitmapSmall == null) { - mLoadingLock.unlock(); - return null; - } - if (mOriginalBitmapLarge == null) { - mLoadingLock.unlock(); - return null; - } - - Bitmap filteredImage = null; - - if (hiRes) { - filteredImage = mHiresCache.get(imagePreset); - } else { - filteredImage = mCache.get(imagePreset); - } - - if (filteredImage == null) { - if (hiRes) { - mHiresCache.prepare(imagePreset); - mHiresCache.addObserver(caller); - } else { - mCache.prepare(imagePreset); - mCache.addObserver(caller); - } - } - mLoadingLock.unlock(); - return filteredImage; - } - - public void resetImageForPreset(ImagePreset imagePreset, ImageShow caller) { - mLoadingLock.lock(); - mHiresCache.reset(imagePreset); - mCache.reset(imagePreset); - mZoomCache.reset(imagePreset); - mLoadingLock.unlock(); - } - public void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity, File destination) { preset.setIsHighQuality(true); @@ -624,10 +575,4 @@ public class ImageLoader { Utils.closeSilently(is); } } - - public void addCacheListener(ImageShow imageShow) { - mHiresCache.addObserver(imageShow); - mCache.addObserver(imageShow); - } - } diff --git a/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java b/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java new file mode 100644 index 000000000..c4837ad4a --- /dev/null +++ b/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.cache; + +import android.graphics.Bitmap; +import com.android.gallery3d.app.Log; + +public class TripleBufferBitmap { + + private static String LOGTAG = "TripleBufferBitmap"; + + private Bitmap mBitmaps[] = new Bitmap[3]; + private Bitmap mProducer = null; + private Bitmap mConsumer = null; + private Bitmap mIntermediate = null; + private boolean mNeedsSwap = false; + + private final Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; + + public TripleBufferBitmap() { + + } + + public synchronized void updateBitmaps(Bitmap bitmap) { + mBitmaps[0] = bitmap.copy(mBitmapConfig, true); + mBitmaps[1] = bitmap.copy(mBitmapConfig, true); + mBitmaps[2] = bitmap.copy(mBitmapConfig, true); + mProducer = mBitmaps[0]; + mConsumer = mBitmaps[1]; + mIntermediate = mBitmaps[2]; + } + + public synchronized Bitmap getProducer() { + return mProducer; + } + + public synchronized Bitmap getConsumer() { + return mConsumer; + } + + public synchronized void swapProducer() { + Bitmap intermediate = mIntermediate; + mIntermediate = mProducer; + mProducer = intermediate; + mNeedsSwap = true; + } + + public synchronized void swapConsumer() { + if (!mNeedsSwap) { + return; + } + Bitmap intermediate = mIntermediate; + mIntermediate = mConsumer; + mConsumer = intermediate; + mNeedsSwap = false; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java index db74cc8bc..96f98d5b1 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java @@ -179,6 +179,16 @@ public class ImageFilter implements Cloneable { this.mImagePreset = mPreset; } + public boolean equals(ImageFilter filter) { + if (!same(filter)) { + return false; + } + if (mParameter != filter.mParameter) { + return false; + } + return true; + } + public boolean same(ImageFilter filter) { if (filter == null) { return false; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java index 6f7c36e28..63dc82c34 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java @@ -19,6 +19,7 @@ package com.android.gallery3d.filtershow.filters; import android.app.Activity; import android.graphics.Bitmap; import android.renderscript.Allocation; +import android.renderscript.Allocation.MipmapControl; import android.renderscript.RenderScript; import android.util.Log; @@ -29,25 +30,43 @@ public class ImageFilterRS extends ImageFilter { protected static Allocation mInPixelsAllocation; protected static Allocation mOutPixelsAllocation; private static android.content.res.Resources mResources = null; + private static Bitmap sOldBitmap = null; + private Bitmap mOldBitmap = null; - public void prepare(Bitmap bitmap) { - if (mInPixelsAllocation != null) { - mInPixelsAllocation.destroy(); + private static Bitmap mReturnBitmap = null; + private final Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; + + public void prepare(Bitmap bitmap, float scaleFactor, boolean highQuality) { + if (sOldBitmap == null + || (bitmap.getWidth() != sOldBitmap.getWidth()) + || (bitmap.getHeight() != sOldBitmap.getHeight())) { + if (mInPixelsAllocation != null) { + mInPixelsAllocation.destroy(); + } + if (mOutPixelsAllocation != null) { + mOutPixelsAllocation.destroy(); + } + Bitmap bitmapBuffer = bitmap.copy(mBitmapConfig, true); + mOutPixelsAllocation = Allocation.createFromBitmap(mRS, bitmapBuffer, + MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + mInPixelsAllocation = Allocation.createTyped(mRS, + mOutPixelsAllocation.getType()); + sOldBitmap = bitmap; } - if (mOutPixelsAllocation != null) { - mOutPixelsAllocation.destroy(); + mInPixelsAllocation.copyFrom(bitmap); + if (mOldBitmap != sOldBitmap) { + createFilter(mResources, scaleFactor, highQuality); + mOldBitmap = sOldBitmap; } - mInPixelsAllocation = Allocation.createFromBitmap(mRS, bitmap, - Allocation.MipmapControl.MIPMAP_NONE, - Allocation.USAGE_SCRIPT); - mOutPixelsAllocation = Allocation.createTyped(mRS, mInPixelsAllocation.getType()); } - public void createFilter(android.content.res.Resources res, float scaleFactor, - boolean highQuality) { + public void createFilter(android.content.res.Resources res, + float scaleFactor, boolean highQuality) { + // Stub } public void runFilter() { + // Stub } public void update(Bitmap bitmap) { @@ -60,8 +79,7 @@ public class ImageFilterRS extends ImageFilter { return bitmap; } try { - prepare(bitmap); - createFilter(mResources, scaleFactor, highQuality); + prepare(bitmap, scaleFactor, highQuality); runFilter(); update(bitmap); } catch (android.renderscript.RSIllegalArgumentException e) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java index 588fa9aa9..3d73ef686 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java @@ -415,7 +415,6 @@ public abstract class ImageGeometry extends ImageShow { syncLocalToMasterGeometry(); clearDirtyGeometryFlag(); } - requestFilteredImages(); Bitmap image = getFiltersOnlyImage(); if (image == null) { invalidate(); diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 3d22e05ef..93c4622fa 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -198,12 +198,14 @@ public class ImageShow extends View implements OnGestureListener, int maxp = ImageFilter.DEFAULT_MAX_PARAMETER; int minp = ImageFilter.DEFAULT_MIN_PARAMETER; if (getCurrentFilter() != null) { + if (getCurrentFilter().getParameter() == parameter) { + return; + } getCurrentFilter().setParameter(parameter); maxp = getCurrentFilter().getMaxParameter(); minp = getCurrentFilter().getMinParameter(); } if (getImagePreset() != null) { - mImageLoader.resetImageForPreset(getImagePreset(), this); getImagePreset().fillImageStateAdapter(mMasterImage.getState()); } if (getPanelController() != null) { @@ -255,10 +257,6 @@ public class ImageShow extends View implements OnGestureListener, mSeekBar = seekBar; } - public void setCurrentFilter(ImageFilter filter) { - mMasterImage.setCurrentFilter(filter); - } - public ImageFilter getCurrentFilter() { return mMasterImage.getCurrentFilter(); } @@ -355,14 +353,6 @@ public class ImageShow extends View implements OnGestureListener, mMasterImage.updatePresets(true); } - public void updateImagePresets(boolean force) { - mMasterImage.updatePresets(force); - } - - public void requestFilteredImages() { - mMasterImage.requestImages(); - } - public Bitmap getFiltersOnlyImage() { return mMasterImage.getFiltersOnlyImage(); } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java b/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java index e45b7b4dc..222519c11 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java @@ -98,7 +98,6 @@ public class ImageZoom extends ImageShow { filteredImage = mImageLoader.getScaleOneImageForPreset(this, getImagePreset(), mZoomBounds, false); } else { - requestFilteredImages(); filteredImage = getFilteredImage(); } canvas.save(); diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index 98bef52c7..f4b97b710 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -1,13 +1,29 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.gallery3d.filtershow.imageshow; import android.graphics.Bitmap; import android.graphics.RectF; -import android.util.Log; -import android.widget.ArrayAdapter; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.HistoryAdapter; import com.android.gallery3d.filtershow.ImageStateAdapter; +import com.android.gallery3d.filtershow.cache.TripleBufferBitmap; +import com.android.gallery3d.filtershow.cache.FilteringPipeline; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.presets.ImagePreset; @@ -25,9 +41,9 @@ public class MasterImage { private ImagePreset mGeometryOnlyPreset = null; private ImagePreset mFiltersOnlyPreset = null; - private Bitmap mGeometryOnlyImage = null; - private Bitmap mFiltersOnlyImage = null; - private Bitmap mFilteredImage = null; + private TripleBufferBitmap mFilteredPreview = new TripleBufferBitmap(); + private TripleBufferBitmap mGeometryOnlyPreview = new TripleBufferBitmap(); + private TripleBufferBitmap mFiltersOnlyPreview = new TripleBufferBitmap(); private ImageLoader mLoader = null; private HistoryAdapter mHistory = null; @@ -37,8 +53,7 @@ public class MasterImage { private Vector mObservers = new Vector(); - private MasterImage() { - } + private MasterImage() { } public static MasterImage getImage() { return sMasterImage; @@ -52,11 +67,19 @@ public class MasterImage { mActivity = activity; } - public ImagePreset getPreset() { + public synchronized ImagePreset getPreset() { return mPreset; } - public void setPreset(ImagePreset preset, boolean addToHistory) { + public synchronized ImagePreset getGeometryPreset() { + return mGeometryOnlyPreset; + } + + public synchronized ImagePreset getFiltersOnlyPreset() { + return mFiltersOnlyPreset; + } + + public synchronized void setPreset(ImagePreset preset, boolean addToHistory) { mPreset = preset; mPreset.setImageLoader(mLoader); setGeometry(); @@ -118,29 +141,45 @@ public class MasterImage { return mCurrentFilter; } - public boolean hasModifications() { + public synchronized boolean hasModifications() { if (mPreset == null) { return false; } return mPreset.hasModifications(); } + public TripleBufferBitmap getDoubleBuffer() { + return mFilteredPreview; + } + + public TripleBufferBitmap getGeometryOnlyBuffer() { + return mGeometryOnlyPreview; + } + + public TripleBufferBitmap getFiltersOnlyBuffer() { + return mFiltersOnlyPreview; + } + public Bitmap getFilteredImage() { requestImages(); - return mFilteredImage; + mFilteredPreview.swapConsumer(); + return mFilteredPreview.getConsumer(); } public Bitmap getFiltersOnlyImage() { requestImages(); - return mFiltersOnlyImage; + mFiltersOnlyPreview.swapConsumer(); + return mFiltersOnlyPreview.getConsumer(); } public Bitmap getGeometryOnlyImage() { requestImages(); - return mGeometryOnlyImage; + mGeometryOnlyPreview.swapConsumer(); + return mGeometryOnlyPreview.getConsumer(); } - private void notifyObservers() { + public void notifyObservers() { + requestImages(); for (ImageShow observer : mObservers) { observer.invalidate(); } @@ -152,16 +191,12 @@ public class MasterImage { } public void updatePresets(boolean force) { - if (force) { - mLoader.resetImageForPreset(mPreset, null); - } if (force || mGeometryOnlyPreset == null) { ImagePreset newPreset = new ImagePreset(mPreset); newPreset.setDoApplyFilters(false); if (mGeometryOnlyPreset == null || !newPreset.same(mGeometryOnlyPreset)) { mGeometryOnlyPreset = newPreset; - mGeometryOnlyImage = null; } } if (force || mFiltersOnlyPreset == null) { @@ -170,7 +205,6 @@ public class MasterImage { if (mFiltersOnlyPreset == null || !newPreset.same(mFiltersOnlyPreset)) { mFiltersOnlyPreset = newPreset; - mFiltersOnlyImage = null; } } mActivity.enableSave(hasModifications()); @@ -181,25 +215,9 @@ public class MasterImage { return; } - Bitmap bitmap = mLoader.getImageForPreset(null, mPreset, true); - - if (bitmap != null) { - mFilteredImage = bitmap; - } updatePresets(false); - if (mGeometryOnlyPreset != null) { - bitmap = mLoader.getImageForPreset(null, mGeometryOnlyPreset, - true); - if (bitmap != null) { - mGeometryOnlyImage = bitmap; - } - } - if (mFiltersOnlyPreset != null) { - bitmap = mLoader.getImageForPreset(null, mFiltersOnlyPreset, - true); - if (bitmap != null) { - mFiltersOnlyImage = bitmap; - } - } + FilteringPipeline.getPipeline().updatePreviewBuffer(); + FilteringPipeline.getPipeline().updateGeometryOnlyPreviewBuffer(); + FilteringPipeline.getPipeline().updateFiltersOnlyPreviewBuffer(); } } diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java index 8f3938406..b571ec6ae 100644 --- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java @@ -96,6 +96,14 @@ public class ImagePreset { mDoApplyFilters = value; } + public boolean getDoApplyFilters() { + return mDoApplyFilters; + } + + public GeometryMetadata getGeometry() { + return mGeoData; + } + public boolean hasModifications() { if (mImageBorder != null && !mImageBorder.isNil()) { return true; @@ -165,10 +173,31 @@ public class ImagePreset { this.mImageLoader = mImageLoader; } + public boolean equals(ImagePreset preset) { + if (!same(preset)) { + return false; + } + if (mDoApplyFilters && preset.mDoApplyFilters) { + for (int i = 0; i < preset.mFilters.size(); i++) { + ImageFilter a = preset.mFilters.elementAt(i); + ImageFilter b = mFilters.elementAt(i); + if (!a.equals(b)) { + return false; + } + } + } + return true; + } + public boolean same(ImagePreset preset) { + if (preset == null) { + return false; + } + if (preset.mFilters.size() != mFilters.size()) { return false; } + if (!mName.equalsIgnoreCase(preset.name())) { return false; } @@ -207,6 +236,32 @@ public class ImagePreset { return true; } + public int nbFilters() { + return mFilters.size(); + } + + public int similarUpTo(ImagePreset preset) { + if (!mGeoData.equals(preset.mGeoData)) { + return -1; + } + + for (int i = 0; i < preset.mFilters.size(); i++) { + ImageFilter a = preset.mFilters.elementAt(i); + if (i < mFilters.size()) { + ImageFilter b = mFilters.elementAt(i); + if (!a.same(b)) { + return i; + } + if (a.getParameter() != b.getParameter()) { + return i; + } + } else { + return i; + } + } + return preset.mFilters.size(); + } + public String name() { return mName; } @@ -278,24 +333,40 @@ public class ImagePreset { } public Bitmap apply(Bitmap original) { - // First we apply any transform -- 90 rotate, flip, straighten, crop Bitmap bitmap = original; +// bitmap = applyGeometry(bitmap); + bitmap = applyFilters(bitmap, -1, -1); + return applyBorder(bitmap); + } + + public Bitmap applyGeometry(Bitmap bitmap) { + // Apply any transform -- 90 rotate, flip, straighten, crop + // Returns a new bitmap. + return mGeoData.apply(bitmap, mScaleFactor, mIsHighQuality); + } - if (mDoApplyGeometry) { - bitmap = mGeoData.apply(original, mScaleFactor, mIsHighQuality); + public Bitmap applyBorder(Bitmap bitmap) { + if (mImageBorder != null && mDoApplyGeometry) { + bitmap = mImageBorder.apply(bitmap, mScaleFactor, mIsHighQuality); } + return bitmap; + } + + public Bitmap applyFilters(Bitmap bitmap, int from, int to) { if (mDoApplyFilters) { - for (int i = 0; i < mFilters.size(); i++) { + if (from < 0) { + from = 0; + } + if (to == -1) { + to = mFilters.size(); + } + for (int i = from; i < to; i++) { ImageFilter filter = mFilters.elementAt(i); bitmap = filter.apply(bitmap, mScaleFactor, mIsHighQuality); } } - if (mImageBorder != null && mDoApplyGeometry) { - bitmap = mImageBorder.apply(bitmap, mScaleFactor, mIsHighQuality); - } - return bitmap; } -- 2.11.0