提交 193e2f8f 编写于 作者: S Sam Judd

Make GifDrawable implement GlideDrawable.

上级 3e519104
......@@ -8,6 +8,13 @@ import android.graphics.drawable.Drawable;
* that contain an animation.
*/
public abstract class GlideDrawable extends Drawable implements Animatable {
/** A constant indicating that an animated drawable should loop continuously. */
public static final int LOOP_FOREVER = -1;
/**
* A constant indicating that an animated drawable should loop for its default number of times. For animated GIFs,
* this constant indicates the GIF should use the netscape loop count if present.
*/
public static final int LOOP_INTRINSIC = 0;
/**
* Returns {@code true} if this drawable is animated.
......
......@@ -8,34 +8,37 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.gifdecoder.GifHeader;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
/**
* An animated {@link android.graphics.drawable.Drawable} that plays the frames of an animated GIF.
*/
public class GifDrawable extends Drawable implements Animatable, GifFrameManager.FrameCallback {
public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameCallback {
private final Paint paint = new Paint();
private final GifFrameManager frameManager;
private final GifState state;
private final GifDecoder decoder;
/** The current frame to draw, or null if no frame has been loaded yet */
/** The current frame to draw, or null if no frame has been loaded yet. */
private Bitmap currentFrame;
/** True if the drawable is currently animating */
/** True if the drawable is currently animating. */
private boolean isRunning;
/** True if the drawable should animate while visible */
/** True if the drawable should animate while visible. */
private boolean isStarted;
/** True if the drawable's resources have been recycled */
/** True if the drawable's resources have been recycled. */
private boolean isRecycled;
/** True if the drawable is currently visible. */
private boolean isVisible;
/** The number of times we've looped over all the frames in the gif. */
private int loopCount;
/** The number of times to loop through the gif animation. */
private int maxLoopCount = LOOP_FOREVER;
/**
* Constructor for GifDrawable.
......@@ -96,9 +99,14 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
return state.data;
}
private void resetLoopCount() {
loopCount = 0;
}
@Override
public void start() {
isStarted = true;
resetLoopCount();
if (isVisible) {
startRunning();
}
......@@ -177,7 +185,7 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
@TargetApi(11)
@Override
public void onFrameRead(Bitmap frame) {
public void onFrameRead(Bitmap frame, int frameIndex) {
if (Build.VERSION.SDK_INT >= 11 && getCallback() == null) {
stop();
return;
......@@ -190,7 +198,15 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
invalidateSelf();
}
frameManager.getNextFrame(this);
if (frameIndex == decoder.getFrameCount() - 1) {
loopCount++;
}
if (maxLoopCount != LOOP_FOREVER && loopCount >= maxLoopCount) {
stop();
} else {
frameManager.getNextFrame(this);
}
}
@Override
......@@ -211,6 +227,25 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
return isRecycled;
}
@Override
public boolean isAnimated() {
return true;
}
@Override
public void setLoopCount(int loopCount) {
if (loopCount <= 0 && loopCount != LOOP_FOREVER && loopCount != LOOP_INTRINSIC) {
throw new IllegalArgumentException("Loop count must be greater than 0, or equal to "
+ "GlideDrawable.LOOP_FOREVER, or equal to GlideDrawable.LOOP_INTRINSIC");
}
if (loopCount == LOOP_INTRINSIC) {
maxLoopCount = decoder.getLoopCount();
} else {
maxLoopCount = loopCount;
}
}
static class GifState extends ConstantState {
String id;
GifHeader gifHeader;
......
......@@ -46,7 +46,7 @@ class GifFrameManager {
private DelayTarget next;
public interface FrameCallback {
public void onFrameRead(Bitmap frame);
public void onFrameRead(Bitmap frame, int index);
}
public GifFrameManager(Context context, GifDecoder decoder, Transformation<Bitmap> transformation, int targetWidth,
......@@ -107,6 +107,7 @@ class GifFrameManager {
long targetTime = SystemClock.uptimeMillis() + (Math.max(MIN_FRAME_DELAY, decoder.getNextDelay()));
next = new DelayTarget(cb, targetTime);
next.setFrameIndex(decoder.getCurrentFrameIndex());
Glide.with(context)
.using(frameLoader, GifDecoder.class)
......@@ -137,6 +138,7 @@ class GifFrameManager {
private FrameCallback cb;
private long targetTime;
private Bitmap resource;
private int index;
public DelayTarget(FrameCallback cb, long targetTime) {
super(targetWidth, targetHeight);
......@@ -144,6 +146,10 @@ class GifFrameManager {
this.targetTime = targetTime;
}
public void setFrameIndex(int index) {
this.index = index;
}
@Override
public void onResourceReady(final Bitmap resource, GlideAnimation<Bitmap> glideAnimation) {
this.resource = resource;
......@@ -152,7 +158,7 @@ class GifFrameManager {
@Override
public void run() {
cb.onFrameRead(resource);
cb.onFrameRead(resource, index);
if (current != null) {
Glide.clear(current);
}
......
......@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.gifdecoder.GifHeader;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.tests.GlideShadowLooper;
import org.junit.Before;
......@@ -100,7 +101,7 @@ public class GifDrawableTest {
@Test
public void testStartsLoadingNextFrameWhenCurrentFinishes() {
drawable.setIsRunning(true);
drawable.onFrameRead(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
drawable.onFrameRead(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888), 0);
verify(frameManager).getNextFrame(eq(drawable));
}
......@@ -108,7 +109,7 @@ public class GifDrawableTest {
@Test
public void testInvalidatesSelfWhenFrameReady() {
drawable.setIsRunning(true);
drawable.onFrameRead(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565));
drawable.onFrameRead(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565), 0);
verify(cb).invalidateDrawable(eq(drawable));
}
......@@ -116,7 +117,7 @@ public class GifDrawableTest {
@Test
public void testDoesNotStartLoadingNextFrameWhenCurrentFinishesIfNotRunning() {
drawable.setIsRunning(false);
drawable.onFrameRead(Bitmap.createBitmap(10, 100, Bitmap.Config.ARGB_8888));
drawable.onFrameRead(Bitmap.createBitmap(10, 100, Bitmap.Config.ARGB_8888), 0);
verify(frameManager, never()).getNextFrame(eq(drawable));
}
......@@ -125,7 +126,7 @@ public class GifDrawableTest {
public void testDoesNotStartLoadingNextFrameWhenCurrentFinishesIfHasNoCallback() {
drawable.setIsRunning(true);
drawable.setCallback(null);
drawable.onFrameRead(Bitmap.createBitmap(1, 2, Bitmap.Config.ARGB_8888));
drawable.onFrameRead(Bitmap.createBitmap(1, 2, Bitmap.Config.ARGB_8888), 0);
verify(frameManager, never()).getNextFrame(eq(drawable));
}
......@@ -134,7 +135,7 @@ public class GifDrawableTest {
public void testStopsWhenCurrentFrameFinishesIfHasNoCallback() {
drawable.setIsRunning(true);
drawable.setCallback(null);
drawable.onFrameRead(Bitmap.createBitmap(2, 1, Bitmap.Config.ARGB_8888));
drawable.onFrameRead(Bitmap.createBitmap(2, 1, Bitmap.Config.ARGB_8888), 0);
assertFalse(drawable.isRunning());
}
......@@ -244,4 +245,125 @@ public class GifDrawableTest {
assertEquals(frameWidth, drawable.getIntrinsicWidth());
assertEquals(frameHeight, drawable.getIntrinsicHeight());
}
@Test
public void testIsAnimated() {
assertTrue(drawable.isAnimated());
}
@Test
public void testLoopsASingleTimeIfLoopCountIsSetToOne() {
when(gifDecoder.getFrameCount()).thenReturn(1);
drawable.setLoopCount(1);
drawable.setVisible(true, true);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
verify(frameManager, times(1)).getNextFrame(eq(drawable));
assertFalse(drawable.isRunning());
}
@Test
public void testLoopsForeverIfLoopCountIsSetToLoopForever() {
when(gifDecoder.getFrameCount()).thenReturn(1);
drawable.setLoopCount(GifDrawable.LOOP_FOREVER);
drawable.setVisible(true, true);
drawable.start();
int loops = 40;
for (int i = 0; i < 40; i++) {
drawable.onFrameRead(getBitmap(), 0);
}
verify(frameManager, times(loops + 1)).getNextFrame(eq(drawable));
}
@Test
public void testLoopsOnceIfLoopCountIsSetToOneWithThreeFrames() {
when(gifDecoder.getFrameCount()).thenReturn(3);
drawable.setLoopCount(1);
drawable.setVisible(true, true);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 1);
drawable.onFrameRead(getBitmap(), 2);
verify(frameManager, times(3)).getNextFrame(eq(drawable));
assertFalse(drawable.isRunning());
}
@Test
public void testLoopsThreeTimesIfLoopCountIsSetToThree() {
when(gifDecoder.getFrameCount()).thenReturn(1);
drawable.setLoopCount(3);
drawable.setVisible(true, true);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 0);
verify(frameManager, times(3)).getNextFrame(eq(drawable));
assertFalse(drawable.isRunning());
}
@Test
public void testCallingStartResetsLoopCounter() {
when(gifDecoder.getFrameCount()).thenReturn(2);
drawable.setLoopCount(1);
drawable.setVisible(true, true);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 1);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 1);
verify(frameManager, times(4)).getNextFrame(eq(drawable));
assertFalse(drawable.isRunning());
}
@Test
public void testChangingTheLoopCountAfterHittingTheMaxLoopCount() {
when(gifDecoder.getFrameCount()).thenReturn(1);
drawable.setLoopCount(1);
drawable.setVisible(true, true);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.setLoopCount(2);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 0);
verify(frameManager, times(3)).getNextFrame(eq(drawable));
}
@Test(expected = IllegalArgumentException.class)
public void testThrowsIfGivenLoopCountLessThanZeroAndNotInfinite() {
drawable.setLoopCount(-2);
}
@Test
public void testUsesDecoderNetscapeLoopCountIfLoopCountIsLoopIntrinsic() {
when(gifDecoder.getLoopCount()).thenReturn(2);
when(gifDecoder.getFrameCount()).thenReturn(1);
drawable.setLoopCount(GlideDrawable.LOOP_INTRINSIC);
drawable.setVisible(true, true);
drawable.start();
drawable.onFrameRead(getBitmap(), 0);
drawable.onFrameRead(getBitmap(), 0);
verify(frameManager, times(2)).getNextFrame(eq(drawable));
}
private static Bitmap getBitmap() {
return Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册