summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/data
diff options
context:
space:
mode:
authorOwen Lin <owenlin@google.com>2012-03-07 17:39:56 +0800
committerOwen Lin <owenlin@google.com>2012-03-14 15:48:54 +0800
commit4bb5912e85f6d1bd8a6b78d6d52b4c4da7aeb740 (patch)
treedf0b40113c30443abe15cdb7d364bf80591c4686 /src/com/android/gallery3d/data
parentd5617d5d6a5f4b0c0f68b2fda5a587b8272ca5c2 (diff)
downloadandroid_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.java83
-rw-r--r--src/com/android/gallery3d/data/ImageCacheRequest.java25
-rw-r--r--src/com/android/gallery3d/data/ImageCacheService.java6
-rw-r--r--src/com/android/gallery3d/data/LocalImage.java40
-rw-r--r--src/com/android/gallery3d/data/LocalVideo.java2
-rw-r--r--src/com/android/gallery3d/data/MediaItem.java16
-rw-r--r--src/com/android/gallery3d/data/MtpImage.java4
-rw-r--r--src/com/android/gallery3d/data/UriImage.java31
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;
}
}