1ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddpackage com.bumptech.glide.load.resource.gif;
2ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
3fcca8d79a3a04e71924d06799de2af46c923a84eSam Juddimport android.annotation.TargetApi;
4a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Juddimport android.content.Context;
5a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Juddimport android.content.res.Resources;
6ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddimport android.graphics.Bitmap;
7ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddimport android.graphics.Canvas;
8ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddimport android.graphics.ColorFilter;
9ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddimport android.graphics.Paint;
10ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddimport android.graphics.PixelFormat;
1138ced22f14acd14b69bb102605505d1feb04b46bSam Juddimport android.graphics.Rect;
12ea01ddd754126290fc18c3dc8f6095ce0216768aSam Juddimport android.graphics.drawable.Drawable;
13fcca8d79a3a04e71924d06799de2af46c923a84eSam Juddimport android.os.Build;
1438ced22f14acd14b69bb102605505d1feb04b46bSam Juddimport android.view.Gravity;
15f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Judd
160f49c87b2f26f3e086f021461b7e5409a7d42be0Sam Juddimport com.bumptech.glide.gifdecoder.GifDecoder;
17a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Juddimport com.bumptech.glide.gifdecoder.GifHeader;
18a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Juddimport com.bumptech.glide.load.Transformation;
194185efc3779f8caf2dd8103aa84705e7a88f679cSam Juddimport com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
20193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Juddimport com.bumptech.glide.load.resource.drawable.GlideDrawable;
21ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
225f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd/**
235f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * An animated {@link android.graphics.drawable.Drawable} that plays the frames of an animated GIF.
245f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */
25193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Juddpublic class GifDrawable extends GlideDrawable implements GifFrameManager.FrameCallback {
26a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    private final Paint paint = new Paint();
2738ced22f14acd14b69bb102605505d1feb04b46bSam Judd    private final Rect destRect = new Rect();
28da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private final GifFrameManager frameManager;
29da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private final GifState state;
30da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private final GifDecoder decoder;
31da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd
32193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    /** True if the drawable is currently animating. */
33da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private boolean isRunning;
34193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    /** True if the drawable should animate while visible. */
35da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private boolean isStarted;
36193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    /** True if the drawable's resources have been recycled. */
374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    private boolean isRecycled;
38a9d87fb5e13a86c9ba52d400815566c020e1d3d5Sam Judd    /**
39a9d87fb5e13a86c9ba52d400815566c020e1d3d5Sam Judd     * True if the drawable is currently visible. Default to true because on certain platforms (at least 4.1.1),
40a9d87fb5e13a86c9ba52d400815566c020e1d3d5Sam Judd     * setVisible is not called on {@link android.graphics.drawable.Drawable Drawables} during
41a9d87fb5e13a86c9ba52d400815566c020e1d3d5Sam Judd     * {@link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}. See issue #130.
42a9d87fb5e13a86c9ba52d400815566c020e1d3d5Sam Judd     */
43a9d87fb5e13a86c9ba52d400815566c020e1d3d5Sam Judd    private boolean isVisible = true;
44193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    /** The number of times we've looped over all the frames in the gif. */
45193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    private int loopCount;
46193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    /** The number of times to loop through the gif animation. */
47193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    private int maxLoopCount = LOOP_FOREVER;
48ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
4938ced22f14acd14b69bb102605505d1feb04b46bSam Judd    private boolean applyGravity;
5038ced22f14acd14b69bb102605505d1feb04b46bSam Judd
5121c2822985058df655ee4874798297f74c5e367eSam Judd    /**
5221c2822985058df655ee4874798297f74c5e367eSam Judd     * Constructor for GifDrawable.
5321c2822985058df655ee4874798297f74c5e367eSam Judd     *
544185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd     * @see #setFrameTransformation(com.bumptech.glide.load.Transformation, android.graphics.Bitmap)
5521c2822985058df655ee4874798297f74c5e367eSam Judd     *
5621c2822985058df655ee4874798297f74c5e367eSam Judd     * @param context A context.
5721c2822985058df655ee4874798297f74c5e367eSam Judd     * @param bitmapProvider An {@link com.bumptech.glide.gifdecoder.GifDecoder.BitmapProvider} that can be used to
5821c2822985058df655ee4874798297f74c5e367eSam Judd     *                       retrieve re-usable {@link android.graphics.Bitmap}s.
594185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd     * @param bitmapPool A {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} that can be used to return
604185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd     *                   the first frame when this drawable is recycled.
6121c2822985058df655ee4874798297f74c5e367eSam Judd     * @param frameTransformation An {@link com.bumptech.glide.load.Transformation} that can be applied to each frame.
6221c2822985058df655ee4874798297f74c5e367eSam Judd     * @param targetFrameWidth The desired width of the frames displayed by this drawable (the width of the view or
6321c2822985058df655ee4874798297f74c5e367eSam Judd     *                         {@link com.bumptech.glide.request.target.Target} this drawable is being loaded into).
6421c2822985058df655ee4874798297f74c5e367eSam Judd     * @param targetFrameHeight The desired height of the frames displayed by this drawable (the height of the view or
6521c2822985058df655ee4874798297f74c5e367eSam Judd     *                          {@link com.bumptech.glide.request.target.Target} this drawable is being loaded into).
6621c2822985058df655ee4874798297f74c5e367eSam Judd     * @param gifHeader The header data for this gif.
6721c2822985058df655ee4874798297f74c5e367eSam Judd     * @param data The full bytes of the gif.
684185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd     * @param firstFrame The decoded and transformed first frame of this gif.
6921c2822985058df655ee4874798297f74c5e367eSam Judd     */
704185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd    public GifDrawable(Context context, GifDecoder.BitmapProvider bitmapProvider, BitmapPool bitmapPool,
71b4cc7fa5f667e4a56514823cf2ad85d7435b498aSam Judd            Transformation<Bitmap> frameTransformation, int targetFrameWidth, int targetFrameHeight,
724185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd            GifHeader gifHeader, byte[] data, Bitmap firstFrame) {
73b4cc7fa5f667e4a56514823cf2ad85d7435b498aSam Judd        this(new GifState(gifHeader, data, context, frameTransformation, targetFrameWidth, targetFrameHeight,
744185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd                bitmapProvider, bitmapPool, firstFrame));
75a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    }
76a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
775ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    GifDrawable(GifState state) {
7872e189cc27d62677a7e45dc5ddfcf4cc0d6466abSam Judd        if (state == null) {
7972e189cc27d62677a7e45dc5ddfcf4cc0d6466abSam Judd            throw new NullPointerException("GifState must not be null");
8072e189cc27d62677a7e45dc5ddfcf4cc0d6466abSam Judd        }
8172e189cc27d62677a7e45dc5ddfcf4cc0d6466abSam Judd
82a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        this.state = state;
83a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        this.decoder = new GifDecoder(state.bitmapProvider);
84b4cc7fa5f667e4a56514823cf2ad85d7435b498aSam Judd        decoder.setData(state.gifHeader, state.data);
85b4762ebd63a4da3efd214b91cf45909a9471a3e7Sam Judd        frameManager = new GifFrameManager(state.context, decoder, state.targetWidth, state.targetHeight);
86b4762ebd63a4da3efd214b91cf45909a9471a3e7Sam Judd        frameManager.setFrameTransformation(state.frameTransformation);
87a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    }
88a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
894185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd    // Visible for testing.
904185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd    GifDrawable(GifDecoder decoder, GifFrameManager frameManager, Bitmap firstFrame, BitmapPool bitmapPool) {
91ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        this.decoder = decoder;
92ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        this.frameManager = frameManager;
93a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        this.state = new GifState(null);
944185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        state.bitmapPool = bitmapPool;
954185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        state.firstFrame = firstFrame;
964185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd    }
974185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd
984185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd    public Bitmap getFirstFrame() {
994185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        return state.firstFrame;
100ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
101ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
1024185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd    public void setFrameTransformation(Transformation<Bitmap> frameTransformation, Bitmap firstFrame) {
103a61f3e9b2f21355cf8a0d3cdb0da15f19369a5ebSam Judd        if (firstFrame == null) {
104a61f3e9b2f21355cf8a0d3cdb0da15f19369a5ebSam Judd            throw new NullPointerException("The first frame of the GIF must not be null");
105a61f3e9b2f21355cf8a0d3cdb0da15f19369a5ebSam Judd        }
106b4762ebd63a4da3efd214b91cf45909a9471a3e7Sam Judd        if (frameTransformation == null) {
107b4762ebd63a4da3efd214b91cf45909a9471a3e7Sam Judd            throw new NullPointerException("The frame transformation must not be null");
108b4762ebd63a4da3efd214b91cf45909a9471a3e7Sam Judd        }
10921c2822985058df655ee4874798297f74c5e367eSam Judd        state.frameTransformation = frameTransformation;
1104185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        state.firstFrame = firstFrame;
111b4762ebd63a4da3efd214b91cf45909a9471a3e7Sam Judd        frameManager.setFrameTransformation(frameTransformation);
11221c2822985058df655ee4874798297f74c5e367eSam Judd    }
11321c2822985058df655ee4874798297f74c5e367eSam Judd
114f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd    public GifDecoder getDecoder() {
115f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd        return decoder;
116f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd    }
117f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd
11821c2822985058df655ee4874798297f74c5e367eSam Judd    public Transformation<Bitmap> getFrameTransformation() {
11921c2822985058df655ee4874798297f74c5e367eSam Judd        return state.frameTransformation;
12021c2822985058df655ee4874798297f74c5e367eSam Judd    }
12121c2822985058df655ee4874798297f74c5e367eSam Judd
12221c2822985058df655ee4874798297f74c5e367eSam Judd    public byte[] getData() {
12321c2822985058df655ee4874798297f74c5e367eSam Judd        return state.data;
12421c2822985058df655ee4874798297f74c5e367eSam Judd    }
12521c2822985058df655ee4874798297f74c5e367eSam Judd
126b4ed2bbf3fbe3c5bccc57fa253ee8abd13b38549Sam Judd    public int getFrameCount() {
127b4ed2bbf3fbe3c5bccc57fa253ee8abd13b38549Sam Judd        return decoder.getFrameCount();
128b4ed2bbf3fbe3c5bccc57fa253ee8abd13b38549Sam Judd    }
129b4ed2bbf3fbe3c5bccc57fa253ee8abd13b38549Sam Judd
130193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    private void resetLoopCount() {
131193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        loopCount = 0;
132193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    }
133193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd
134ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
135ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public void start() {
136da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        isStarted = true;
137193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        resetLoopCount();
138da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        if (isVisible) {
139da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd            startRunning();
140da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        }
141da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    }
142da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd
143da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    @Override
144da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    public void stop() {
145da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        isStarted = false;
146da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        stopRunning();
1476958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd
1486958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        // On APIs > honeycomb we know our drawable is not being displayed anymore when it's callback is cleared and so
1496958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        // we can use the absence of a callback as an indication that it's ok to clear our temporary data. Prior to
1506958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        // honeycomb we can't tell if our callback is null and instead eagerly reset to avoid holding on to resources we
1516958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        // no longer need.
1526958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
1536958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd            reset();
1546958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        }
1556958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd    }
1566958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd
1576958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd    /**
1586958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd     * Clears temporary data and resets the drawable back to the first frame.
1596958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd     */
1606958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd    private void reset() {
1616958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        frameManager.clear();
1626958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        invalidateSelf();
163da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    }
164da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd
165da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private void startRunning() {
166a128f6fbc301213074e0e3eaaf7c92f71c5da901Sam Judd        // If we have only a single frame, we don't want to decode it endlessly.
167a128f6fbc301213074e0e3eaaf7c92f71c5da901Sam Judd        if (decoder.getFrameCount() == 1) {
168a128f6fbc301213074e0e3eaaf7c92f71c5da901Sam Judd            invalidateSelf();
169a128f6fbc301213074e0e3eaaf7c92f71c5da901Sam Judd        }  else if (!isRunning) {
170ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd            isRunning = true;
1718e9362526ca1e73364de269ae09c75c98d0e23f5Sam Judd            frameManager.getNextFrame(this);
172ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd            invalidateSelf();
173ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        }
174ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
175ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
176da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    private void stopRunning() {
177da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        isRunning = false;
178da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd    }
179da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd
180ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
181ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public boolean setVisible(boolean visible, boolean restart) {
182da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        isVisible = visible;
183ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        if (!visible) {
184da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd            stopRunning();
185da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd        } else if (isStarted) {
186da4b3dbb786c0f791496e8701bb2e4fc9dbe3536Sam Judd            startRunning();
187ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        }
188ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        return super.setVisible(visible, restart);
189ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
190ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
191ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
192ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public int getIntrinsicWidth() {
1934185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        return state.firstFrame.getWidth();
194ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
195ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
196ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
197ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public int getIntrinsicHeight() {
1984185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        return state.firstFrame.getHeight();
199ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
200ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
201ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
202ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public boolean isRunning() {
203ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        return isRunning;
204ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
205ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
206ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    // For testing.
207ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    void setIsRunning(boolean isRunning) {
208ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        this.isRunning = isRunning;
209ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
210ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
211ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
21238ced22f14acd14b69bb102605505d1feb04b46bSam Judd    protected void onBoundsChange(Rect bounds) {
21338ced22f14acd14b69bb102605505d1feb04b46bSam Judd        super.onBoundsChange(bounds);
21438ced22f14acd14b69bb102605505d1feb04b46bSam Judd        applyGravity = true;
21538ced22f14acd14b69bb102605505d1feb04b46bSam Judd    }
21638ced22f14acd14b69bb102605505d1feb04b46bSam Judd
21738ced22f14acd14b69bb102605505d1feb04b46bSam Judd    @Override
218ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public void draw(Canvas canvas) {
2191579932986cc7438b103c811adeb0a14feab0adaSam Judd        if (isRecycled) {
2201579932986cc7438b103c811adeb0a14feab0adaSam Judd            return;
2211579932986cc7438b103c811adeb0a14feab0adaSam Judd        }
22238ced22f14acd14b69bb102605505d1feb04b46bSam Judd
22338ced22f14acd14b69bb102605505d1feb04b46bSam Judd        if (applyGravity) {
22438ced22f14acd14b69bb102605505d1feb04b46bSam Judd            Gravity.apply(GifState.GRAVITY, getIntrinsicWidth(), getIntrinsicHeight(), getBounds(), destRect);
22538ced22f14acd14b69bb102605505d1feb04b46bSam Judd            applyGravity = false;
22638ced22f14acd14b69bb102605505d1feb04b46bSam Judd        }
22738ced22f14acd14b69bb102605505d1feb04b46bSam Judd
2281579932986cc7438b103c811adeb0a14feab0adaSam Judd        Bitmap currentFrame = frameManager.getCurrentFrame();
2294185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        Bitmap toDraw = currentFrame != null ? currentFrame : state.firstFrame;
23038ced22f14acd14b69bb102605505d1feb04b46bSam Judd        canvas.drawBitmap(toDraw, null, destRect, paint);
231ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
232ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
233ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
234ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public void setAlpha(int i) {
235ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        paint.setAlpha(i);
236ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
237ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
238ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
239ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public void setColorFilter(ColorFilter colorFilter) {
240ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        paint.setColorFilter(colorFilter);
241ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
242ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
243ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
244ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public int getOpacity() {
2452c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd        // We can't tell, so default to transparent to be safe.
2462c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd        return PixelFormat.TRANSPARENT;
247ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
248ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
249e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
250ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    @Override
2511579932986cc7438b103c811adeb0a14feab0adaSam Judd    public void onFrameRead(int frameIndex) {
2526958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && getCallback() == null) {
2530d8a954b91ef8a37c944c2fdee75966c6f94d7bbSam Judd            stop();
2546958330c93825bbfb70aea1aff9aeed76b59af4eSam Judd            reset();
2550d8a954b91ef8a37c944c2fdee75966c6f94d7bbSam Judd            return;
256e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp        }
257e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp        if (!isRunning) {
258ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd            return;
259ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        }
2600d8a954b91ef8a37c944c2fdee75966c6f94d7bbSam Judd
2611579932986cc7438b103c811adeb0a14feab0adaSam Judd        invalidateSelf();
262ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
263193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        if (frameIndex == decoder.getFrameCount() - 1) {
264193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd            loopCount++;
265193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        }
266193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd
267193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        if (maxLoopCount != LOOP_FOREVER && loopCount >= maxLoopCount) {
268193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd            stop();
269193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        } else {
270193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd            frameManager.getNextFrame(this);
271193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        }
272ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
273ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd
274a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    @Override
275a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    public ConstantState getConstantState() {
276a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        return state;
277a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    }
278a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
2795f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd    /**
2805f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd     * Clears any resources for loading frames that are currently held on to by this object.
2815f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd     */
282ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    public void recycle() {
2834f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        isRecycled = true;
2844185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        state.bitmapPool.put(state.firstFrame);
285ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd        frameManager.clear();
286ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd    }
2874f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2883e519104d688b295f6b1b46d1cc78695a36654a7Sam Judd    // For testing.
2895f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd    boolean isRecycled() {
2904f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return isRecycled;
2914f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
292193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd
293193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    @Override
294193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    public boolean isAnimated() {
295193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        return true;
296193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    }
297193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd
298193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    @Override
299193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    public void setLoopCount(int loopCount) {
300193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        if (loopCount <= 0 && loopCount != LOOP_FOREVER && loopCount != LOOP_INTRINSIC) {
301193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd            throw new IllegalArgumentException("Loop count must be greater than 0, or equal to "
302193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd                    + "GlideDrawable.LOOP_FOREVER, or equal to GlideDrawable.LOOP_INTRINSIC");
303193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        }
304193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd
305193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        if (loopCount == LOOP_INTRINSIC) {
306193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd            maxLoopCount = decoder.getLoopCount();
307193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        } else {
308193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd            maxLoopCount = loopCount;
309193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd        }
310193e2f8f9d0c56ce4faa7cfe4e62ca6187be90b8Sam Judd    }
311a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
312a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    static class GifState extends ConstantState {
31338ced22f14acd14b69bb102605505d1feb04b46bSam Judd        private static final int GRAVITY = Gravity.FILL;
314a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        GifHeader gifHeader;
315a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        byte[] data;
316a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        Context context;
317a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        Transformation<Bitmap> frameTransformation;
318a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        int targetWidth;
319a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        int targetHeight;
320a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        GifDecoder.BitmapProvider bitmapProvider;
3214185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        BitmapPool bitmapPool;
3224185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd        Bitmap firstFrame;
323a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
324b4cc7fa5f667e4a56514823cf2ad85d7435b498aSam Judd        public GifState(GifHeader header, byte[] data, Context context,
325a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                Transformation<Bitmap> frameTransformation, int targetWidth, int targetHeight,
3264185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd                GifDecoder.BitmapProvider provider, BitmapPool bitmapPool, Bitmap firstFrame) {
327a61f3e9b2f21355cf8a0d3cdb0da15f19369a5ebSam Judd            if (firstFrame == null) {
328a61f3e9b2f21355cf8a0d3cdb0da15f19369a5ebSam Judd                throw new NullPointerException("The first frame of the GIF must not be null");
329a61f3e9b2f21355cf8a0d3cdb0da15f19369a5ebSam Judd            }
330a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            gifHeader = header;
331a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            this.data = data;
3324185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd            this.bitmapPool = bitmapPool;
3334185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd            this.firstFrame = firstFrame;
334a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            this.context = context.getApplicationContext();
335a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            this.frameTransformation = frameTransformation;
336a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            this.targetWidth = targetWidth;
337a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            this.targetHeight = targetHeight;
338a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            bitmapProvider = provider;
339a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        }
340a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
341a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        public GifState(GifState original) {
342a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            if (original != null) {
343a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                gifHeader = original.gifHeader;
344a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                data = original.data;
345a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                context = original.context;
346a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                frameTransformation = original.frameTransformation;
347a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                targetWidth = original.targetWidth;
348a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                targetHeight = original.targetHeight;
349a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd                bitmapProvider = original.bitmapProvider;
3504185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd                bitmapPool = original.bitmapPool;
3514185efc3779f8caf2dd8103aa84705e7a88f679cSam Judd                firstFrame = original.firstFrame;
352a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            }
353a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        }
354a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
355a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        @Override
356a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        public Drawable newDrawable(Resources res) {
35721c2822985058df655ee4874798297f74c5e367eSam Judd            return newDrawable();
358a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        }
359a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
360a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        @Override
361a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        public Drawable newDrawable() {
362a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            return new GifDrawable(this);
363a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        }
364a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd
365a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        @Override
366a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        public int getChangingConfigurations() {
367a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd            return 0;
368a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd        }
369a23b64f5872138b0a5fa4c95be5dcf3f6f0942d8Sam Judd    }
370ea01ddd754126290fc18c3dc8f6095ce0216768aSam Judd}
371