提交 2007440c 编写于 作者: S Sam Judd

Add API to fix mark limit/buffer size.

Fixes #225.
上级 17180f4c
package com.bumptech.glide.util;
import com.bumptech.glide.load.resource.bitmap.RecyclableBufferedInputStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
......@@ -24,12 +25,12 @@ import static org.mockito.Mockito.when;
@RunWith(JUnit4.class)
public class ExceptionCatchingInputStreamTest {
private InputStream wrapped;
private RecyclableBufferedInputStream wrapped;
private ExceptionCatchingInputStream is;
@Before
public void setUp() throws Exception {
wrapped = mock(InputStream.class);
wrapped = mock(RecyclableBufferedInputStream.class);
is = new ExceptionCatchingInputStream();
is.setInputStream(wrapped);
}
......@@ -93,6 +94,13 @@ public class ExceptionCatchingInputStreamTest {
verify(wrapped).reset();
}
@Test
public void testFixMarkLimitCallsFixMarkLimitOnWrappedStream() {
is.fixMarkLimit();
verify(wrapped).fixMarkLimit();
}
@Test
public void testCallsSkipOnWrapped() throws IOException {
long toSkip = 67;
......
......@@ -63,6 +63,10 @@ public class LruBitmapPool implements BitmapPool {
@Override
public synchronized boolean put(Bitmap bitmap) {
if (!bitmap.isMutable() || strategy.getSize(bitmap) > maxSize) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Reject bitmap from pool=" + strategy.logBitmap(bitmap) + " is mutable="
+ bitmap.isMutable());
}
return false;
}
......
......@@ -105,6 +105,7 @@ public abstract class Downsampler implements BitmapDecoder<InputStream> {
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
// TODO(#126): when the framework handles exceptions better, consider removing.
final ExceptionCatchingInputStream stream =
ExceptionCatchingInputStream.obtain(new RecyclableBufferedInputStream(is, bytesForStream));
......@@ -137,9 +138,10 @@ public abstract class Downsampler implements BitmapDecoder<InputStream> {
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(stream, options, pool, inWidth, inHeight, sampleSize, decodeFormat);
downsampleWithSize(stream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
// BitmapDecoder swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
final Exception streamException = stream.getException();
......@@ -185,8 +187,8 @@ public abstract class Downsampler implements BitmapDecoder<InputStream> {
return Math.max(1, powerOfTwoSampleSize);
}
protected Bitmap downsampleWithSize(InputStream is, BitmapFactory.Options options,
BitmapPool pool, int inWidth, int inHeight, int sampleSize, DecodeFormat decodeFormat) {
private Bitmap downsampleWithSize(ExceptionCatchingInputStream is, BitmapFactory.Options options, BitmapPool pool,
int inWidth, int inHeight, int sampleSize, DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
Bitmap.Config config = getConfig(is, decodeFormat);
options.inSampleSize = sampleSize;
......@@ -280,15 +282,14 @@ public abstract class Downsampler implements BitmapDecoder<InputStream> {
* android.graphics.BitmapFactory.Options)}.
* @return an array containing the dimensions of the image in the form {width, height}.
*/
public int[] getDimensions(InputStream is, BitmapFactory.Options options) {
public int[] getDimensions(ExceptionCatchingInputStream is, BitmapFactory.Options options) {
options.inJustDecodeBounds = true;
decodeStream(is, options);
options.inJustDecodeBounds = false;
return new int[] { options.outWidth, options.outHeight };
}
private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options) {
private static Bitmap decodeStream(ExceptionCatchingInputStream is, BitmapFactory.Options options) {
if (options.inJustDecodeBounds) {
// This is large, but jpeg headers are not size bounded so we need something large enough to minimize
// the possibility of not being able to fit enough of the header in the buffer to get the image size so
......@@ -296,6 +297,11 @@ public abstract class Downsampler implements BitmapDecoder<InputStream> {
// original size each time we use up the buffer space without passing the mark so this is a maximum
// bound on the buffer size, not a default. Most of the time we won't go past our pre-allocated 16kb.
is.mark(MARK_POSITION);
} else {
// Once we've read the image header, we no longer need to allow the buffer to expand in size. To avoid
// unnecessary allocations reading image data, we fix the mark limit so that it is no larger than our
// current buffer size here. See issue #225.
is.fixMarkLimit();
}
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
......
......@@ -17,6 +17,8 @@ package com.bumptech.glide.load.resource.bitmap;
* limitations under the License.
*/
import android.util.Log;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
......@@ -36,32 +38,33 @@ import java.io.InputStream;
* </pre>
*/
public class RecyclableBufferedInputStream extends FilterInputStream {
private static final String TAG = "BufferedIs";
/**
* The buffer containing the current bytes read from the target InputStream.
*/
protected volatile byte[] buf;
private volatile byte[] buf;
/**
* The total number of bytes inside the byte array {@code buf}.
*/
protected int count;
private int count;
/**
* The current limit, which when passed, invalidates the current mark.
*/
protected int marklimit;
private int marklimit;
/**
* The currently marked position. -1 indicates no mark has been set or the
* mark has been invalidated.
*/
protected int markpos = -1;
private int markpos = -1;
/**
* The current position within the byte array {@code buf}.
*/
protected int pos;
private int pos;
public RecyclableBufferedInputStream(InputStream in, byte[] buffer) {
super(in);
......@@ -94,6 +97,17 @@ public class RecyclableBufferedInputStream extends FilterInputStream {
throw new IOException("BufferedInputStream is closed");
}
/**
* Reduces the mark limit to match the current buffer length to prevent the buffer from
* continuing to increase in size.
*
* <p>Subsequent calls to {@link #mark(int)} will be obeyed and may cause the buffer size
* to increase.
*/
public synchronized void fixMarkLimit() {
marklimit = buf.length;
}
/**
* Closes this stream. The source stream is closed and any resources
* associated with it are released.
......@@ -134,6 +148,9 @@ public class RecyclableBufferedInputStream extends FilterInputStream {
if (newLength > marklimit) {
newLength = marklimit;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "allocate buffer of length: " + newLength);
}
byte[] newbuf = new byte[newLength];
System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length);
// Reassign buf, which will invalidate any local references
......
package com.bumptech.glide.util;
import com.bumptech.glide.load.resource.bitmap.RecyclableBufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Queue;
......@@ -15,10 +17,10 @@ public class ExceptionCatchingInputStream extends InputStream {
private static final Queue<ExceptionCatchingInputStream> QUEUE = Util.createQueue(0);
private InputStream wrapped;
private RecyclableBufferedInputStream wrapped;
private IOException exception;
public static ExceptionCatchingInputStream obtain(InputStream toWrap) {
public static ExceptionCatchingInputStream obtain(RecyclableBufferedInputStream toWrap) {
ExceptionCatchingInputStream result;
synchronized (QUEUE) {
result = QUEUE.poll();
......@@ -41,7 +43,7 @@ public class ExceptionCatchingInputStream extends InputStream {
// Do nothing.
}
void setInputStream(InputStream toWrap) {
void setInputStream(RecyclableBufferedInputStream toWrap) {
wrapped = toWrap;
}
......@@ -118,6 +120,10 @@ public class ExceptionCatchingInputStream extends InputStream {
return result;
}
public void fixMarkLimit() {
wrapped.fixMarkLimit();
}
public IOException getException() {
return exception;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册