OSDN Git Service

Clear Gallery's bitmap pool on photo editor start. Minor refactoring.
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / cache / ImageLoader.java
index 00fcf4e..08ed403 100644 (file)
@@ -18,16 +18,15 @@ package com.android.gallery3d.filtershow.cache;
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Matrix;
 import android.graphics.Rect;
-import android.graphics.Bitmap.CompressFormat;
 import android.media.ExifInterface;
 import android.net.Uri;
 import android.provider.MediaStore;
@@ -35,16 +34,13 @@ import android.util.Log;
 
 import com.adobe.xmp.XMPException;
 import com.adobe.xmp.XMPMeta;
-
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.exif.ExifInvalidFormatException;
 import com.android.gallery3d.exif.ExifParser;
 import com.android.gallery3d.exif.ExifTag;
-import com.android.gallery3d.filtershow.CropExtras;
 import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.HistoryAdapter;
-import com.android.gallery3d.filtershow.imageshow.ImageCrop;
 import com.android.gallery3d.filtershow.imageshow.ImageShow;
 import com.android.gallery3d.filtershow.presets.ImagePreset;
 import com.android.gallery3d.filtershow.tools.BitmapTask;
@@ -52,7 +48,6 @@ import com.android.gallery3d.filtershow.tools.SaveCopyTask;
 import com.android.gallery3d.util.InterruptableOutputStream;
 import com.android.gallery3d.util.XmpUtilHelper;
 
-import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -62,6 +57,8 @@ import java.io.OutputStream;
 import java.util.Vector;
 import java.util.concurrent.locks.ReentrantLock;
 
+
+// TODO: this class has waaaay to much bitmap copying.  Cleanup.
 public class ImageLoader {
 
     private static final String LOGTAG = "ImageLoader";
@@ -91,6 +88,7 @@ public class ImageLoader {
     public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
     public static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
 
+    private static final int BITMAP_LOAD_BACKOUT_ATTEMPTS = 5;
     private Context mContext = null;
     private Uri mUri = null;
 
@@ -264,12 +262,12 @@ public class ImageLoader {
                 bitmap.getHeight(), matrix, true);
     }
 
-    private Bitmap loadRegionBitmap(Uri uri, Rect bounds) {
+    private Bitmap loadRegionBitmap(Uri uri, BitmapFactory.Options options, Rect bounds) {
         InputStream is = null;
         try {
             is = mContext.getContentResolver().openInputStream(uri);
             BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
-            return decoder.decodeRegion(bounds, null);
+            return decoder.decodeRegion(bounds, options);
         } catch (FileNotFoundException e) {
             Log.e(LOGTAG, "FileNotFoundException: " + uri);
         } catch (Exception e) {
@@ -312,6 +310,7 @@ public class ImageLoader {
             // decode with inSampleSize
             BitmapFactory.Options o2 = new BitmapFactory.Options();
             o2.inSampleSize = scale;
+            o2.inMutable = true;
 
             Utils.closeSilently(is);
             is = mContext.getContentResolver().openInputStream(uri);
@@ -369,21 +368,38 @@ public class ImageLoader {
     // 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) {
+                                            Rect destination, boolean force) {
         mLoadingLock.lock();
         Bitmap bmp = mZoomCache.getImage(imagePreset, bounds);
         if (force || bmp == null) {
-            bmp = loadRegionBitmap(mUri, bounds);
+            BitmapFactory.Options options = new BitmapFactory.Options();
+            options.inMutable = true;
+            if (destination != null) {
+                if (bounds.width() > destination.width()) {
+                    int sampleSize = 1;
+                    int w = bounds.width();
+                    while (w > destination.width()) {
+                        sampleSize *= 2;
+                        w /= sampleSize;
+                    }
+                    options.inSampleSize = sampleSize;
+                }
+            }
+            bmp = loadRegionBitmap(mUri, options, bounds);
+            if (destination != null) {
+                mLoadingLock.unlock();
+                return bmp;
+            }
             if (bmp != null) {
-                // TODO: this workaround for RS might not be needed ultimately
-                Bitmap bmp2 = bmp.copy(Bitmap.Config.ARGB_8888, true);
                 float scaleFactor = imagePreset.getScaleFactor();
-                imagePreset.setScaleFactor(1.0f);
-                bmp2 = imagePreset.apply(bmp2);
+                float scale = (float) bmp.getWidth() / (float) getOriginalBounds().width();
+                imagePreset.setScaleFactor(scale);
+                imagePreset.setupEnvironment();
+                bmp = imagePreset.apply(bmp);
                 imagePreset.setScaleFactor(scaleFactor);
-                mZoomCache.setImage(imagePreset, bounds, bmp2);
+                mZoomCache.setImage(imagePreset, bounds, bmp);
                 mLoadingLock.unlock();
-                return bmp2;
+                return bmp;
             }
         }
         mLoadingLock.unlock();
@@ -406,28 +422,97 @@ public class ImageLoader {
 
     public static Bitmap loadMutableBitmap(Context context, Uri sourceUri) {
         BitmapFactory.Options options = new BitmapFactory.Options();
+        return loadMutableBitmap(context, sourceUri, options);
+    }
+
+    public static Bitmap loadMutableBitmap(Context context, Uri sourceUri,
+            BitmapFactory.Options options) {
         // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
         // exist)
         options.inMutable = true;
 
+        Bitmap bitmap = decodeUriWithBackouts(context, sourceUri, options);
+        if (bitmap == null) {
+            return null;
+        }
+        int orientation = ImageLoader.getOrientation(context, sourceUri);
+        bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
+        return bitmap;
+    }
+
+    public static Bitmap decodeUriWithBackouts(Context context, Uri sourceUri,
+            BitmapFactory.Options options) {
+        boolean noBitmap = true;
+        int num_tries = 0;
+        InputStream is = getInputStream(context, sourceUri);
+
+        if (options.inSampleSize < 1) {
+            options.inSampleSize = 1;
+        }
+        // Stopgap fix for low-memory devices.
+        Bitmap bmap = null;
+        while (noBitmap) {
+            if (is == null) {
+                return null;
+            }
+            try {
+                // Try to decode, downsample if low-memory.
+                bmap = BitmapFactory.decodeStream(is, null, options);
+                noBitmap = false;
+            } catch (java.lang.OutOfMemoryError e) {
+                // Try 5 times before failing for good.
+                if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) {
+                    throw e;
+                }
+                is = null;
+                bmap = null;
+                System.gc();
+                is = getInputStream(context, sourceUri);
+                options.inSampleSize *= 2;
+            }
+        }
+        Utils.closeSilently(is);
+        return bmap;
+    }
+
+    private static InputStream getInputStream(Context context, Uri sourceUri) {
         InputStream is = null;
-        Bitmap bitmap = null;
         try {
             is = context.getContentResolver().openInputStream(sourceUri);
-            bitmap = BitmapFactory.decodeStream(is, null, options);
         } catch (FileNotFoundException e) {
             Log.w(LOGTAG, "could not load bitmap ", e);
-            is = null;
-            bitmap = null;
-        } finally {
             Utils.closeSilently(is);
+            is = null;
         }
-        if (bitmap == null) {
-            return null;
+        return is;
+    }
+
+    public static Bitmap decodeResourceWithBackouts(Resources res, BitmapFactory.Options options,
+            int id) {
+        boolean noBitmap = true;
+        int num_tries = 0;
+        if (options.inSampleSize < 1) {
+            options.inSampleSize = 1;
         }
-        int orientation = ImageLoader.getOrientation(context, sourceUri);
-        bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
-        return bitmap;
+        // Stopgap fix for low-memory devices.
+        Bitmap bmap = null;
+        while (noBitmap) {
+            try {
+                // Try to decode, downsample if low-memory.
+                bmap = BitmapFactory.decodeResource(
+                        res, id, options);
+                noBitmap = false;
+            } catch (java.lang.OutOfMemoryError e) {
+                // Try 5 times before failing for good.
+                if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) {
+                    throw e;
+                }
+                bmap = null;
+                System.gc();
+                options.inSampleSize *= 2;
+            }
+        }
+        return bmap;
     }
 
     public void returnFilteredResult(ImagePreset preset,
@@ -435,7 +520,7 @@ public class ImageLoader {
         preset.setQuality(ImagePreset.QUALITY_FINAL);
         preset.setScaleFactor(1.0f);
 
-        BitmapTask.Callbacks<ImagePreset> cb = new BitmapTask.Callbacks<ImagePreset>() {
+        BitmapTask.Callbacks<ImagePreset, Bitmap> cb = new BitmapTask.Callbacks<ImagePreset, Bitmap>() {
 
             @Override
             public void onComplete(Bitmap result) {
@@ -451,16 +536,41 @@ public class ImageLoader {
                 if (param == null || mUri == null) {
                     return null;
                 }
-                Bitmap bitmap = loadMutableBitmap(mContext, mUri);
-                if (bitmap == null) {
-                    Log.w(LOGTAG, "Failed to save image!");
-                    return null;
+                BitmapFactory.Options options = new BitmapFactory.Options();
+                boolean noBitmap = true;
+                int num_tries = 0;
+                if (options.inSampleSize < 1) {
+                    options.inSampleSize = 1;
+                }
+                Bitmap bitmap = null;
+                // Stopgap fix for low-memory devices.
+                while (noBitmap) {
+                    try {
+                        // Try to do bitmap operations, downsample if low-memory
+                        bitmap = loadMutableBitmap(mContext, mUri, options);
+                        if (bitmap == null) {
+                            Log.w(LOGTAG, "Failed to save image!");
+                            return null;
+                        }
+                        param.setupEnvironment();
+                        bitmap = param.applyGeometry(bitmap);
+                        bitmap = param.apply(bitmap);
+                        noBitmap = false;
+                    } catch (java.lang.OutOfMemoryError e) {
+                        // Try 5 times before failing for good.
+                        if (++num_tries >= 5) {
+                            throw e;
+                        }
+                        bitmap = null;
+                        System.gc();
+                        options.inSampleSize *= 2;
+                    }
                 }
-                return param.apply(bitmap);
+                return bitmap;
             }
         };
 
-        (new BitmapTask<ImagePreset>(cb)).execute(preset);
+        (new BitmapTask<ImagePreset, Bitmap>(cb)).execute(preset);
     }
 
     private String getFileExtension(String requestFormat) {
@@ -494,7 +604,7 @@ public class ImageLoader {
 
         final InterruptableOutputStream ios = new InterruptableOutputStream(out);
 
-        BitmapTask.Callbacks<Bitmap> cb = new BitmapTask.Callbacks<Bitmap>() {
+        BitmapTask.Callbacks<Bitmap, Bitmap> cb = new BitmapTask.Callbacks<Bitmap, Bitmap>() {
 
             @Override
             public void onComplete(Bitmap result) {
@@ -508,6 +618,9 @@ public class ImageLoader {
 
             @Override
             public Bitmap onExecute(Bitmap param) {
+                if (param == null) {
+                    return null;
+                }
                 CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(outputFormat));
                 param.compress(cf, DEFAULT_COMPRESS_QUALITY, ios);
                 Utils.closeSilently(ios);
@@ -515,7 +628,7 @@ public class ImageLoader {
             }
         };
 
-        (new BitmapTask<Bitmap>(cb)).execute(bmap);
+        (new BitmapTask<Bitmap, Bitmap>(cb)).execute(bmap);
     }
 
     public void setAdapter(HistoryAdapter adapter) {