该文章阅读的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 {// 如果数组中没有元素就不是动图就直接返回nilNSUInteger 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];#elseNSMutableData *imageData = [NSMutableData data];CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatGIF];// Create an image destination. GIF does not support EXIF image orientationCGImageDestinationRef 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];
#endifreturn 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];}}];#elseNSBitmapImageRep *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];}}
#endifreturn 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(一)——从使用入手
源码阅读: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