blob: 960f808484987e83d26e65c65d1a166db4289b33 [file] [log] [blame]
package com.bumptech.glide.load.resource.gif;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import com.bumptech.glide.Glide;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.load.Encoder;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.NullEncoder;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.UUID;
class GifFrameManager {
private final GifFrameModelLoader frameLoader;
private final GifFrameResourceDecoder frameResourceDecoder;
private final GifDecoder decoder;
private final Handler mainHandler;
private final Context context;
private final Encoder<GifDecoder> sourceEncoder;
private final Transformation<Bitmap>[] transformation;
private final int targetWidth;
private final int targetHeight;
private final FrameSignature signature;
private DelayTarget current;
private DelayTarget next;
public interface FrameCallback {
void onFrameRead(int index);
}
public GifFrameManager(Context context, GifDecoder decoder, Transformation<Bitmap> transformation, int targetWidth,
int targetHeight) {
this(context, Glide.get(context).getBitmapPool(), decoder, new Handler(Looper.getMainLooper()), transformation,
targetWidth, targetHeight);
}
@SuppressWarnings("unchecked")
public GifFrameManager(Context context, BitmapPool bitmapPool, GifDecoder decoder, Handler mainHandler,
Transformation<Bitmap> transformation, int targetWidth, int targetHeight) {
if (transformation == null) {
throw new NullPointerException("Transformation must not be null");
}
this.context = context;
this.frameResourceDecoder = new GifFrameResourceDecoder(bitmapPool);
this.decoder = decoder;
this.mainHandler = mainHandler;
this.transformation = new Transformation[] {transformation};
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
this.frameLoader = new GifFrameModelLoader();
this.sourceEncoder = NullEncoder.get();
this.signature = new FrameSignature();
}
Transformation<Bitmap> getTransformation() {
return transformation[0];
}
public void getNextFrame(FrameCallback cb) {
decoder.advance();
long targetTime = SystemClock.uptimeMillis() + decoder.getNextDelay();
next = new DelayTarget(cb, targetTime);
next.setFrameIndex(decoder.getCurrentFrameIndex());
// Use an incrementing signature to make sure we never hit an active resource that matches one of our frames.
signature.increment();
Glide.with(context)
.using(frameLoader, GifDecoder.class)
.load(decoder)
.as(Bitmap.class)
.signature(signature)
.sourceEncoder(sourceEncoder)
.decoder(frameResourceDecoder)
.transform(transformation)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(next);
}
public Bitmap getCurrentFrame() {
return current != null ? current.resource : null;
}
public void clear() {
if (current != null) {
mainHandler.removeCallbacks(current);
Glide.clear(current);
current = null;
}
if (next != null) {
mainHandler.removeCallbacks(next);
Glide.clear(next);
next = null;
}
decoder.resetFrameIndex();
}
class DelayTarget extends SimpleTarget<Bitmap> implements Runnable {
private final FrameCallback cb;
private final long targetTime;
private Bitmap resource;
private int index;
public DelayTarget(FrameCallback cb, long targetTime) {
super(targetWidth, targetHeight);
this.cb = cb;
this.targetTime = targetTime;
}
public void setFrameIndex(int index) {
this.index = index;
}
@Override
public void onResourceReady(final Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
this.resource = resource;
mainHandler.postAtTime(this, targetTime);
}
@Override
public void run() {
cb.onFrameRead(index);
if (current != null) {
Glide.clear(current);
}
current = this;
}
@Override
public void onLoadCleared(Drawable placeholder) {
resource = null;
}
}
private static class FrameSignature implements Key {
private final UUID uuid;
private int id;
public FrameSignature() {
this.uuid = UUID.randomUUID();
}
public void increment() {
id++;
}
@Override
public boolean equals(Object o) {
if (o instanceof FrameSignature) {
FrameSignature other = (FrameSignature) o;
return other.uuid.equals(uuid) && id == other.id;
}
return false;
}
@Override
public int hashCode() {
int result = uuid.hashCode();
result = 31 * result + id;
return result;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) throws UnsupportedEncodingException {
throw new UnsupportedOperationException("Not implemented");
}
}
}