com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glide Show documentation
Show all versions of glide Show documentation
A fast and efficient image loading library for Android focused on smooth scrolling.
package com.bumptech.glide.load.resource.bitmap;
import android.graphics.Bitmap;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.util.ExceptionCatchingInputStream;
import com.bumptech.glide.util.MarkEnforcingInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Decodes {@link android.graphics.Bitmap Bitmaps} from {@link java.io.InputStream InputStreams}.
*/
public class StreamBitmapDecoder implements ResourceDecoder {
private final Downsampler downsampler;
private final ArrayPool byteArrayPool;
public StreamBitmapDecoder(Downsampler downsampler, ArrayPool byteArrayPool) {
this.downsampler = downsampler;
this.byteArrayPool = byteArrayPool;
}
@Override
public boolean handles(InputStream source, Options options) throws IOException {
return downsampler.handles(source);
}
@Override
public Resource decode(InputStream source, int width, int height, Options options)
throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a
// way to determine if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still
// attempt to decode the full image even when the header decode fails and/or overflows our read
// buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
/**
* Callbacks that provide reasonable handling for streams that may be unbuffered or insufficiently
* buffered or that may throw exceptions during decoding.
*/
static class UntrustedCallbacks implements Downsampler.DecodeCallbacks {
private final RecyclableBufferedInputStream bufferedStream;
private final ExceptionCatchingInputStream exceptionStream;
public UntrustedCallbacks(RecyclableBufferedInputStream bufferedStream,
ExceptionCatchingInputStream exceptionStream) {
this.bufferedStream = bufferedStream;
this.exceptionStream = exceptionStream;
}
@Override
public void onObtainBounds() {
// 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.
bufferedStream.fixMarkLimit();
}
@Override
public void onDecodeComplete(BitmapPool bitmapPool, Bitmap downsampled) throws IOException {
// 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.
IOException streamException = exceptionStream.getException();
if (streamException != null) {
if (downsampled != null) {
bitmapPool.put(downsampled);
}
throw streamException;
}
}
}
}