summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMark Wei <markwei@google.com>2013-10-21 16:11:15 -0700
committerMark Wei <markwei@google.com>2013-10-21 19:50:52 -0700
commitcea0c012d538f11b3ee97d4b7e78f4c1ea73d5be (patch)
tree9506342cb46c574f3913c762f6e11d08b532acfd /src
parent7a0df68fac718a68ed44d5c667ebaee38e334f63 (diff)
downloadbitmap-cea0c012d538f11b3ee97d4b7e78f4c1ea73d5be.tar.gz
Modifications to bitmap library to make integrating into BigTop simpler.
RequestKey returns ParcelFileDescriptor instead of AssetFileDescriptor to make it easier to read files off of internal cache. Do not assume we want a top-1/3 crop. Provide vertical center in DecodeTask constructor. Move DecodeTask params into DecodeOptions to avoid param bloat. Add option to not limit bitmap density in BasicBitmapDrawable. Name AsyncTask threads in pool. Link DEBUG flags. Change-Id: I9416ac647c0c4935ee488b7db43cccd9de565c54
Diffstat (limited to 'src')
-rw-r--r--src/com/android/bitmap/DecodeTask.java136
-rw-r--r--src/com/android/bitmap/NamedThreadFactory.java39
-rw-r--r--src/com/android/bitmap/RequestKey.java9
-rw-r--r--src/com/android/bitmap/UnrefedBitmapCache.java2
-rw-r--r--src/com/android/bitmap/UnrefedPooledCache.java2
-rw-r--r--src/com/android/bitmap/drawable/BasicBitmapDrawable.java27
-rw-r--r--src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java18
7 files changed, 180 insertions, 53 deletions
diff --git a/src/com/android/bitmap/DecodeTask.java b/src/com/android/bitmap/DecodeTask.java
index 9dbcea9..c90d772 100644
--- a/src/com/android/bitmap/DecodeTask.java
+++ b/src/com/android/bitmap/DecodeTask.java
@@ -16,12 +16,13 @@
package com.android.bitmap;
-import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Rect;
import android.os.AsyncTask;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.util.Log;
import com.android.bitmap.util.BitmapUtils;
@@ -47,10 +48,10 @@ import java.io.InputStream;
public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
private final RequestKey mKey;
- private final int mDestW;
- private final int mDestH;
+ private final DecodeOptions mDecodeOpts;
private final DecodeCallback mDecodeCallback;
private final BitmapCache mCache;
+
private final BitmapFactory.Options mOpts = new BitmapFactory.Options();
private ReusableBitmap mInBitmap = null;
@@ -58,7 +59,7 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
private static final boolean CROP_DURING_DECODE = true;
private static final String TAG = DecodeTask.class.getSimpleName();
- private static final boolean DEBUG = false;
+ public static final boolean DEBUG = false;
/**
* Callback interface for clients to be notified of decode state changes and completion.
@@ -83,12 +84,19 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
void onDecodeCancel(RequestKey key);
}
- public DecodeTask(RequestKey key, int w, int h, DecodeCallback view,
- BitmapCache cache) {
- mKey = key;
- mDestW = w;
- mDestH = h;
- mDecodeCallback = view;
+ /**
+ * Create new DecodeTask.
+ *
+ * @param requestKey The request to decode, also the key to use for the cache.
+ * @param decodeOpts The decode options.
+ * @param callback The callback to notify of decode state changes.
+ * @param cache The cache and pool.
+ */
+ public DecodeTask(RequestKey requestKey, DecodeOptions decodeOpts,
+ DecodeCallback callback, BitmapCache cache) {
+ mKey = requestKey;
+ mDecodeOpts = decodeOpts;
+ mDecodeCallback = callback;
mCache = cache;
}
@@ -106,7 +114,7 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
}
ReusableBitmap result = null;
- AssetFileDescriptor fd = null;
+ ParcelFileDescriptor fd = null;
InputStream in = null;
try {
final boolean isJellyBeanOrAbove = android.os.Build.VERSION.SDK_INT
@@ -135,7 +143,7 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
Trace.beginSection("get bytesize");
final long byteSize;
if (fd != null) {
- byteSize = fd.getLength();
+ byteSize = fd.getStatSize();
} else {
byteSize = -1;
}
@@ -148,8 +156,8 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
// Creating an input stream from the file descriptor makes it useless
// afterwards.
Trace.beginSection("create fd and stream");
- final AssetFileDescriptor orientationFd = mKey.createFd();
- in = orientationFd.createInputStream();
+ final ParcelFileDescriptor orientationFd = mKey.createFd();
+ in = new AutoCloseInputStream(orientationFd);
Trace.endSection();
}
orientation = Exif.getOrientation(in, byteSize);
@@ -209,7 +217,8 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
srcW = mOpts.outHeight;
srcH = mOpts.outWidth;
}
- mOpts.inSampleSize = calculateSampleSize(srcW, srcH, mDestW, mDestH);
+ mOpts.inSampleSize = calculateSampleSize(srcW, srcH, mDecodeOpts.destW,
+ mDecodeOpts.destH, mDecodeOpts.sampleSizeStrategy);
mOpts.inJustDecodeBounds = false;
mOpts.inMutable = true;
if (isJellyBeanOrAbove && orientation == 0) {
@@ -219,8 +228,9 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
+ mCache.toDebugString());
}
Trace.beginSection("create reusable bitmap");
- mInBitmap = new ReusableBitmap(Bitmap.createBitmap(mDestW, mDestH,
- Bitmap.Config.ARGB_8888));
+ mInBitmap = new ReusableBitmap(
+ Bitmap.createBitmap(mDecodeOpts.destW, mDecodeOpts.destH,
+ Bitmap.Config.ARGB_8888));
Trace.endSection();
if (isCancelled()) {
@@ -359,7 +369,7 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
return result;
}
- private Bitmap decodeCropped(final AssetFileDescriptor fd, final InputStream in,
+ private Bitmap decodeCropped(final ParcelFileDescriptor fd, final InputStream in,
final int orientation, final Rect outSrcRect) throws IOException {
final BitmapRegionDecoder brd;
if (fd != null) {
@@ -386,11 +396,13 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
// Coordinates are orientation corrected.
// Center the decode on the top 1/3.
- BitmapUtils.calculateCroppedSrcRect(srcW, srcH, mDestW, mDestH, mDestH, mOpts.inSampleSize,
- 1f / 3, true /* absoluteFraction */, 1f, outSrcRect);
+ BitmapUtils.calculateCroppedSrcRect(srcW, srcH, mDecodeOpts.destW, mDecodeOpts.destH,
+ mDecodeOpts.destH, mOpts.inSampleSize, mDecodeOpts.verticalCenter,
+ true /* absoluteFraction */,
+ 1f, outSrcRect);
if (DEBUG) System.out.println("rect for this decode is: " + outSrcRect
+ " srcW/H=" + srcW + "/" + srcH
- + " dstW/H=" + mDestW + "/" + mDestH);
+ + " dstW/H=" + mDecodeOpts.destW + "/" + mDecodeOpts.destH);
// calculateCroppedSrcRect() gave us the source rectangle "as if" the orientation has
// been corrected. We need to decode the uncorrected source rectangle. Calculate true
@@ -425,7 +437,7 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
return in;
}
- private Bitmap decode(AssetFileDescriptor fd, InputStream in) {
+ private Bitmap decode(ParcelFileDescriptor fd, InputStream in) {
final Bitmap result;
if (fd != null) {
result = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, mOpts);
@@ -435,19 +447,22 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
return result;
}
- private static int calculateSampleSize(int srcW, int srcH, int destW, int destH) {
+ private static int calculateSampleSize(int srcW, int srcH, int destW, int destH, int strategy) {
int result;
final float sz = Math.min((float) srcW / destW, (float) srcH / destH);
- // round to the nearest power of two, or just truncate
- final boolean stricter = true;
-
- //noinspection ConstantConditions
- if (stricter) {
- result = (int) Math.pow(2, (int) (0.5 + (Math.log(sz) / Math.log(2))));
- } else {
- result = (int) sz;
+ switch (strategy) {
+ case DecodeOptions.STRATEGY_TRUNCATE:
+ result = (int) sz;
+ break;
+ case DecodeOptions.STRATEGY_ROUND_UP:
+ result = (int) Math.ceil(sz);
+ break;
+ case DecodeOptions.STRATEGY_ROUND_NEAREST:
+ default:
+ result = (int) Math.pow(2, (int) (0.5 + (Math.log(sz) / Math.log(2))));
+ break;
}
return Math.max(1, result);
}
@@ -481,4 +496,63 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
}
}
+ public static class DecodeOptions {
+
+ /**
+ * Round sample size to the nearest power of 2. Depending on the source and destination
+ * dimensions, we will either truncate, in which case we decode from a bigger region and
+ * crop down, or we will round up, in which case we decode from a smaller region and scale
+ * up.
+ */
+ public static final int STRATEGY_ROUND_NEAREST = 0;
+ /**
+ * Always decode from a bigger region and crop down.
+ */
+ public static final int STRATEGY_TRUNCATE = 1;
+
+ /**
+ * Always decode from a smaller region and scale up.
+ */
+ public static final int STRATEGY_ROUND_UP = 2;
+
+ /**
+ * The destination width to decode to.
+ */
+ public int destW;
+ /**
+ * The destination height to decode to.
+ */
+ public int destH;
+ /**
+ * If the destination dimensions are smaller than the source image provided by the request
+ * key, this will determine where vertically the destination rect will be cropped from.
+ * Value from 0f for top-most crop to 1f for bottom-most crop.
+ */
+ public float verticalCenter;
+ /**
+ * One of the STRATEGY constants.
+ */
+ public int sampleSizeStrategy;
+
+ public DecodeOptions(final int destW, final int destH) {
+ this(destW, destH, 0.5f, STRATEGY_ROUND_NEAREST);
+ }
+
+ /**
+ * Create new DecodeOptions.
+ * @param destW The destination width to decode to.
+ * @param destH The destination height to decode to.
+ * @param verticalCenter If the destination dimensions are smaller than the source image
+ * provided by the request key, this will determine where vertically
+ * the destination rect will be cropped from.
+ * @param sampleSizeStrategy One of the STRATEGY constants.
+ */
+ public DecodeOptions(final int destW, final int destH, final float verticalCenter,
+ final int sampleSizeStrategy) {
+ this.destW = destW;
+ this.destH = destH;
+ this.verticalCenter = verticalCenter;
+ this.sampleSizeStrategy = sampleSizeStrategy;
+ }
+ }
}
diff --git a/src/com/android/bitmap/NamedThreadFactory.java b/src/com/android/bitmap/NamedThreadFactory.java
new file mode 100644
index 0000000..83fb13a
--- /dev/null
+++ b/src/com/android/bitmap/NamedThreadFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bitmap;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class NamedThreadFactory implements ThreadFactory {
+ private final ThreadFactory mDefaultThreadFactory;
+ private final String mBaseName;
+ private final AtomicInteger mCount = new AtomicInteger(0);
+
+ public NamedThreadFactory(final String baseName) {
+ mDefaultThreadFactory = Executors.defaultThreadFactory();
+ mBaseName = baseName;
+ }
+
+ @Override
+ public Thread newThread(final Runnable runnable) {
+ final Thread thread = mDefaultThreadFactory.newThread(runnable);
+ thread.setName(mBaseName + "-" + mCount.getAndIncrement());
+ return thread;
+ }
+}
diff --git a/src/com/android/bitmap/RequestKey.java b/src/com/android/bitmap/RequestKey.java
index 108b255..0e92164 100644
--- a/src/com/android/bitmap/RequestKey.java
+++ b/src/com/android/bitmap/RequestKey.java
@@ -16,8 +16,7 @@
package com.android.bitmap;
-import android.content.res.AssetFileDescriptor;
-
+import android.os.ParcelFileDescriptor;
import java.io.IOException;
import java.io.InputStream;
@@ -36,10 +35,10 @@ import java.io.InputStream;
public interface RequestKey {
/**
- * Create an {@link AssetFileDescriptor} for a local file stored on the device. This method will
- * be called first; if it returns null, {@link #createInputStream()} will be called.
+ * Create an {@link ParcelFileDescriptor} for a local file stored on the device. This method
+ * will be called first; if it returns null, {@link #createInputStream()} will be called.
*/
- public AssetFileDescriptor createFd() throws IOException;
+ public ParcelFileDescriptor createFd() throws IOException;
/**
* Create an {@link InputStream} for a file. This method will be called if {@link #createFd()}
diff --git a/src/com/android/bitmap/UnrefedBitmapCache.java b/src/com/android/bitmap/UnrefedBitmapCache.java
index 428880e..f30b8fc 100644
--- a/src/com/android/bitmap/UnrefedBitmapCache.java
+++ b/src/com/android/bitmap/UnrefedBitmapCache.java
@@ -39,7 +39,7 @@ public class UnrefedBitmapCache extends UnrefedPooledCache<RequestKey, ReusableB
private LruCache<RequestKey, NullReusableBitmap> mNullRequests;
- private final static boolean DEBUG = false;
+ private final static boolean DEBUG = DecodeTask.DEBUG;
private final static String TAG = UnrefedBitmapCache.class.getSimpleName();
public UnrefedBitmapCache(final int targetSizeBytes, final float nonPooledFraction,
diff --git a/src/com/android/bitmap/UnrefedPooledCache.java b/src/com/android/bitmap/UnrefedPooledCache.java
index ff0ab7b..50d793f 100644
--- a/src/com/android/bitmap/UnrefedPooledCache.java
+++ b/src/com/android/bitmap/UnrefedPooledCache.java
@@ -47,7 +47,7 @@ public class UnrefedPooledCache<K, V extends Poolable> implements PooledCache<K,
private final int mTargetSize;
private final LruCache<K, V> mNonPooledCache;
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = DecodeTask.DEBUG;
private static final String TAG = UnrefedPooledCache.class.getSimpleName();
/**
diff --git a/src/com/android/bitmap/drawable/BasicBitmapDrawable.java b/src/com/android/bitmap/drawable/BasicBitmapDrawable.java
index 2aa6011..ae26571 100644
--- a/src/com/android/bitmap/drawable/BasicBitmapDrawable.java
+++ b/src/com/android/bitmap/drawable/BasicBitmapDrawable.java
@@ -27,6 +27,8 @@ import android.util.Log;
import com.android.bitmap.BitmapCache;
import com.android.bitmap.DecodeTask;
+import com.android.bitmap.DecodeTask.DecodeOptions;
+import com.android.bitmap.NamedThreadFactory;
import com.android.bitmap.RequestKey;
import com.android.bitmap.ReusableBitmap;
import com.android.bitmap.util.BitmapUtils;
@@ -50,34 +52,39 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
private RequestKey mCurrKey;
private ReusableBitmap mBitmap;
private final BitmapCache mCache;
+ private final boolean mLimitDensity;
private DecodeTask mTask;
private int mDecodeWidth;
+
private int mDecodeHeight;
+ private static final String TAG = BasicBitmapDrawable.class.getSimpleName();
// based on framework CL:I015d77
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+
private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, 1, TimeUnit.SECONDS,
- new LinkedBlockingQueue<Runnable>(128));
+ new LinkedBlockingQueue<Runnable>(128), new NamedThreadFactory("decode"));
private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
- private static final boolean LIMIT_BITMAP_DENSITY = true;
-
private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH;
+ private static final float VERTICAL_CENTER = 1f / 2;
private final float mDensity;
private final Paint mPaint = new Paint();
+
private final Rect mSrcRect = new Rect();
- private static final String TAG = BasicBitmapDrawable.class.getSimpleName();
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = DecodeTask.DEBUG;
- public BasicBitmapDrawable(final Resources res, final BitmapCache cache) {
+ public BasicBitmapDrawable(final Resources res, final BitmapCache cache,
+ final boolean limitDensity) {
mDensity = res.getDisplayMetrics().density;
mCache = cache;
+ mLimitDensity = limitDensity;
mPaint.setFilterBitmap(true);
}
@@ -156,7 +163,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(),
bounds.width(), bounds.height(),
bounds.height(), Integer.MAX_VALUE,
- 0.5f, false /* absoluteFraction */,
+ VERTICAL_CENTER, false /* absoluteFraction */,
1, mSrcRect);
final int orientation = mBitmap.getOrientation();
@@ -236,7 +243,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
}
Trace.beginSection("decode");
- if (LIMIT_BITMAP_DENSITY) {
+ if (mLimitDensity) {
final float scale =
Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT
/ mDensity);
@@ -254,7 +261,9 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
if (mTask != null) {
mTask.cancel();
}
- mTask = new DecodeTask(mCurrKey, bufferW, bufferH, this, mCache);
+ final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, VERTICAL_CENTER,
+ DecodeOptions.STRATEGY_ROUND_NEAREST);
+ mTask = new DecodeTask(mCurrKey, opts, this, mCache);
mTask.executeOnExecutor(EXECUTOR);
Trace.endSection();
}
diff --git a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
index 01f5638..61efa12 100644
--- a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
+++ b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
@@ -32,6 +32,7 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.view.animation.LinearInterpolator;
+import com.android.bitmap.DecodeTask.DecodeOptions;
import com.android.bitmap.R;
import com.android.bitmap.BitmapCache;
import com.android.bitmap.DecodeAggregator;
@@ -59,8 +60,10 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod
Drawable.Callback, Runnable, Parallaxable, DecodeAggregator.Callback {
private RequestKey mCurrKey;
+
private ReusableBitmap mBitmap;
private final BitmapCache mCache;
+ private final boolean mLimitDensity;
private DecodeAggregator mDecodeAggregator;
private DecodeTask mTask;
private int mDecodeWidth;
@@ -80,10 +83,10 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod
private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
- private static final boolean LIMIT_BITMAP_DENSITY = true;
-
private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH;
+ private static final float VERTICAL_CENTER = 1f / 3;
+
private static final int LOAD_STATE_UNINITIALIZED = 0;
private static final int LOAD_STATE_NOT_YET_LOADED = 1;
private static final int LOAD_STATE_LOADING = 2;
@@ -100,10 +103,11 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod
public static final String TAG = ExtendedBitmapDrawable.class.getSimpleName();
public ExtendedBitmapDrawable(final Resources res, final BitmapCache cache,
- final DecodeAggregator decodeAggregator, final Drawable placeholder,
- final Drawable progress) {
+ final boolean limitDensity, final DecodeAggregator decodeAggregator,
+ final Drawable placeholder, final Drawable progress) {
mDensity = res.getDisplayMetrics().density;
mCache = cache;
+ mLimitDensity = limitDensity;
this.mDecodeAggregator = decodeAggregator;
mPaint.setFilterBitmap(true);
@@ -365,7 +369,7 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod
}
Trace.beginSection("decode");
- if (LIMIT_BITMAP_DENSITY) {
+ if (mLimitDensity) {
final float scale =
Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT
/ mDensity);
@@ -384,7 +388,9 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod
mTask.cancel();
}
setLoadState(LOAD_STATE_NOT_YET_LOADED);
- mTask = new DecodeTask(mCurrKey, bufferW, bufferH, this, mCache);
+ final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, VERTICAL_CENTER,
+ DecodeOptions.STRATEGY_ROUND_NEAREST);
+ mTask = new DecodeTask(mCurrKey, opts, this, mCache);
mTask.executeOnExecutor(EXECUTOR);
Trace.endSection();
}