diff options
author | Owen Lin <owenlin@google.com> | 2012-03-07 17:39:56 +0800 |
---|---|---|
committer | Owen Lin <owenlin@google.com> | 2012-03-14 15:48:54 +0800 |
commit | 4bb5912e85f6d1bd8a6b78d6d52b4c4da7aeb740 (patch) | |
tree | df0b40113c30443abe15cdb7d364bf80591c4686 /src/com/android/gallery3d/data | |
parent | d5617d5d6a5f4b0c0f68b2fda5a587b8272ca5c2 (diff) | |
download | android_packages_apps_Gallery2-4bb5912e85f6d1bd8a6b78d6d52b4c4da7aeb740.tar.gz android_packages_apps_Gallery2-4bb5912e85f6d1bd8a6b78d6d52b4c4da7aeb740.tar.bz2 android_packages_apps_Gallery2-4bb5912e85f6d1bd8a6b78d6d52b4c4da7aeb740.zip |
Reuse bitmap for all micro thumb images to prevent GC.
Change-Id: I27d3002e5bb745a597f52962fe24744c8329441c
Diffstat (limited to 'src/com/android/gallery3d/data')
-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 |
8 files changed, 118 insertions, 89 deletions
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; } } |