com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder 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.gif;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import com.bumptech.glide.Glide;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.gifdecoder.GifHeader;
import com.bumptech.glide.gifdecoder.GifHeaderParser;
import com.bumptech.glide.gifdecoder.StandardGifDecoder;
import com.bumptech.glide.load.ImageHeaderParser;
import com.bumptech.glide.load.ImageHeaderParser.ImageType;
import com.bumptech.glide.load.ImageHeaderParserUtils;
import com.bumptech.glide.load.Option;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.UnitTransformation;
import com.bumptech.glide.util.LogTime;
import com.bumptech.glide.util.Util;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Queue;
/**
* An {@link com.bumptech.glide.load.ResourceDecoder} that decodes {@link
* com.bumptech.glide.load.resource.gif.GifDrawable} from {@link java.io.InputStream} data.
*/
public class ByteBufferGifDecoder implements ResourceDecoder {
private static final String TAG = "BufferGifDecoder";
private static final GifDecoderFactory GIF_DECODER_FACTORY = new GifDecoderFactory();
/**
* If set to {@code true}, disables this decoder
* ({@link #handles(ByteBuffer, Options)} will return {@code false}). Defaults to
* {@code false}.
*/
public static final Option DISABLE_ANIMATION = Option.memory(
"com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder.DisableAnimation", false);
private static final GifHeaderParserPool PARSER_POOL = new GifHeaderParserPool();
private final Context context;
private final List parsers;
private final GifHeaderParserPool parserPool;
private final BitmapPool bitmapPool;
private final GifDecoderFactory gifDecoderFactory;
private final GifBitmapProvider provider;
public ByteBufferGifDecoder(Context context) {
this(context, Glide.get(context).getRegistry().getImageHeaderParsers(),
Glide.get(context).getBitmapPool(), Glide.get(context).getArrayPool());
}
public ByteBufferGifDecoder(
Context context, List parsers, BitmapPool bitmapPool,
ArrayPool arrayPool) {
this(context, parsers, bitmapPool, arrayPool, PARSER_POOL, GIF_DECODER_FACTORY);
}
// Visible for testing.
ByteBufferGifDecoder(
Context context,
List parsers,
BitmapPool bitmapPool,
ArrayPool arrayPool,
GifHeaderParserPool parserPool,
GifDecoderFactory gifDecoderFactory) {
this.context = context.getApplicationContext();
this.parsers = parsers;
this.bitmapPool = bitmapPool;
this.gifDecoderFactory = gifDecoderFactory;
this.provider = new GifBitmapProvider(bitmapPool, arrayPool);
this.parserPool = parserPool;
}
@Override
public boolean handles(ByteBuffer source, Options options) throws IOException {
return !options.get(DISABLE_ANIMATION)
&& ImageHeaderParserUtils.getType(parsers, source) == ImageType.GIF;
}
@Override
public GifDrawableResource decode(ByteBuffer source, int width, int height, Options options) {
final GifHeaderParser parser = parserPool.obtain(source);
try {
return decode(source, width, height, parser);
} finally {
parserPool.release(parser);
}
}
private GifDrawableResource decode(ByteBuffer byteBuffer, int width, int height,
GifHeaderParser parser) {
long startTime = LogTime.getLogTime();
final GifHeader header = parser.parseHeader();
if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) {
// If we couldn't decode the GIF, we will end up with a frame count of 0.
return null;
}
int sampleSize = getSampleSize(header, width, height);
GifDecoder gifDecoder = gifDecoderFactory.build(provider, header, byteBuffer, sampleSize);
gifDecoder.advance();
Bitmap firstFrame = gifDecoder.getNextFrame();
if (firstFrame == null) {
return null;
}
Transformation unitTransformation = UnitTransformation.get();
GifDrawable gifDrawable =
new GifDrawable(context, gifDecoder, bitmapPool, unitTransformation, width, height,
firstFrame);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Decoded GIF from stream in " + LogTime.getElapsedMillis(startTime));
}
return new GifDrawableResource(gifDrawable);
}
private static int getSampleSize(GifHeader gifHeader, int targetWidth, int targetHeight) {
int exactSampleSize = Math.min(gifHeader.getHeight() / targetHeight,
gifHeader.getWidth() / targetWidth);
int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 : Integer.highestOneBit(exactSampleSize);
// Although functionally equivalent to 0 for BitmapFactory, 1 is a safer default for our code
// than 0.
int sampleSize = Math.max(1, powerOfTwoSampleSize);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Downsampling GIF"
+ ", sampleSize: " + sampleSize
+ ", target dimens: [" + targetWidth + "x" + targetHeight + "]"
+ ", actual dimens: [" + gifHeader.getWidth() + "x" + gifHeader.getHeight() + "]");
}
return sampleSize;
}
// Visible for testing.
static class GifDecoderFactory {
public GifDecoder build(GifDecoder.BitmapProvider provider, GifHeader header,
ByteBuffer data, int sampleSize) {
return new StandardGifDecoder(provider, header, data, sampleSize);
}
}
// Visible for testing.
static class GifHeaderParserPool {
private final Queue pool = Util.createQueue(0);
public synchronized GifHeaderParser obtain(ByteBuffer buffer) {
GifHeaderParser result = pool.poll();
if (result == null) {
result = new GifHeaderParser();
}
return result.setData(buffer);
}
public synchronized void release(GifHeaderParser parser) {
parser.clear();
pool.offer(parser);
}
}
}