Ahab's Studio.

Glide 源码分析 - 展示 gif 原理

字数统计: 914阅读时长: 4 min
2019/02/17 Share

在展示 gif 时,即使不调用 asGif 方法,Glide 也能识别出 gif 类型并正常展示。解码逻辑位于 Downsampler 的 decode 方法中,我们先从这里开始,看看 Glide 是如何识别 gif 类型的。decode 方法中调用了 decodeFromWrappedStreams 方法开始实际解码逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
long startTime = LogTime.getLogTime();

int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
int sourceWidth = sourceDimensions[0];
int sourceHeight = sourceDimensions[1];
String sourceMimeType = options.outMimeType;

// If we failed to obtain the image dimensions, we may end up with an incorrectly sized Bitmap,
// so we want to use a mutable Bitmap type. One way this can happen is if the image header is so
// large (10mb+) that our attempt to use inJustDecodeBounds fails and we're forced to decode the
// full size image.
if (sourceWidth == -1 || sourceHeight == -1) {
isHardwareConfigAllowed = false;
}

int orientation = ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool);
int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
boolean isExifOrientationRequired = TransformationUtils.isExifOrientationRequired(orientation);

int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;

ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);

// ... 省略下面的解码逻辑代码

return rotated;
}

可以看到解码开始前,准备了图片角度、输出尺寸的信息,并通过 ImageHeaderParserUtils 获取了图片类型:

1
ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);

Glide 中定义了以下图片类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* The format of the image data including whether or not the image may include transparent
* pixels.
*/
enum ImageType {
GIF(true),
JPEG(false),
RAW(false),
/** PNG type with alpha. */
PNG_A(true),
/** PNG type without alpha. */
PNG(false),
/** WebP type with alpha. */
WEBP_A(true),
/** WebP type without alpha. */
WEBP(false),
/** Unrecognized type. */
UNKNOWN(false);

private final boolean hasAlpha;

ImageType(boolean hasAlpha) {
this.hasAlpha = hasAlpha;
}

public boolean hasAlpha() {
return hasAlpha;
}
}

而实际获取图片类型的逻辑位于 DefaultImageHeaderParser 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@NonNull
private ImageType getType(Reader reader) throws IOException {
final int firstTwoBytes = reader.getUInt16();

// JPEG.
if (firstTwoBytes == EXIF_MAGIC_NUMBER) {
return JPEG;
}

final int firstFourBytes = (firstTwoBytes << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
// PNG.
if (firstFourBytes == PNG_HEADER) {
// See: http://stackoverflow.com/questions/2057923/how-to-check-a-png-for-grayscale-alpha
// -color-type
reader.skip(25 - 4);
int alpha = reader.getByte();
// A RGB indexed PNG can also have transparency. Better safe than sorry!
return alpha >= 3 ? PNG_A : PNG;
}

// GIF from first 3 bytes.
if (firstFourBytes >> 8 == GIF_HEADER) {
return GIF;
}

// WebP (reads up to 21 bytes). See https://developers.google.com/speed/webp/docs/riff_container
// for details.
if (firstFourBytes != RIFF_HEADER) {
return UNKNOWN;
}
// Bytes 4 - 7 contain length information. Skip these.
reader.skip(4);
final int thirdFourBytes =
(reader.getUInt16() << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
if (thirdFourBytes != WEBP_HEADER) {
return UNKNOWN;
}
final int fourthFourBytes =
(reader.getUInt16() << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
if ((fourthFourBytes & VP8_HEADER_MASK) != VP8_HEADER) {
return UNKNOWN;
}
if ((fourthFourBytes & VP8_HEADER_TYPE_MASK) == VP8_HEADER_TYPE_EXTENDED) {
// Skip some more length bytes and check for transparency/alpha flag.
reader.skip(4);
return (reader.getByte() & WEBP_EXTENDED_ALPHA_FLAG) != 0 ? ImageType.WEBP_A : ImageType.WEBP;
}
if ((fourthFourBytes & VP8_HEADER_TYPE_MASK) == VP8_HEADER_TYPE_LOSSLESS) {
// See chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
// for more info.
reader.skip(4);
return (reader.getByte() & WEBP_LOSSLESS_ALPHA_FLAG) != 0 ? ImageType.WEBP_A : ImageType.WEBP;
}
return ImageType.WEBP;
}

可以看到这里是通过文件头标示来获取图片类型的,而 gif 文件头如下:

查看代码,Glide 中便是通过 0x474946 文件头来判断的。

Glide 中将 gif 类型图片封装成了 GifDrawable,在 ByteBufferGifDecoder 中可以看到 GifDrawable 的生成逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Nullable
private GifDrawableResource decode(
ByteBuffer byteBuffer, int width, int height, GifHeaderParser parser, Options options) {
long startTime = LogTime.getLogTime();
try {
final GifHeader header = parser.parseHeader();
if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) {
// If we couldn't decode the GIF, we will end up with a frame count of 0.
return null;
}

Bitmap.Config config = options.get(GifOptions.DECODE_FORMAT) == DecodeFormat.PREFER_RGB_565
? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

int sampleSize = getSampleSize(header, width, height);
GifDecoder gifDecoder = gifDecoderFactory.build(provider, header, byteBuffer, sampleSize);
gifDecoder.setDefaultBitmapConfig(config);
gifDecoder.advance();
Bitmap firstFrame = gifDecoder.getNextFrame();
if (firstFrame == null) {
return null;
}

Transformation<Bitmap> unitTransformation = UnitTransformation.get();

GifDrawable gifDrawable =
new GifDrawable(context, gifDecoder, unitTransformation, width, height, firstFrame);

return new GifDrawableResource(gifDrawable);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Decoded GIF from stream in " + LogTime.getElapsedMillis(startTime));
}
}
}

至此已经获取到 GifDrawable ,GifDrawable 中持有一个 GifFrameLoader,而 GifFrameLoader 中持有了 gif 解码器 StandardGifDecoder 。由此可以得出 gif 的展示逻辑就封装于 GifDrawable 中,主要通过 GifFrameLoader 实现。

关注公众号,Get 更多知识点

原文作者:Ahab

原文链接:http://yhaowa.gitee.io/3fe9ef36/

发表日期:February 17th 2019, 6:03:14 pm

更新日期:April 7th 2020, 11:34:42 pm

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG