package com.bumptech.glide.load.resource.gif;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.ResourceEncoder;
import com.bumptech.glide.load.SkipCache;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
import com.bumptech.glide.load.resource.NullCacheDecoder;
import com.bumptech.glide.load.resource.bitmap.BitmapEncoder;
import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.request.target.SimpleTarget;
import java.io.InputStream;
class GifFrameManager {
// 16ms per frame = 60fps
static final long MIN_FRAME_DELAY = 16;
private final MemorySizeCalculator calculator;
private final GifFrameModelLoader frameLoader;
private final GifFrameResourceDecoder frameResourceDecoder;
private final ResourceDecoder<InputStream, Bitmap> cacheDecoder;
private final GifDecoder decoder;
private final Handler mainHandler;
private final ResourceEncoder<Bitmap> encoder;
private final Context context;
private Transformation<Bitmap> transformation;
private DelayTarget current;
private DelayTarget next;
public interface FrameCallback {
public void onFrameRead(Bitmap frame);
}
public GifFrameManager(Context context, GifDecoder decoder, Transformation<Bitmap> transformation) {
this(context, decoder, new Handler(Looper.getMainLooper()), transformation);
}
public GifFrameManager(Context context, GifDecoder decoder, Handler mainHandler,
Transformation<Bitmap> transformation) {
this.context = context;
this.decoder = decoder;
this.mainHandler = mainHandler;
this.transformation = transformation;
calculator = new MemorySizeCalculator(context);
frameLoader = new GifFrameModelLoader();
frameResourceDecoder = new GifFrameResourceDecoder();
if (!decoder.isTransparent()) {
// For non transparent gifs, we can beat the performance of our gif decoder for each frame by decoding jpegs
// from disk.
cacheDecoder = new StreamBitmapDecoder(context);
encoder = new BitmapEncoder(Bitmap.CompressFormat.JPEG, 70);
} else {
// For transparent gifs, we would have to encode as pngs which is actually slower than our gif decoder so we
// avoid writing frames to the disk cache entirely.
cacheDecoder = NullCacheDecoder.get();
encoder = SkipCache.get();
}
}
Transformation<Bitmap> getTransformation() {
return transformation;
}
public void getNextFrame(FrameCallback cb) {
decoder.advance();
// We don't want to blow out the entire memory cache with frames of gifs, so try to set some
// maximum size beyond which we will always just decode one frame at a time.
boolean skipCache = decoder.getDecodedFramesByteSizeSum() > calculator.getMemoryCacheSize() / 2;
long targetTime = SystemClock.uptimeMillis() + (Math.min(MIN_FRAME_DELAY, decoder.getNextDelay()));
next = new DelayTarget(decoder, cb, targetTime, mainHandler);
Glide.with(context)
.using(frameLoader, GifDecoder.class)
.load(decoder)
.as(Bitmap.class)
.decoder(frameResourceDecoder)
.cacheDecoder(cacheDecoder)
.transform(transformation)
.encoder(encoder)
.skipMemoryCache(skipCache)
.into(next);
}
public void clear() {
if (current != null) {
Glide.clear(current);
mainHandler.removeCallbacks(current);
}
if (next != null) {
Glide.clear(next);
mainHandler.removeCallbacks(next);
}
}
class DelayTarget extends SimpleTarget<Bitmap> implements Runnable {
private FrameCallback cb;
private long targetTime;
private Handler mainHandler;
private Bitmap resource;
public DelayTarget(GifDecoder decoder, FrameCallback cb, long targetTime, Handler mainHandler) {
super(decoder.getWidth(), decoder.getHeight());
this.cb = cb;
this.targetTime = targetTime;
this.mainHandler = mainHandler;
}
@Override
public void onResourceReady(final Bitmap resource) {
this.resource = resource;
mainHandler.postAtTime(this, targetTime);
if (current != null) {
Glide.clear(current);
}
current = next;
next = null;
}
@Override
public void run() {
cb.onFrameRead(resource);
}
}
}