OSDN Git Service

Improves bitmap reuse / memory usage
authornicolasroard <nicolasroard@google.com>
Thu, 15 Aug 2013 16:58:50 +0000 (09:58 -0700)
committernicolasroard <nicolasroard@google.com>
Thu, 15 Aug 2013 16:58:50 +0000 (09:58 -0700)
bug:8782701

Change-Id: Ic655d6f1704dd0429e96819566f50927de02b994

src/com/android/gallery3d/filtershow/cache/BitmapCache.java [new file with mode: 0644]
src/com/android/gallery3d/filtershow/cache/ImageLoader.java
src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
src/com/android/gallery3d/filtershow/pipeline/Buffer.java
src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java
src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java
src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java

diff --git a/src/com/android/gallery3d/filtershow/cache/BitmapCache.java b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java
new file mode 100644 (file)
index 0000000..bd1130d
--- /dev/null
@@ -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;
+    }
+}
index 3877c49..15ffb18 100644 (file)
@@ -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;
     }
 
index b0352de..2bb269e 100644 (file)
@@ -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;
index 2d84f40..7e17bd4 100644 (file)
@@ -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;
+    }
 }
index 7444512..7b51a75 100644 (file)
@@ -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;
+    }
 }
 
index edd07d2..8f6eda2 100644 (file)
@@ -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;
             }
         }
index 932e2fc..0794277 100644 (file)
@@ -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) {
index b540d96..2757aff 100644 (file)
@@ -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) {
index 98e69f6..871e4cd 100644 (file)
@@ -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;