Record a GIF Problem Investigation and Exploration
One day, we received feedback from the operator that a GIF
banner could not be played on the website, and it was normal on the APP side. The operator indicated that the last configuration of GIF
banner was normal, and gave the picture links of the two configurations.
Analysis
From the link address of the image, the two banner images are all suffixed with jpg, but the GIF judgment is not determined by its suffix, so let’s first look at the headers of the two images:
As we can see, although the image suffix is jpg
, both figures are typical GIF
diagrams. The Header
part of GIF
is most commonly known as 47 49 46 38 38 61
, ie GIF89a
and also GIF87a
. The former is an enhanced version of the latter. The transparent channels and animations we are familiar with are the former. The ability to have it, and another important point, GIF89a
has added Application Extension
, which will be used in later analysis.
Since they are all GIF diagrams, let’s try opening the Console to replace the new image with the web page and find that it is not impossible to play the image, but the animation is short and only played once, so it is mistaken for no animation at all.
If you have experience in editing GIF diagrams, you must know that you can select the number of GIF loops when exporting, but since my computer does not have Photoshop installed, I still check it with the old method:
NETSCAPE
is the famous Netscape company. The most well-known one is probably the story that its Netscape browser was defeated by IE
in the browser war of the year. In the 1990s, Netscape introduced the Netscape Looping Application Extension
, an unofficial implementation of the Application Extension
defined in GIF89a
mentioned above, and is the most common implementation ever passed. It adds a declaration of the number of animation loops, and I refer to a diagram here to introduce its structure:
(Image source see here)
The picture is very clear, so we can see that the loop Count value of the infinite loop image is 0, and the image that only loops once has no associated Block.
At this point, the solution came out, telling the operator to re-export the GIF image, and set the number of loops to “unlimited”. The problem solved.
Explore
However, I am still confused. Why is there no problem on the APP?
Let’s look at the source code.
Here is the explore about our Android Project. Glide is wildly used around the world. We know that Glide supports GIF display by default. When Glide is used for image loading, the basic version can be quickly applied in just a few lines, as shown below.
1 | Glide.with(context) |
Generally speaking, a more appropriate way to look at the source code is to look at the call chain from the externally exposed method. However, the previous part of this article has briefly introduced the structure of GIF
, and we can follow this line of thought to find it.
As you can see from the above introduction, the latest configuration of the GIF
image is missing the Block
describing the number of loops, so it is reasonable to doubt that the Glide
framework does not read the relevant information?
Directly search for the keyword GifDecoder
and find it is an interface
. Observe that there are several related methods as follows.
1 |
|
Then search for its interface implementation, and found that there is only one implementation class StandardGifDecoder
, the method is implemented as follows.
1 |
|
Then look for the assignment of header.loopCount, we will find the following function:
1 | /** |
Then find the location where the function is called, and simply trace back to the slightly outer class, which will be called in the GifHeaderParser class.
1 |
|
There are two main classes that call this method, one of which is the StandardGifDecoder mentioned above.
1 |
|
At this point, we can draw a phased conclusion that StandardGifDecoder
does have the number of loops to get GIF
.
Looking back at the GIF
structure introduced at the beginning of the article, obviously, our initial guess is wrong, Glide
still reads the number of loops in the relevant Block
.
That being the case, why use Glide
on the app to play GIF
infinitely, and guess: Will it get the number of loops, but it doesn’t consume it by default?
We look up the callers of the three functions mentioned above in turn.
1 |
|
After a simple filter, you will find that only the last method will be called in GlideFrameLoader.
1 | int getLoopCount() { |
Continue to trace back and you will find this function called in GifDrawable.
1 | // Public API. |
And this method is not called by default. Continuing to check the class, you will find that the maxLoopCount
variable defaults to LOOP_FOREVER
, which is an infinite loop, and the variable is only reassigned in the method.
At this point, we can basically confirm that Glide
reads the number of loops in the picture information but does not consume it by default. The important code for the number of GIF
loops in Glide
is here. Of course, there are other confirmation steps to be completed from the conclusion, but it is not relevant to the problem described in this article. It is simple here. List it, no longer repeat them.
- The
parseHeader()
function has two classes that call it. Another unmentioned class isByteBufferGifDecoder
, which is also used by theStreamGifDecoder
class, which can be searched for in theGlide
class. - The
handles
method inStreamGifDecoder
contains two logics aboutGIF
, one isGifOptions
, which can be set to prohibitGIF
playback by the method of this class, and the second is to determine whether the file isGIF.
, the default processing logic in theDefaultImageHeaderParser
class, is also handled by determining whether the file header is0x474946
. - For
Glide
how to convert the image address toDrawable
and select the correspondingDecoder
, you can search forGlide
source code analysis, this article will not be expanded.
Usage
After understanding the principle, if we want Glide
to play GIF
, set it several times in the picture to play it a few times, how to modify it?
As you can see from the previous section, the easiest way is to call setLoopCount(int loopCount)
. For example, you can get GifDrawable
through listener
, which can be selected according to the specific scene.
If you use code to describe it, it is like this.
1 | // Method 1 |
More
In most scenarios, we refer to the image in the GIF
format in order to obtain a relatively lightweight animation effect compared to the video. So, in some scenarios, is there any alternative?
Try the SVG Animation
, Lottie
, WebP
, APNG
.