该文章阅读的SDWebImage的版本为4.3.3。
这个类提供了四个方法,这四个方法可分为两类,一类是动图处理,一类是图像方向处理。
1.私有函数
先来看一下这个类里的两个函数
/**
这个函数是计算两个整数a和b的最大公约数
*/
static NSUInteger gcd(NSUInteger a, NSUInteger b) {
NSUInteger c;
while (a != 0) {
c = a;
a = b % a;
b = c;
}
return b;
}
复制代码
/**
这个函数是计算一个整数数组的最大公约数
*/
static NSUInteger gcdArray(size_t const count, NSUInteger const * const values) {
if (count == 0) {
return 0;
}
NSUInteger result = values[0];
for (size_t i = 1; i < count; ++i) {
result = gcd(values[i], result);
}
return result;
}
复制代码
2.动图相关方法
/**
将元素为SDWebImageFrame对象的数组转换为动图
*/
+ (UIImage * _Nullable)animatedImageWithFrames:(NSArray<SDWebImageFrame *> * _Nullable)frames;
复制代码
+ (UIImage *)animatedImageWithFrames:(NSArray<SDWebImageFrame *> *)frames {
// 如果数组中没有元素就不是动图就直接返回nil
NSUInteger frameCount = frames.count;
if (frameCount == 0) {
return nil;
}
// 生成临时变量保存动图
UIImage *animatedImage;
#if SD_UIKIT || SD_WATCH
// 生成一个元素类型为非负整数,长度为动图帧数的数组,保存每一帧的展示时间
NSUInteger durations[frameCount];
for (size_t i = 0; i < frameCount; i++) {
// 遍历传入的SDWebImageFrame对象数组,获取每一帧的展示时间
durations[i] = frames[i].duration * 1000;
}
// 计算所有帧展示时长的最大公约数
NSUInteger const gcd = gcdArray(frameCount, durations);
// 生成临时变量保存总时长
__block NSUInteger totalDuration = 0;
// 生成临时变量保存动图数组
NSMutableArray<UIImage *> *animatedImages = [NSMutableArray arrayWithCapacity:frameCount];
// 遍历传入的SDWebImageFrame对象数组
[frames enumerateObjectsUsingBlock:^(SDWebImageFrame * _Nonnull frame, NSUInteger idx, BOOL * _Nonnull stop) {
// 获取SDWebImageFrame对象保存的每一帧的图像
UIImage *image = frame.image;
// 获取SDWebImageFrame对象保存的每一帧的展示时间
NSUInteger duration = frame.duration * 1000;
// 增加总时长
totalDuration += duration;
// 生成临时变量保存重复次数
NSUInteger repeatCount;
// 如果计算出的最大公约数大于零,每一帧的重复次数就是展示时间除以最大公约数
// 否则每一帧只重复一次,也就说不重复
if (gcd) {
repeatCount = duration / gcd;
} else {
repeatCount = 1;
}
// 根据重复次数向动图数组中重复添加同一帧
for (size_t i = 0; i < repeatCount; ++i) {
[animatedImages addObject:image];
}
}];
// 利用生成的动图数组和时长生成动图对象
animatedImage = [UIImage animatedImageWithImages:animatedImages duration:totalDuration / 1000.f];
#else
NSMutableData *imageData = [NSMutableData data];
CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatGIF];
// Create an image destination. GIF does not support EXIF image orientation
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frameCount, NULL);
if (!imageDestination) {
// Handle failure.
return nil;
}
for (size_t i = 0; i < frameCount; i++) {
@autoreleasepool {
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);
}
}
// Finalize the destination.
if (CGImageDestinationFinalize(imageDestination) == NO) {
// Handle failure.
CFRelease(imageDestination);
return nil;
}
CFRelease(imageDestination);
SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:imageData];
animatedImage = [[NSImage alloc] initWithSize:imageRep.size];
[animatedImage addRepresentation:imageRep];
#endif
return animatedImage;
}
复制代码
/**
将动图转换为元素为SDWebImageFrame对象的数组,是上面那个方法的逆方法
*/
+ (NSArray<SDWebImageFrame *> * _Nullable)framesFromAnimatedImage:(UIImage * _Nullable)animatedImage;
复制代码
+ (NSArray<SDWebImageFrame *> *)framesFromAnimatedImage:(UIImage *)animatedImage {
// 如果没传参就不继续执行了,直接返回空
if (!animatedImage) {
return nil;
}
// 生成临时变量保存SDWebImageFrame对象和数量
NSMutableArray<SDWebImageFrame *> *frames = [NSMutableArray array];
NSUInteger frameCount = 0;
#if SD_UIKIT || SD_WATCH
// 获取动图的帧图片数组
NSArray<UIImage *> *animatedImages = animatedImage.images;
// 获取动图的帧图片数量
frameCount = animatedImages.count;
// 如果帧图片的数量为0就不继续执行了,直接返回空
if (frameCount == 0) {
return nil;
}
// 计算每一帧的平均展示时间
NSTimeInterval avgDuration = animatedImage.duration / frameCount;
// 如果这个动图没有展示时间就默认每一帧展示100毫秒
if (avgDuration == 0) {
avgDuration = 0.1;
}
// 记录不同帧图片的数量
__block NSUInteger index = 0;
// 记录一帧图片重复次数
__block NSUInteger repeatCount = 1;
// 记录当前遍历到的图片之前的图片
__block UIImage *previousImage = animatedImages.firstObject;
[animatedImages enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
// 第一张图片不处理
if (idx == 0) {
return;
}
if ([image isEqual:previousImage]) {
// 如果这一帧的图片和之前一帧图片相同就添加重复次数
repeatCount++;
} else {
// 如果两帧图片不相同,就生成SDWebImageFrame对象
SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
// 数组记录对象
[frames addObject:frame];
// 重复次数设置为一次
repeatCount = 1;
// 记录不同的帧数自增
index++;
}
// 记录当前图片,用于下次遍历使用
previousImage = image;
// 如果是最后一张照片就直接添加
if (idx == frameCount - 1) {
SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
[frames addObject:frame];
}
}];
#else
NSBitmapImageRep *bitmapRep;
for (NSImageRep *imageRep in animatedImage.representations) {
if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
bitmapRep = (NSBitmapImageRep *)imageRep;
break;
}
}
if (bitmapRep) {
frameCount = [[bitmapRep valueForProperty:NSImageFrameCount] unsignedIntegerValue];
}
if (frameCount == 0) {
return nil;
}
for (size_t i = 0; i < frameCount; i++) {
@autoreleasepool {
// NSBitmapImageRep need to manually change frame. "Good taste" API
[bitmapRep setProperty:NSImageCurrentFrame withValue:@(i)];
float frameDuration = [[bitmapRep valueForProperty:NSImageCurrentFrameDuration] floatValue];
NSImage *frameImage = [[NSImage alloc] initWithCGImage:bitmapRep.CGImage size:CGSizeZero];
SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:frameImage duration:frameDuration];
[frames addObject:frame];
}
}
#endif
return frames;
}
复制代码
3.图像方向处理相关方法
/**
将EXIF图像方向转换为iOS版本的方向
*/
+ (UIImageOrientation)imageOrientationFromEXIFOrientation:(NSInteger)exifOrientation;
复制代码
+ (UIImageOrientation)imageOrientationFromEXIFOrientation:(NSInteger)exifOrientation {
// CGImagePropertyOrientation在iOS8上才可用,这里是为了保持兼容性
UIImageOrientation imageOrientation = UIImageOrientationUp;
// 根据不同参数返回不同类型
switch (exifOrientation) {
case 1:
imageOrientation = UIImageOrientationUp;
break;
case 3:
imageOrientation = UIImageOrientationDown;
break;
case 8:
imageOrientation = UIImageOrientationLeft;
break;
case 6:
imageOrientation = UIImageOrientationRight;
break;
case 2:
imageOrientation = UIImageOrientationUpMirrored;
break;
case 4:
imageOrientation = UIImageOrientationDownMirrored;
break;
case 5:
imageOrientation = UIImageOrientationLeftMirrored;
break;
case 7:
imageOrientation = UIImageOrientationRightMirrored;
break;
default:
break;
}
return imageOrientation;
}
复制代码
/**
将iOS版本的方向转换为EXIF图像方向
*/
+ (NSInteger)exifOrientationFromImageOrientation:(UIImageOrientation)imageOrientation;
复制代码
+ (NSInteger)exifOrientationFromImageOrientation:(UIImageOrientation)imageOrientation {
NSInteger exifOrientation = 1;
// 根据不同类型返回不同数字
switch (imageOrientation) {
case UIImageOrientationUp:
exifOrientation = 1;
break;
case UIImageOrientationDown:
exifOrientation = 3;
break;
case UIImageOrientationLeft:
exifOrientation = 8;
break;
case UIImageOrientationRight:
exifOrientation = 6;
break;
case UIImageOrientationUpMirrored:
exifOrientation = 2;
break;
case UIImageOrientationDownMirrored:
exifOrientation = 4;
break;
case UIImageOrientationLeftMirrored:
exifOrientation = 5;
break;
case UIImageOrientationRightMirrored:
exifOrientation = 7;
break;
default:
break;
}
return exifOrientation;
}
复制代码
源码阅读系列: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