diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/gallery3d/app/AbstractGalleryActivity.java | 17 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/DecodeUtils.java | 83 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/ImageCacheRequest.java | 25 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/ImageCacheService.java | 6 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/LocalImage.java | 40 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/LocalVideo.java | 2 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/MediaItem.java | 16 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/MtpImage.java | 4 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/UriImage.java | 31 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/AbstractDisplayItem.java | 11 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java | 5 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/AlbumSlidingWindow.java | 9 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/BitmapPool.java | 113 |
13 files changed, 261 insertions, 101 deletions
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java index f3e60cee9..7d9d72b13 100644 --- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java +++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java @@ -16,15 +16,6 @@ package com.android.gallery3d.app; -import com.android.gallery3d.R; -import com.android.gallery3d.data.DataManager; -import com.android.gallery3d.data.ImageCacheService; -import com.android.gallery3d.ui.GLRoot; -import com.android.gallery3d.ui.GLRootView; -import com.android.gallery3d.ui.PositionRepository; -import com.android.gallery3d.util.ThreadPool; - -import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; @@ -39,6 +30,13 @@ import android.os.Bundle; import android.view.Window; import android.view.WindowManager; +import com.android.gallery3d.R; +import com.android.gallery3d.data.DataManager; +import com.android.gallery3d.ui.BitmapPool; +import com.android.gallery3d.ui.GLRoot; +import com.android.gallery3d.ui.GLRootView; +import com.android.gallery3d.util.ThreadPool; + public class AbstractGalleryActivity extends Activity implements GalleryActivity { @SuppressWarnings("unused") private static final String TAG = "AbstractGalleryActivity"; @@ -177,6 +175,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryActivity } finally { mGLRootView.unlockRenderThread(); } + BitmapPool.clear(); } @Override diff --git a/src/com/android/gallery3d/data/DecodeUtils.java b/src/com/android/gallery3d/data/DecodeUtils.java index 969fd9854..319458a14 100644 --- a/src/com/android/gallery3d/data/DecodeUtils.java +++ b/src/com/android/gallery3d/data/DecodeUtils.java @@ -16,20 +16,17 @@ package com.android.gallery3d.data; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.BitmapRegionDecoder; -import android.graphics.Rect; -import android.net.Uri; -import android.os.ParcelFileDescriptor; +import android.util.FloatMath; + +import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.util.ThreadPool.CancelListener; +import com.android.gallery3d.util.ThreadPool.JobContext; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -40,27 +37,29 @@ public class DecodeUtils { private static class DecodeCanceller implements CancelListener { Options mOptions; + public DecodeCanceller(Options options) { mOptions = options; } + + @Override public void onCancel() { mOptions.requestCancelDecode(); } } - public static Bitmap requestDecode(JobContext jc, FileDescriptor fd, Options options) { + public static Bitmap decode(JobContext jc, FileDescriptor fd, Options options) { if (options == null) options = new Options(); jc.setCancelListener(new DecodeCanceller(options)); return ensureGLCompatibleBitmap( BitmapFactory.decodeFileDescriptor(fd, null, options)); } - public static Bitmap requestDecode(JobContext jc, byte[] bytes, - Options options) { - return requestDecode(jc, bytes, 0, bytes.length, options); + public static Bitmap decode(JobContext jc, byte[] bytes, Options options) { + return decode(jc, bytes, 0, bytes.length, options); } - public static Bitmap requestDecode(JobContext jc, byte[] bytes, int offset, + public static Bitmap decode(JobContext jc, byte[] bytes, int offset, int length, Options options) { if (options == null) options = new Options(); jc.setCancelListener(new DecodeCanceller(options)); @@ -68,13 +67,13 @@ public class DecodeUtils { BitmapFactory.decodeByteArray(bytes, offset, length, options)); } - public static Bitmap requestDecode(JobContext jc, final String filePath, - Options options, int targetSize) { + public static Bitmap decodeThumbnail( + JobContext jc, String filePath, Options options, int targetSize, int type) { FileInputStream fis = null; try { fis = new FileInputStream(filePath); FileDescriptor fd = fis.getFD(); - return requestDecode(jc, fd, options, targetSize); + return decodeThumbnail(jc, fd, options, targetSize, type); } catch (Exception ex) { Log.w(TAG, ex); return null; @@ -83,8 +82,8 @@ public class DecodeUtils { } } - public static Bitmap requestDecode(JobContext jc, FileDescriptor fd, - Options options, int targetSize) { + public static Bitmap decodeThumbnail( + JobContext jc, FileDescriptor fd, Options options, int targetSize, int type) { if (options == null) options = new Options(); jc.setCancelListener(new DecodeCanceller(options)); @@ -92,14 +91,40 @@ public class DecodeUtils { BitmapFactory.decodeFileDescriptor(fd, null, options); if (jc.isCancelled()) return null; - options.inSampleSize = BitmapUtils.computeSampleSizeLarger( - options.outWidth, options.outHeight, targetSize); + int w = options.outWidth; + int h = options.outHeight; + + if (type == MediaItem.TYPE_MICROTHUMBNAIL) { + // We center-crop the original image as it's micro thumbnail. In this case, + // we want to make sure the shorter side >= "targetSize". + float scale = (float) targetSize / Math.min(w, h); + options.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); + + // For an extremely wide image, e.g. 300x30000, we may got OOM when decoding + // it for TYPE_MICROTHUMBNAIL. So we add a max number of pixels limit here. + final int MAX_PIXEL_COUNT = 640000; // 400 x 1600 + if ((w / options.inSampleSize) * (h / options.inSampleSize) > MAX_PIXEL_COUNT) { + options.inSampleSize = BitmapUtils.computeSampleSize( + FloatMath.sqrt((float) MAX_PIXEL_COUNT / (w * h))); + } + } else { + // For screen nail, we only want to keep the longer side >= targetSize. + float scale = (float) targetSize / Math.max(w, h); + options.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); + } + options.inJustDecodeBounds = false; Bitmap result = BitmapFactory.decodeFileDescriptor(fd, null, options); - // We need to resize down if the decoder does not support inSampleSize. - // (For example, GIF images.) - result = BitmapUtils.resizeDownIfTooBig(result, targetSize, true); + if (result == null) return null; + + // We need to resize down if the decoder does not support inSampleSize + // (For example, GIF images) + float scale = (float) targetSize / (type == MediaItem.TYPE_MICROTHUMBNAIL + ? Math.min(result.getWidth(), result.getHeight()) + : Math.max(result.getWidth(), result.getHeight())); + + if (scale <= 0.5) result = BitmapUtils.resizeBitmapByScale(result, scale, true); return ensureGLCompatibleBitmap(result); } @@ -110,7 +135,7 @@ public class DecodeUtils { * Note: The returned image may be resized down. However, both width and height must be * larger than the <code>targetSize</code>. */ - public static Bitmap requestDecodeIfBigEnough(JobContext jc, byte[] data, + public static Bitmap decodeIfBigEnough(JobContext jc, byte[] data, Options options, int targetSize) { if (options == null) options = new Options(); jc.setCancelListener(new DecodeCanceller(options)); @@ -138,7 +163,7 @@ public class DecodeUtils { return newBitmap; } - public static BitmapRegionDecoder requestCreateBitmapRegionDecoder( + public static BitmapRegionDecoder createBitmapRegionDecoder( JobContext jc, byte[] bytes, int offset, int length, boolean shareable) { if (offset < 0 || length <= 0 || offset + length > bytes.length) { @@ -156,7 +181,7 @@ public class DecodeUtils { } } - public static BitmapRegionDecoder requestCreateBitmapRegionDecoder( + public static BitmapRegionDecoder createBitmapRegionDecoder( JobContext jc, String filePath, boolean shareable) { try { return BitmapRegionDecoder.newInstance(filePath, shareable); @@ -166,7 +191,7 @@ public class DecodeUtils { } } - public static BitmapRegionDecoder requestCreateBitmapRegionDecoder( + public static BitmapRegionDecoder createBitmapRegionDecoder( JobContext jc, FileDescriptor fd, boolean shareable) { try { return BitmapRegionDecoder.newInstance(fd, shareable); @@ -176,7 +201,7 @@ public class DecodeUtils { } } - public static BitmapRegionDecoder requestCreateBitmapRegionDecoder( + public static BitmapRegionDecoder createBitmapRegionDecoder( JobContext jc, InputStream is, boolean shareable) { try { return BitmapRegionDecoder.newInstance(is, shareable); diff --git a/src/com/android/gallery3d/data/ImageCacheRequest.java b/src/com/android/gallery3d/data/ImageCacheRequest.java index 104ff4839..64dfc9f45 100644 --- a/src/com/android/gallery3d/data/ImageCacheRequest.java +++ b/src/com/android/gallery3d/data/ImageCacheRequest.java @@ -16,15 +16,16 @@ package com.android.gallery3d.data; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.data.ImageCacheService.ImageData; +import com.android.gallery3d.ui.BitmapPool; import com.android.gallery3d.util.ThreadPool.Job; import com.android.gallery3d.util.ThreadPool.JobContext; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; - abstract class ImageCacheRequest implements Job<Bitmap> { private static final String TAG = "ImageCacheRequest"; @@ -53,8 +54,14 @@ abstract class ImageCacheRequest implements Job<Bitmap> { if (data != null) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_8888; - Bitmap bitmap = DecodeUtils.requestDecode(jc, data.mData, - data.mOffset, data.mData.length - data.mOffset, options); + Bitmap bitmap; + if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { + bitmap = BitmapPool.decode(jc, BitmapPool.TYPE_MICRO_THUMB, + data.mData, data.mOffset, data.mData.length - data.mOffset, options); + } else { + bitmap = DecodeUtils.decode(jc, + data.mData, data.mOffset, data.mData.length - data.mOffset, options); + } if (bitmap == null && !jc.isCancelled()) { Log.w(TAG, "decode cached failed " + debugTag); } @@ -69,15 +76,13 @@ abstract class ImageCacheRequest implements Job<Bitmap> { } if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = BitmapUtils.resizeDownAndCropCenter(bitmap, - mTargetSize, true); + bitmap = BitmapUtils.resizeAndCropCenter(bitmap, mTargetSize, true); } else { - bitmap = BitmapUtils.resizeDownBySideLength(bitmap, - mTargetSize, true); + bitmap = BitmapUtils.resizeDownBySideLength(bitmap, mTargetSize, true); } if (jc.isCancelled()) return null; - byte[] array = BitmapUtils.compressBitmap(bitmap); + byte[] array = BitmapUtils.compressToBytes(bitmap); if (jc.isCancelled()) return null; cacheService.putImageData(mPath, mType, array); diff --git a/src/com/android/gallery3d/data/ImageCacheService.java b/src/com/android/gallery3d/data/ImageCacheService.java index 3adce1332..fdc27749b 100644 --- a/src/com/android/gallery3d/data/ImageCacheService.java +++ b/src/com/android/gallery3d/data/ImageCacheService.java @@ -16,13 +16,13 @@ package com.android.gallery3d.data; +import android.content.Context; + import com.android.gallery3d.common.BlobCache; import com.android.gallery3d.common.Utils; import com.android.gallery3d.util.CacheManager; import com.android.gallery3d.util.GalleryUtils; -import android.content.Context; - import java.io.IOException; import java.nio.ByteBuffer; @@ -33,7 +33,7 @@ public class ImageCacheService { private static final String IMAGE_CACHE_FILE = "imgcache"; private static final int IMAGE_CACHE_MAX_ENTRIES = 5000; private static final int IMAGE_CACHE_MAX_BYTES = 200 * 1024 * 1024; - private static final int IMAGE_CACHE_VERSION = 3; + private static final int IMAGE_CACHE_VERSION = 4; private BlobCache mCache; diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java index fa3ece3b6..4f2797e02 100644 --- a/src/com/android/gallery3d/data/LocalImage.java +++ b/src/com/android/gallery3d/data/LocalImage.java @@ -16,13 +16,6 @@ package com.android.gallery3d.data; -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.util.GalleryUtils; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; -import com.android.gallery3d.util.UpdateHelper; - import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; @@ -35,6 +28,13 @@ import android.provider.MediaStore.Images; import android.provider.MediaStore.Images.ImageColumns; import android.util.Log; +import com.android.gallery3d.app.GalleryApp; +import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.util.GalleryUtils; +import com.android.gallery3d.util.ThreadPool.Job; +import com.android.gallery3d.util.ThreadPool.JobContext; +import com.android.gallery3d.util.UpdateHelper; + import java.io.File; import java.io.IOException; @@ -159,14 +159,15 @@ public class LocalImage extends LocalMediaItem { LocalImageRequest(GalleryApp application, Path path, int type, String localFilePath) { - super(application, path, type, getTargetSize(type)); + super(application, path, type, MediaItem.getTargetSize(type)); mLocalFilePath = localFilePath; } @Override - public Bitmap onDecodeOriginal(JobContext jc, int type) { + public Bitmap onDecodeOriginal(JobContext jc, final int type) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_8888; + int targetSize = MediaItem.getTargetSize(type); // try to decode from JPEG EXIF if (type == MediaItem.TYPE_MICROTHUMBNAIL) { @@ -181,25 +182,13 @@ public class LocalImage extends LocalMediaItem { Log.w(TAG, "fail to get exif thumb", t); } if (thumbData != null) { - Bitmap bitmap = DecodeUtils.requestDecodeIfBigEnough( - jc, thumbData, options, getTargetSize(type)); + Bitmap bitmap = DecodeUtils.decodeIfBigEnough( + jc, thumbData, options, targetSize); if (bitmap != null) return bitmap; } } - return DecodeUtils.requestDecode( - jc, mLocalFilePath, options, getTargetSize(type)); - } - } - static int getTargetSize(int type) { - switch (type) { - case TYPE_THUMBNAIL: - return THUMBNAIL_TARGET_SIZE; - case TYPE_MICROTHUMBNAIL: - return MICROTHUMBNAIL_TARGET_SIZE; - default: - throw new RuntimeException( - "should only request thumb/microthumb from cache"); + return DecodeUtils.decodeThumbnail(jc, mLocalFilePath, options, targetSize, type); } } @@ -217,8 +206,7 @@ public class LocalImage extends LocalMediaItem { } public BitmapRegionDecoder run(JobContext jc) { - return DecodeUtils.requestCreateBitmapRegionDecoder( - jc, mLocalFilePath, false); + return DecodeUtils.createBitmapRegionDecoder(jc, mLocalFilePath, false); } } diff --git a/src/com/android/gallery3d/data/LocalVideo.java b/src/com/android/gallery3d/data/LocalVideo.java index d68072bec..c45949ea5 100644 --- a/src/com/android/gallery3d/data/LocalVideo.java +++ b/src/com/android/gallery3d/data/LocalVideo.java @@ -147,7 +147,7 @@ public class LocalVideo extends LocalMediaItem { LocalVideoRequest(GalleryApp application, Path path, int type, String localFilePath) { - super(application, path, type, LocalImage.getTargetSize(type)); + super(application, path, type, MediaItem.getTargetSize(type)); mLocalFilePath = localFilePath; } diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java index 13612321c..b682c2d1b 100644 --- a/src/com/android/gallery3d/data/MediaItem.java +++ b/src/com/android/gallery3d/data/MediaItem.java @@ -16,11 +16,11 @@ package com.android.gallery3d.data; -import com.android.gallery3d.util.ThreadPool.Job; - import android.graphics.Bitmap; import android.graphics.BitmapRegionDecoder; +import com.android.gallery3d.util.ThreadPool.Job; + // MediaItem represents an image or a video item. public abstract class MediaItem extends MediaObject { // NOTE: These type numbers are stored in the image cache, so it should not @@ -89,4 +89,16 @@ public abstract class MediaItem extends MediaObject { // Returns 0, 0 if the information is not available. public abstract int getWidth(); public abstract int getHeight(); + + public static int getTargetSize(int type) { + switch (type) { + case TYPE_THUMBNAIL: + return THUMBNAIL_TARGET_SIZE; + case TYPE_MICROTHUMBNAIL: + return MICROTHUMBNAIL_TARGET_SIZE; + default: + throw new RuntimeException( + "should only request thumb/microthumb from cache"); + } + } } diff --git a/src/com/android/gallery3d/data/MtpImage.java b/src/com/android/gallery3d/data/MtpImage.java index 211b2f2ee..bdbaecd22 100644 --- a/src/com/android/gallery3d/data/MtpImage.java +++ b/src/com/android/gallery3d/data/MtpImage.java @@ -84,7 +84,7 @@ public class MtpImage extends MediaItem { Log.w(TAG, "decoding thumbnail failed"); return null; } - return DecodeUtils.requestDecode(jc, thumbnail, null); + return DecodeUtils.decode(jc, thumbnail, null); } }; } @@ -95,7 +95,7 @@ public class MtpImage extends MediaItem { public BitmapRegionDecoder run(JobContext jc) { byte[] bytes = mMtpContext.getMtpClient().getObject( UsbDevice.getDeviceName(mDeviceId), mObjectId, mObjectSize); - return DecodeUtils.requestCreateBitmapRegionDecoder( + return DecodeUtils.createBitmapRegionDecoder( jc, bytes, 0, bytes.length, false); } }; diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java index 8f91cc036..05850bbb3 100644 --- a/src/com/android/gallery3d/data/UriImage.java +++ b/src/com/android/gallery3d/data/UriImage.java @@ -16,13 +16,6 @@ package com.android.gallery3d.data; -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; @@ -32,6 +25,13 @@ import android.net.Uri; import android.os.ParcelFileDescriptor; import android.webkit.MimeTypeMap; +import com.android.gallery3d.app.GalleryApp; +import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.util.ThreadPool.CancelListener; +import com.android.gallery3d.util.ThreadPool.Job; +import com.android.gallery3d.util.ThreadPool.JobContext; + import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; @@ -180,7 +180,7 @@ public class UriImage extends MediaItem { private class RegionDecoderJob implements Job<BitmapRegionDecoder> { public BitmapRegionDecoder run(JobContext jc) { if (!prepareInputFile(jc)) return null; - BitmapRegionDecoder decoder = DecodeUtils.requestCreateBitmapRegionDecoder( + BitmapRegionDecoder decoder = DecodeUtils.createBitmapRegionDecoder( jc, mFileDescriptor.getFileDescriptor(), false); mWidth = decoder.getWidth(); mHeight = decoder.getHeight(); @@ -195,25 +195,24 @@ public class UriImage extends MediaItem { mType = type; } + @Override public Bitmap run(JobContext jc) { if (!prepareInputFile(jc)) return null; - int targetSize = LocalImage.getTargetSize(mType); + int targetSize = MediaItem.getTargetSize(mType); Options options = new Options(); options.inPreferredConfig = Config.ARGB_8888; - Bitmap bitmap = DecodeUtils.requestDecode(jc, - mFileDescriptor.getFileDescriptor(), options, targetSize); + Bitmap bitmap = DecodeUtils.decodeThumbnail(jc, + mFileDescriptor.getFileDescriptor(), options, targetSize, mType); + if (jc.isCancelled() || bitmap == null) { return null; } if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = BitmapUtils.resizeDownAndCropCenter(bitmap, - targetSize, true); + bitmap = BitmapUtils.resizeAndCropCenter(bitmap, targetSize, true); } else { - bitmap = BitmapUtils.resizeDownBySideLength(bitmap, - targetSize, true); + bitmap = BitmapUtils.resizeDownBySideLength(bitmap, targetSize, true); } - return bitmap; } } diff --git a/src/com/android/gallery3d/ui/AbstractDisplayItem.java b/src/com/android/gallery3d/ui/AbstractDisplayItem.java index 1f8165fb4..2547ebf31 100644 --- a/src/com/android/gallery3d/ui/AbstractDisplayItem.java +++ b/src/com/android/gallery3d/ui/AbstractDisplayItem.java @@ -16,10 +16,10 @@ package com.android.gallery3d.ui; -import com.android.gallery3d.data.MediaItem; - import android.graphics.Bitmap; +import com.android.gallery3d.data.MediaItem; + public abstract class AbstractDisplayItem extends DisplayItem { private static final String TAG = "AbstractDisplayItem"; @@ -44,6 +44,7 @@ public abstract class AbstractDisplayItem extends DisplayItem { protected void updateImage(Bitmap bitmap, boolean isCancelled) { if (mRecycling) { + recycleBitmap(bitmap); return; } @@ -96,7 +97,10 @@ public abstract class AbstractDisplayItem extends DisplayItem { public void recycle() { if (!inState(STATE_UPDATING | STATE_CANCELING)) { - if (mBitmap != null) mBitmap = null; + if (mBitmap != null) { + recycleBitmap(mBitmap); + mBitmap = null; + } } else { mRecycling = true; cancelImageRequest(); @@ -110,4 +114,5 @@ public abstract class AbstractDisplayItem extends DisplayItem { abstract protected void startLoadBitmap(); abstract protected void cancelLoadBitmap(); abstract protected void onBitmapAvailable(Bitmap bitmap); + abstract protected void recycleBitmap(Bitmap bitmap); } diff --git a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java index b02f19e90..5bead6297 100644 --- a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java +++ b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java @@ -352,6 +352,11 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener { } @Override + protected void recycleBitmap(Bitmap bitmap) { + BitmapPool.recycle(BitmapPool.TYPE_MICRO_THUMB, bitmap); + } + + @Override protected void onBitmapAvailable(Bitmap bitmap) { if (isActiveSlot(mSlotIndex)) { --mActiveRequestCount; diff --git a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java index 8d61f934d..142a309b2 100644 --- a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java +++ b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java @@ -293,6 +293,15 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener { } @Override + protected void recycleBitmap(Bitmap bitmap) { + // if mCacheThumbSize > 0, we will keep images in cache so that + // we cannot recycle the bitmap + if (mCacheThumbSize == 0) { + BitmapPool.recycle(BitmapPool.TYPE_MICRO_THUMB, bitmap); + } + } + + @Override protected void onBitmapAvailable(Bitmap bitmap) { boolean isActiveSlot = isActiveSlot(mSlotIndex); if (isActiveSlot) { diff --git a/src/com/android/gallery3d/ui/BitmapPool.java b/src/com/android/gallery3d/ui/BitmapPool.java new file mode 100644 index 000000000..e910aece1 --- /dev/null +++ b/src/com/android/gallery3d/ui/BitmapPool.java @@ -0,0 +1,113 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +package com.android.gallery3d.ui; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; + +import com.android.gallery3d.data.DecodeUtils; +import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.util.ThreadPool.JobContext; + +import java.io.FileDescriptor; +import java.util.ArrayList; + +public class BitmapPool { + private static final String TAG = "BitmapPool"; + + public static final int TYPE_MICRO_THUMB = 0; + private static final int TYPE_COUNT = 1; + private static final int POOL_SIZE = 16; + private static final int EXPECTED_WIDTH[] = {MediaItem.MICROTHUMBNAIL_TARGET_SIZE}; + private static final int EXPECTED_HEIGHT[] = {MediaItem.MICROTHUMBNAIL_TARGET_SIZE}; + + @SuppressWarnings("unchecked") + private static final ArrayList<Bitmap> sPools[] = new ArrayList[TYPE_COUNT]; + static { + for (int i = 0; i < TYPE_COUNT; ++i) { + sPools[i] = new ArrayList<Bitmap>(); + } + } + + private BitmapPool() { + } + + public static Bitmap getBitmap(int type) { + ArrayList<Bitmap> list = sPools[type]; + synchronized (list) { + int size = list.size(); + return size > 0 ? list.remove(size - 1) : null; + } + } + + public static void recycle(int type, Bitmap bitmap) { + if (bitmap == null) return; + if ((bitmap.getWidth() != EXPECTED_WIDTH[type]) + || (bitmap.getHeight() != EXPECTED_HEIGHT[type])) { + bitmap.recycle(); + return; + } + ArrayList<Bitmap> list = sPools[type]; + synchronized (list) { + if (list.size() < POOL_SIZE) list.add(bitmap); + } + } + + public static void clear() { + for (int i = 0; i < TYPE_COUNT; ++i) { + ArrayList<Bitmap> list = sPools[i]; + synchronized (list) { + list.clear(); + } + } + } + + public static Bitmap decode(JobContext jc, int type, + byte[] data, int offset, int length, BitmapFactory.Options options) { + if (options == null) options = new BitmapFactory.Options(); + if (options.inSampleSize < 1) options.inSampleSize = 1; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + options.inBitmap = (options.inSampleSize == 1) ? getBitmap(type) : null; + try { + Bitmap bitmap = DecodeUtils.decode(jc, data, offset, length, options); + if (options.inBitmap != null && options.inBitmap != bitmap) { + recycle(type, bitmap); + options.inBitmap = null; + } + return bitmap; + } catch (IllegalArgumentException e) { + if (options.inBitmap == null) throw e; + + Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap"); + recycle(type, options.inBitmap); + options.inBitmap = null; + return DecodeUtils.decode(jc, data, offset, length, options); + } + } + + // This is the same as the method above except the source data comes + // from a file descriptor instead of a byte array. + public static Bitmap decode(int type, + JobContext jc, FileDescriptor fileDescriptor, Options options) { + if (options == null) options = new BitmapFactory.Options(); + if (options.inSampleSize < 1) options.inSampleSize = 1; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + options.inBitmap = (options.inSampleSize == 1) ? getBitmap(type) : null; + try { + Bitmap bitmap = DecodeUtils.decode(jc, fileDescriptor, options); + if (options.inBitmap != null&& options.inBitmap != bitmap) { + recycle(type, bitmap); + options.inBitmap = null; + } + return bitmap; + } catch (IllegalArgumentException e) { + if (options.inBitmap == null) throw e; + + Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap"); + recycle(type, options.inBitmap); + options.inBitmap = null; + return DecodeUtils.decode(jc, fileDescriptor, options); + } + } +} |