该文章阅读的SDWebImage的版本为4.3.3。
这个类和SDWebImageImageIOCoder
类一样,都是编解码器类,只不过这个类是针对GIF格式的图像。
首先可以看到,这个类只遵守并实现了SDWebImageCoder
协议,说明这个类不支持GIF格式的图像的逐行解码功能。
1.公共方法
/**
获取该类的单例对象
*/
+ (nonnull instancetype)sharedCoder;
复制代码
+ (instancetype)sharedCoder {
static SDWebImageGIFCoder *coder;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
coder = [[SDWebImageGIFCoder alloc] init];
});
return coder;
}
复制代码
2.私有方法
/**
获取动图每一帧的持续时间
*/
- (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
// 创建变量保存持续时间,默认0.1秒
float frameDuration = 0.1f;
// 获取图像源中指定位置的图像属性
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
if (!cfFrameProperties) {
// 如果没有获取到就直接返回默认持续时间
return frameDuration;
}
// 获取图像属性中的GIF属性字典
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
// 从GIF属性字典中获取当帧图片的持续时间
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
if (delayTimeUnclampedProp != nil) {
frameDuration = [delayTimeUnclampedProp floatValue];
} else {
// 如果通过上面那个key没获取到,就通过下面这个key获取当帧图片的持续时间
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
if (delayTimeProp != nil) {
frameDuration = [delayTimeProp floatValue];
}
}
// 如果获取到的持续时间小于11毫秒,就设置成100毫秒
if (frameDuration < 0.011f) {
frameDuration = 0.100f;
}
// 释放属性对象
CFRelease(cfFrameProperties);
// 返回持续时间
return frameDuration;
}
复制代码
3.SDWebImageCoder协议方法实现
- 解码
- (BOOL)canDecodeFromData:(nullable NSData *)data {
// 如果是GIF格式的图片就返回YES
return ([NSData sd_imageFormatForImageData:data] == SDImageFormatGIF);
}
复制代码
- (UIImage *)decodedImageWithData:(NSData *)data {
// 如果没有数据就返回空
if (!data) {
return nil;
}
#if SD_MAC
SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data];
NSImage *animatedImage = [[NSImage alloc] initWithSize:imageRep.size];
[animatedImage addRepresentation:imageRep];
return animatedImage;
#else
// 生成图像源
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
// 如果没生成就返回空
if (!source) {
return nil;
}
// 获取子图片数量
size_t count = CGImageSourceGetCount(source);
// 创建变量保存动图对象
UIImage *animatedImage;
if (count <= 1) {
// 如果子图片数量不超过1张就直接生成图片对象
animatedImage = [[UIImage alloc] initWithData:data];
} else {
// 创建可变数组保存SDWebImageFrame对象
NSMutableArray<SDWebImageFrame *> *frames = [NSMutableArray array];
// 遍历子图片对象,并将其包装成SDWebImageFrame对象
for (size_t i = 0; i < count; i++) {
// 获取指定帧数的相位图
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL);
// 如果没获取到就跳过进入下次循环
if (!imageRef) {
continue;
}
// 获取指定帧数的持续时间
float duration = [self sd_frameDurationAtIndex:i source:source];
// 根据相位图生成图片对象
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
// 释放相位图
CGImageRelease(imageRef);
// 将一帧的信息封装成SDWebImageFrame对象
SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:image duration:duration];
// 将封装好的SDWebImageFrame对象添加到数组中保存
[frames addObject:frame];
}
// 创建变量保存循环次数
NSUInteger loopCount = 1;
// 获取到图像属性
NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(source, nil);
// 获取到GIF相关的图像属性
NSDictionary *gifProperties = [imageProperties valueForKey:(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary];
if (gifProperties) {
// 获取GIF循环次数
NSNumber *gifLoopCount = [gifProperties valueForKey:(__bridge_transfer NSString *)kCGImagePropertyGIFLoopCount];
if (gifLoopCount != nil) {
// 保存循环次数
loopCount = gifLoopCount.unsignedIntegerValue;
}
}
// 利用封装好的SDWebImageFrame对象数组生成动图对象
animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames];
// 设置动图对象的循环次数
animatedImage.sd_imageLoopCount = loopCount;
}
// 释放图像源
CFRelease(source);
// 返回动图对象
return animatedImage;
#endif
}
复制代码
- (UIImage *)decompressedImageWithImage:(UIImage *)image
data:(NSData *__autoreleasing _Nullable *)data
options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict {
// GIF类型图片对象不解压
return image;
}
复制代码
- 编码
- (BOOL)canEncodeToFormat:(SDImageFormat)format {
// 如果是GIF格式的图片就返回YES
return (format == SDImageFormatGIF);
}
复制代码
- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format {
// 如果没有图片对象就返回空
if (!image) {
return nil;
}
// 如果图片类型不是GIF格式就返回空
if (format != SDImageFormatGIF) {
return nil;
}
// 创建变量保存图像数据
NSMutableData *imageData = [NSMutableData data];
// 获取GIF图像格式的CFStringRef格式字符串
CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatGIF];
// 生成图片对象的SDWebImageFrame类型元素的数组
NSArray<SDWebImageFrame *> *frames = [SDWebImageCoderHelper framesFromAnimatedImage:image];
// 创建图像目的地
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frames.count, NULL);
// 如果创建失败就直接返回空
if (!imageDestination) {
return nil;
}
if (frames.count == 0) {
如果是单帧的动图就直接将图片添加到图像目的地中
CGImageDestinationAddImage(imageDestination, image.CGImage, nil);
} else {
// 获取到动图的循环次数
NSUInteger loopCount = image.sd_imageLoopCount;
// 创建一个动图属性字典保存循环次数
NSDictionary *gifProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary: @{(__bridge_transfer NSString *)kCGImagePropertyGIFLoopCount : @(loopCount)}};
// 为图像目的地设置属性
CGImageDestinationSetProperties(imageDestination, (__bridge CFDictionaryRef)gifProperties);
// 遍历每一帧图片
for (size_t i = 0; i < frames.count; i++) {
// 获取SDWebImageFrame对象
SDWebImageFrame *frame = frames[i];
// 获取每一帧的展示时间
float frameDuration = frame.duration;
// 获取每一帧的位图
CGImageRef frameImageRef = frame.image.CGImage;
// 创建一个属性字典保存每一帧的展示时间
NSDictionary *frameProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary : @{(__bridge_transfer NSString *)kCGImagePropertyGIFDelayTime : @(frameDuration)}};
// 将位图和其对应的属性添加到图像目的地中
CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties);
}
}
// 如果编码失败就返回空
if (CGImageDestinationFinalize(imageDestination) == NO) {
imageData = nil;
}
// 释放掉imageDestination对象
CFRelease(imageDestination);
// 返回编码后的数据
return [imageData copy];
}
复制代码
源码阅读系列:SDWebImage
源码阅读:SDWebImage(二)——SDWebImageCompat
源码阅读:SDWebImage(三)——NSData+ImageContentType
源码阅读:SDWebImage(四)——SDWebImageCoder
源码阅读:SDWebImage(五)——SDWebImageFrame
源码阅读:SDWebImage(六)——SDWebImageCoderHelper
源码阅读:SDWebImage(七)——SDWebImageImageIOCoder
源码阅读:SDWebImage(八)——SDWebImageGIFCoder
源码阅读:SDWebImage(九)——SDWebImageCodersManager
源码阅读:SDWebImage(十)——SDImageCacheConfig
源码阅读:SDWebImage(十一)——SDImageCache
源码阅读:SDWebImage(十二)——SDWebImageDownloaderOperation
源码阅读:SDWebImage(十三)——SDWebImageDownloader
源码阅读:SDWebImage(十四)——SDWebImageManager
源码阅读:SDWebImage(十五)——SDWebImagePrefetcher
源码阅读:SDWebImage(十六)——SDWebImageTransition
源码阅读:SDWebImage(十七)——UIView+WebCacheOperation
源码阅读:SDWebImage(十八)——UIView+WebCache
源码阅读:SDWebImage(十九)——UIImage+ForceDecode/UIImage+GIF/UIImage+MultiFormat
源码阅读:SDWebImage(二十)——UIButton+WebCache
源码阅读:SDWebImage(二十一)——UIImageView+WebCache/UIImageView+HighlightedWebCache