Merge "bitmapfun: Add support to use inBitmap option" into jb-mr1-dev
This commit is contained in:
@@ -20,8 +20,8 @@ import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
@@ -33,11 +33,16 @@ import android.util.Log;
|
||||
import com.example.android.bitmapfun.BuildConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class holds our bitmap caches (memory and disk).
|
||||
@@ -68,6 +73,8 @@ public class ImageCache {
|
||||
private final Object mDiskCacheLock = new Object();
|
||||
private boolean mDiskCacheStarting = true;
|
||||
|
||||
private HashSet<SoftReference<Bitmap>> mReusableBitmaps;
|
||||
|
||||
/**
|
||||
* Creating a new ImageCache object using the specified parameters.
|
||||
*
|
||||
@@ -126,6 +133,12 @@ public class ImageCache {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")");
|
||||
}
|
||||
|
||||
// If we're running on Honeycomb or newer, then
|
||||
if (Utils.hasHoneycomb()) {
|
||||
mReusableBitmaps = new HashSet<SoftReference<Bitmap>>();
|
||||
}
|
||||
|
||||
mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
|
||||
|
||||
/**
|
||||
@@ -138,6 +151,14 @@ public class ImageCache {
|
||||
// The removed entry is a recycling drawable, so notify it
|
||||
// that it has been removed from the memory cache
|
||||
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
|
||||
} else {
|
||||
// The removed entry is a standard BitmapDrawable
|
||||
|
||||
if (Utils.hasHoneycomb()) {
|
||||
// We're running on Honeycomb or later, so add the bitmap
|
||||
// to a SoftRefrence set for possible use with inBitmap later
|
||||
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +298,8 @@ public class ImageCache {
|
||||
*/
|
||||
public Bitmap getBitmapFromDiskCache(String data) {
|
||||
final String key = hashKeyForDisk(data);
|
||||
Bitmap bitmap = null;
|
||||
|
||||
synchronized (mDiskCacheLock) {
|
||||
while (mDiskCacheStarting) {
|
||||
try {
|
||||
@@ -293,8 +316,12 @@ public class ImageCache {
|
||||
}
|
||||
inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
|
||||
if (inputStream != null) {
|
||||
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
|
||||
return bitmap;
|
||||
FileDescriptor fd = ((FileInputStream) inputStream).getFD();
|
||||
|
||||
// Decode bitmap, but we don't want to sample so give
|
||||
// MAX_VALUE as the target dimensions
|
||||
bitmap = ImageResizer.decodeSampledBitmapFromDescriptor(
|
||||
fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
@@ -307,10 +334,43 @@ public class ImageCache {
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options - BitmapFactory.Options with out* options populated
|
||||
* @return Bitmap that case be used for inBitmap
|
||||
*/
|
||||
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
|
||||
Bitmap bitmap = null;
|
||||
|
||||
if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
|
||||
final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps.iterator();
|
||||
Bitmap item;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
item = iterator.next().get();
|
||||
|
||||
if (null != item && item.isMutable()) {
|
||||
// Check to see it the item can be used for inBitmap
|
||||
if (canUseForInBitmap(item, options)) {
|
||||
bitmap = item;
|
||||
|
||||
// Remove from reusable set so it can't be used again
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Remove from the set if the reference has been cleared.
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears both the memory and disk cache associated with this ImageCache object. Note that
|
||||
* this includes disk access so this should not be executed on the main/UI thread.
|
||||
@@ -425,6 +485,20 @@ public class ImageCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param candidate - Bitmap to check
|
||||
* @param targetOptions - Options that have the out* value populated
|
||||
* @return true if <code>candidate</code> can be used for inBitmap re-use with
|
||||
* <code>targetOptions</code>
|
||||
*/
|
||||
private static boolean canUseForInBitmap(
|
||||
Bitmap candidate, BitmapFactory.Options targetOptions) {
|
||||
int width = targetOptions.outWidth / targetOptions.inSampleSize;
|
||||
int height = targetOptions.outHeight / targetOptions.inSampleSize;
|
||||
|
||||
return candidate.getWidth() == width && candidate.getHeight() == height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a usable cache directory (external if available, internal otherwise).
|
||||
*
|
||||
|
||||
@@ -241,7 +241,8 @@ public class ImageFetcher extends ImageResizer {
|
||||
|
||||
Bitmap bitmap = null;
|
||||
if (fileDescriptor != null) {
|
||||
bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth, mImageHeight);
|
||||
bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth,
|
||||
mImageHeight, getImageCache());
|
||||
}
|
||||
if (fileInputStream != null) {
|
||||
try {
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.example.android.bitmapfun.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.android.bitmapfun.BuildConfig;
|
||||
@@ -90,7 +92,8 @@ public class ImageResizer extends ImageWorker {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "processBitmap - " + resId);
|
||||
}
|
||||
return decodeSampledBitmapFromResource(mResources, resId, mImageWidth, mImageHeight);
|
||||
return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,
|
||||
mImageHeight, getImageCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,11 +108,12 @@ public class ImageResizer extends ImageWorker {
|
||||
* @param resId The resource id of the image data
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
|
||||
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions
|
||||
* that are equal to or greater than the requested width and height
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
|
||||
int reqWidth, int reqHeight) {
|
||||
int reqWidth, int reqHeight, ImageCache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
@@ -119,6 +123,11 @@ public class ImageResizer extends ImageWorker {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
return BitmapFactory.decodeResource(res, resId, options);
|
||||
@@ -130,11 +139,12 @@ public class ImageResizer extends ImageWorker {
|
||||
* @param filename The full path of the file to decode
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
|
||||
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions
|
||||
* that are equal to or greater than the requested width and height
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromFile(String filename,
|
||||
int reqWidth, int reqHeight) {
|
||||
int reqWidth, int reqHeight, ImageCache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
@@ -144,6 +154,11 @@ public class ImageResizer extends ImageWorker {
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
return BitmapFactory.decodeFile(filename, options);
|
||||
@@ -155,11 +170,12 @@ public class ImageResizer extends ImageWorker {
|
||||
* @param fileDescriptor The file descriptor to read from
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
|
||||
* @return A bitmap sampled down from the original with the same aspect ratio and dimensions
|
||||
* that are equal to or greater than the requested width and height
|
||||
*/
|
||||
public static Bitmap decodeSampledBitmapFromDescriptor(
|
||||
FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {
|
||||
FileDescriptor fileDescriptor, int reqWidth, int reqHeight, ImageCache cache) {
|
||||
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
@@ -171,9 +187,34 @@ public class ImageResizer extends ImageWorker {
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
// If we're running on Honeycomb or newer, try to use inBitmap
|
||||
if (Utils.hasHoneycomb()) {
|
||||
addInBitmapOptions(options, cache);
|
||||
}
|
||||
|
||||
return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
|
||||
// inBitmap only works with mutable bitmaps so force the decoder to
|
||||
// return mutable bitmaps.
|
||||
options.inMutable = true;
|
||||
|
||||
if (cache != null) {
|
||||
// Try and find a bitmap to use for inBitmap
|
||||
Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
|
||||
|
||||
if (inBitmap != null) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Found bitmap to use for inBitmap");
|
||||
}
|
||||
options.inBitmap = inBitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
|
||||
* bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
|
||||
|
||||
@@ -164,6 +164,13 @@ public abstract class ImageWorker {
|
||||
*/
|
||||
protected abstract Bitmap processBitmap(Object data);
|
||||
|
||||
/**
|
||||
* @return The {@link ImageCache} object currently being used by this ImageWorker.
|
||||
*/
|
||||
protected ImageCache getImageCache() {
|
||||
return mImageCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels any pending work attached to the provided ImageView.
|
||||
* @param imageView
|
||||
|
||||
Reference in New Issue
Block a user