diff options
9 files changed, 181 insertions, 52 deletions
diff --git a/src/com/android/gallery3d/filtershow/cache/BitmapCache.java b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java new file mode 100644 index 000000000..bd1130d26 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java @@ -0,0 +1,96 @@ +/* + * 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.graphics.Canvas; +import android.util.Log; +import com.android.gallery3d.filtershow.pipeline.Buffer; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; + +public class BitmapCache { + private static final String LOGTAG = "BitmapCache"; + private HashMap<Long, ArrayList<WeakReference<Bitmap>>> + mBitmapCache = new HashMap<Long, ArrayList<WeakReference<Bitmap>>>(); + private final int mMaxItemsPerKey = 4; + + public void cache(Buffer buffer) { + if (buffer == null) { + return; + } + Bitmap bitmap = buffer.getBitmap(); + cache(bitmap); + } + + public synchronized void cache(Bitmap bitmap) { + if (bitmap == null) { + return; + } + Long key = calcKey(bitmap.getWidth(), bitmap.getHeight()); + ArrayList<WeakReference<Bitmap>> list = mBitmapCache.get(key); + if (list == null) { + list = new ArrayList<WeakReference<Bitmap>>(); + mBitmapCache.put(key, list); + } + if (list.size() < mMaxItemsPerKey) { + for (int i = 0; i < list.size(); i++) { + WeakReference<Bitmap> ref = list.get(i); + if (ref.get() == bitmap) { + return; // bitmap already in the cache + } + } + list.add(new WeakReference<Bitmap>(bitmap)); + } + } + + public synchronized Bitmap getBitmap(int w, int h) { + Long key = calcKey(w, h); + WeakReference<Bitmap> ref = null; //mBitmapCache.remove(key); + ArrayList<WeakReference<Bitmap>> list = mBitmapCache.get(key); + if (list != null && list.size() > 0) { + ref = list.remove(0); + if (list.size() == 0) { + mBitmapCache.remove(key); + } + } + Bitmap bitmap = null; + if (ref != null) { + bitmap = ref.get(); + } + if (bitmap == null + || bitmap.getWidth() != w + || bitmap.getHeight() != h) { + bitmap = Bitmap.createBitmap( + w, h, Bitmap.Config.ARGB_8888); + } + return bitmap; + } + + public Bitmap getBitmapCopy(Bitmap source) { + Bitmap bitmap = getBitmap(source.getWidth(), source.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(source, 0, 0, null); + return bitmap; + } + + private Long calcKey(long w, long h) { + return (w << 32) | h; + } +} diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index 3877c4980..15ffb1814 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -24,7 +24,9 @@ import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; +import android.graphics.Canvas; import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.Rect; import android.net.Uri; import android.provider.MediaStore; @@ -37,6 +39,7 @@ import com.android.gallery3d.common.Utils; import com.android.gallery3d.exif.ExifInterface; import com.android.gallery3d.exif.ExifTag; import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.pipeline.FilterEnvironment; import com.android.gallery3d.filtershow.tools.XmpPresets; import com.android.gallery3d.util.XmpUtilHelper; @@ -230,22 +233,50 @@ public final class ImageLoader { * if it is a subset of the bitmap stored at uri. Otherwise returns * null. */ - public static Bitmap loadRegionBitmap(Context context, Uri uri, BitmapFactory.Options options, - Rect bounds) { + public static Bitmap loadRegionBitmap(Context context, FilterEnvironment environment, + Uri uri, BitmapFactory.Options options, + Rect bounds) { InputStream is = null; + int w = 0; + int h = 0; + if (options.inSampleSize != 0) { + return null; + } try { is = context.getContentResolver().openInputStream(uri); BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false); Rect r = new Rect(0, 0, decoder.getWidth(), decoder.getHeight()); + w = decoder.getWidth(); + h = decoder.getHeight(); + Rect imageBounds = new Rect(bounds); // return null if bounds are not entirely within the bitmap - if (!r.contains(bounds)) { - return null; + if (!r.contains(imageBounds)) { + imageBounds.intersect(r); } - return decoder.decodeRegion(bounds, options); + Bitmap reuse = environment.getBitmap(imageBounds.width(), imageBounds.height()); + options.inBitmap = reuse; + Bitmap bitmap = decoder.decodeRegion(imageBounds, options); + if (bitmap != reuse) { + environment.cache(reuse); // not reused, put back in cache + } + if (imageBounds.width() != bounds.width() || imageBounds.height() != bounds.height()) { + Bitmap temp = environment.getBitmap(bounds.width(), bounds.height()); + Canvas canvas = new Canvas(temp); + canvas.drawARGB(0, 0, 0, 0); + float dx = imageBounds.left - bounds.left; + float dy = imageBounds.top - bounds.top; + canvas.drawBitmap(bitmap, dx, dy, null); + return temp; + } + return bitmap; } catch (FileNotFoundException e) { Log.e(LOGTAG, "FileNotFoundException for " + uri, e); } catch (IOException e) { Log.e(LOGTAG, "FileNotFoundException for " + uri, e); + } catch (IllegalArgumentException e) { + Log.e(LOGTAG, "exc, image decoded " + w + " x " + h + " bounds: " + + bounds.left + "," + bounds.top + " - " + + bounds.width() + "x" + bounds.height() + " exc: " + e); } finally { Utils.closeSilently(is); } @@ -365,8 +396,10 @@ public final class ImageLoader { return bmap; } - public static Bitmap getScaleOneImageForPreset(Context context, Uri uri, Rect bounds, - Rect destination) { + public static Bitmap getScaleOneImageForPreset(Context context, + FilterEnvironment environment, + Uri uri, Rect bounds, + Rect destination) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inMutable = true; if (destination != null) { @@ -380,7 +413,7 @@ public final class ImageLoader { options.inSampleSize = sampleSize; } } - Bitmap bmp = loadRegionBitmap(context, uri, options, bounds); + Bitmap bmp = loadRegionBitmap(context, environment, uri, options, bounds); return bmp; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index b0352de3d..2bb269e29 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -197,7 +197,9 @@ public class ImageShow extends View implements OnGestureListener, @Override public void onDraw(Canvas canvas) { - MasterImage.getImage().setImageShowSize(getWidth(), getHeight()); + MasterImage.getImage().setImageShowSize( + getWidth() - 2*mShadowMargin, + getHeight() - 2*mShadowMargin); float cx = canvas.getWidth()/2.0f; float cy = canvas.getHeight()/2.0f; diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index 2d84f40f8..7e17bd470 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -27,6 +27,7 @@ import android.os.Message; import com.android.gallery3d.exif.ExifTag; import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; @@ -94,6 +95,7 @@ public class MasterImage implements RenderingRequestCaller { private boolean mShowsOriginal; private List<ExifTag> mEXIF; + private BitmapCache mBitmapCache = new BitmapCache(); private MasterImage() { } @@ -371,6 +373,7 @@ public class MasterImage implements RenderingRequestCaller { public void invalidatePartialPreview() { if (mPartialBitmap != null) { + mBitmapCache.cache(mPartialBitmap); mPartialBitmap = null; notifyObservers(); } @@ -378,6 +381,7 @@ public class MasterImage implements RenderingRequestCaller { public void invalidateHighresPreview() { if (mHighresBitmap != null) { + mBitmapCache.cache(mHighresBitmap); mHighresBitmap = null; notifyObservers(); } @@ -476,11 +480,13 @@ public class MasterImage implements RenderingRequestCaller { } if (request.getType() == RenderingRequest.PARTIAL_RENDERING && request.getScaleFactor() == getScaleFactor()) { + mBitmapCache.cache(mPartialBitmap); mPartialBitmap = request.getBitmap(); notifyObservers(); needsCheckModification = true; } if (request.getType() == RenderingRequest.HIGHRES_RENDERING) { + mBitmapCache.cache(mHighresBitmap); mHighresBitmap = request.getBitmap(); notifyObservers(); needsCheckModification = true; @@ -586,4 +592,7 @@ public class MasterImage implements RenderingRequestCaller { return mEXIF; } + public BitmapCache getBitmapCache() { + return mBitmapCache; + } } diff --git a/src/com/android/gallery3d/filtershow/pipeline/Buffer.java b/src/com/android/gallery3d/filtershow/pipeline/Buffer.java index 744451229..7b51a75d7 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/Buffer.java +++ b/src/com/android/gallery3d/filtershow/pipeline/Buffer.java @@ -19,19 +19,23 @@ package com.android.gallery3d.filtershow.pipeline; import android.graphics.Bitmap; import android.support.v8.renderscript.Allocation; import android.support.v8.renderscript.RenderScript; +import android.util.Log; +import com.android.gallery3d.filtershow.cache.BitmapCache; +import com.android.gallery3d.filtershow.imageshow.MasterImage; public class Buffer { private static final String LOGTAG = "Buffer"; private Bitmap mBitmap; private Allocation mAllocation; private boolean mUseAllocation = false; - private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; private ImagePreset mPreset; public Buffer(Bitmap bitmap) { RenderScript rs = CachingPipeline.getRenderScriptContext(); if (bitmap != null) { - mBitmap = bitmap.copy(BITMAP_CONFIG, true); + BitmapCache cache = MasterImage.getImage().getBitmapCache(); + cache.cache(mBitmap); + mBitmap = cache.getBitmapCopy(bitmap); } if (mUseAllocation) { // TODO: recreate the allocation when the RS context changes @@ -41,10 +45,6 @@ public class Buffer { } } - public void setBitmap(Bitmap bitmap) { - mBitmap = bitmap.copy(BITMAP_CONFIG, true); - } - public Bitmap getBitmap() { return mBitmap; } @@ -70,5 +70,11 @@ public class Buffer { mPreset.updateWith(preset); } } + + public void remove() { + BitmapCache cache = MasterImage.getImage().getBitmapCache(); + cache.cache(mBitmap); + mBitmap = null; + } } diff --git a/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java b/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java index edd07d226..8f6eda261 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java +++ b/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java @@ -145,6 +145,7 @@ public class CacheProcessing { if (similar) { similarUpToIndex = i; } else { + environment.cache(cacheStep.cache); mSteps.remove(i); mSteps.insertElementAt(newStep, i); } @@ -188,9 +189,9 @@ public class CacheProcessing { Log.v(LOGTAG, "i: " + i + " get new copy for cacheBitmap " + cacheBitmap + " apply..."); } + environment.cache(step.cache); cacheBitmap = environment.getBitmapCopy(cacheBitmap); cacheBitmap = step.apply(environment, cacheBitmap); - environment.cache(step.cache); step.cache = cacheBitmap; } } diff --git a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java index 932e2fc00..0794277b0 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java +++ b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java @@ -157,6 +157,7 @@ public class CachingPipeline implements PipelineInterface { private void setupEnvironment(ImagePreset preset, boolean highResPreview) { mEnvironment.setPipeline(this); mEnvironment.setFiltersManager(mFiltersManager); + mEnvironment.setBitmapCache(MasterImage.getImage().getBitmapCache()); if (highResPreview) { mEnvironment.setScaleFactor(mHighResPreviewScaleFactor); } else { @@ -213,8 +214,7 @@ public class CachingPipeline implements PipelineInterface { if (bitmap == null) { return; } - // TODO: use a cache of bitmaps - bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); + bitmap = mEnvironment.getBitmapCopy(bitmap); bitmap = preset.applyGeometry(bitmap, mEnvironment); mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW); @@ -251,6 +251,7 @@ public class CachingPipeline implements PipelineInterface { if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { MasterImage master = MasterImage.getImage(); bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(), + mEnvironment, master.getUri(), request.getBounds(), request.getDestination()); if (bitmap == null) { diff --git a/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java b/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java index b540d96dd..2757aff11 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java +++ b/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.support.v8.renderscript.Allocation; +import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; import com.android.gallery3d.filtershow.filters.FiltersManagerInterface; @@ -36,6 +37,7 @@ public class FilterEnvironment { private FiltersManagerInterface mFiltersManager; private PipelineInterface mPipeline; private volatile boolean mStop = false; + private BitmapCache mBitmapCache; public static final int QUALITY_ICON = 0; public static final int QUALITY_PREVIEW = 1; @@ -49,53 +51,27 @@ public class FilterEnvironment { this.mStop = stop; } - private HashMap<Long, WeakReference<Bitmap>> - bitmapCach = new HashMap<Long, WeakReference<Bitmap>>(); - private HashMap<Integer, Integer> generalParameters = new HashMap<Integer, Integer>(); + public void setBitmapCache(BitmapCache cache) { + mBitmapCache = cache; + } + public void cache(Buffer buffer) { - if (buffer == null) { - return; - } - Bitmap bitmap = buffer.getBitmap(); - cache(bitmap); + mBitmapCache.cache(buffer); } public void cache(Bitmap bitmap) { - if (bitmap == null) { - return; - } - Long key = calcKey(bitmap.getWidth(), bitmap.getHeight()); - bitmapCach.put(key, new WeakReference<Bitmap>(bitmap)); + mBitmapCache.cache(bitmap); } public Bitmap getBitmap(int w, int h) { - Long key = calcKey(w, h); - WeakReference<Bitmap> ref = bitmapCach.remove(key); - Bitmap bitmap = null; - if (ref != null) { - bitmap = ref.get(); - } - if (bitmap == null - || bitmap.getWidth() != w - || bitmap.getHeight() != h) { - bitmap = Bitmap.createBitmap( - w, h, Bitmap.Config.ARGB_8888); - } - return bitmap; + return mBitmapCache.getBitmap(w, h); } public Bitmap getBitmapCopy(Bitmap source) { - Bitmap bitmap = getBitmap(source.getWidth(), source.getHeight()); - Canvas canvas = new Canvas(bitmap); - canvas.drawBitmap(source, 0, 0, null); - return bitmap; - } - - private Long calcKey(long w, long h) { - return (w << 32) | h; + return mBitmapCache.getBitmapCopy(source); } public void setImagePreset(ImagePreset imagePreset) { diff --git a/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java b/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java index 98e69f60e..871e4cd6c 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java +++ b/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java @@ -30,6 +30,11 @@ public class SharedBuffer { private volatile boolean mNeedsRepaint = true; public void setProducer(Bitmap producer) { + synchronized (this) { + if (mProducer != null) { + mProducer.remove(); + } + } Buffer buffer = new Buffer(producer); synchronized (this) { mProducer = buffer; |