該文章閱讀的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