SDWebImage源码阅读SDWebImageDecoder
Posted CHM
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDWebImage源码阅读SDWebImageDecoder相关的知识,希望对你有一定的参考价值。
一般我们都是使用:
1 + (nullable UIImage *)imageNamed:(NSString *)name; // load from main bundle
和:
1 + (nullable UIImage *)imageWithContentsOfFile:(NSString *)path;
两种方式加载图片,它们两个的区别在SDWebImage源码阅读前的准备(三)UIImage.h 里面的 “(六):加载和创建UIImage 的类方法和实例方法:”部分有详细的介绍。
为什么要对图片进行解码?难道不能直接使用上面的两种加载方式直接进行加载显示吗,答案是可以,而且大概我们编码都是使用上面的两种方式直接在主线程加载图片然后显示在UIImageView上,并且并没有发现什么问题。那为什么SDWebImage 还要费劲去进行解码图片呢,其实我们自己不解码图片我们也是可以直接使用的(其实是系统为我们进行了解码的操作),一般下载的图片或者我们手动拖进主bundle 的图片都是PNG 或者JPG 其他格式的图片,这些图片都是经过编码压缩后的图片数据,并不是控件可以直接显示的位图,如果我们直接使用 "+ (nullable UIImage *)imageNamed:(NSString *)name" 来加载图片,系统默认会在主线程立即进行图片的解码工作,这个过程就是把图片数据解码成可供控件直接显示的位图数据,由于这个解码操作比较耗时,并且默认是在主线程进行,所以当在主线程调用了大量的 "+ (nullable UIImage *)imageNamed:(NSString *)name" 方法后就会产生卡顿。(同时由于位图体积较大,所以在磁盘缓存中不会直接缓存位图数据,而是编码压缩过的PNG 活着JPG 数据)
为了解决这个问题,我们有两种功能比较简单的处理方法:
1.使用 "+ (nullable UIImage *)imageWithContentsOfFile:(NSString *)path" 获取到图片
2.自己解码图片,并且把解码过程放在子线程
下面首先对图片的知识做一些拓展:
参考链接:https://www.objccn.io/issue-21-2/
http://honglu.me/2016/09/02/一张图片引发的深思/
下面首先看 SDWebImageDecoder.h 文件:
1 @interface UIImage (ForceDecode) 2 3 + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image; 4 5 + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image; 6 7 @end
其实是一个 UIImage 的 ForceDecode 分类,并定义了两个类方法,一个是解码图像,另一个是压缩图像。
接下来看 SDWebImageDecoder.m 的文件实现:
首先是条件编译:
1 #if SD_UIKIT || SD_WATCH 2 3 xxx... 4 5 #elif SD_MAC 6 + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image { 7 return image; 8 } 9 10 + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image { 11 return image; 12 } 13 #endif
如果是 MAC 开发平台,两个方法都是直接返回 image 参数。
如果是 ios/TV/WATCH 开发平台: + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image
1 static const size_t kBytesPerPixel = 4; 2 static const size_t kBitsPerComponent = 8; 3 4 + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image { 5 if (![UIImage shouldDecodeImage:image]) { 6 return image; 7 } 8 9 // autorelease the bitmap context and all vars to help system to free memory when there are memory warning. 10 // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory]; 11 @autoreleasepool{ 12 13 CGImageRef imageRef = image.CGImage; 14 CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef]; 15 16 size_t width = CGImageGetWidth(imageRef); 17 size_t height = CGImageGetHeight(imageRef); 18 size_t bytesPerRow = kBytesPerPixel * width; 19 20 // kCGImageAlphaNone is not supported in CGBitmapContextCreate. 21 // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast 22 // to create bitmap graphics contexts without alpha info. 23 CGContextRef context = CGBitmapContextCreate(NULL, 24 width, 25 height, 26 kBitsPerComponent, 27 bytesPerRow, 28 colorspaceRef, 29 kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast); 30 if (context == NULL) { 31 return image; 32 } 33 34 // Draw the image into the context and retrieve the new bitmap image without alpha 35 CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); 36 CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context); 37 UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha 38 scale:image.scale 39 orientation:image.imageOrientation]; 40 41 CGContextRelease(context); 42 CGImageRelease(imageRefWithoutAlpha); 43 44 return imageWithoutAlpha; 45 } 46 }
两个静态不可变的类型是 size_t 的变量:
1 static const size_t kBytesPerPixel = 4;
kBytesPerPixel 按命名英语直译过来是每个像素占内存多少字节(Byte),赋值为4,表示每个像素占4个字节。(图像在iOS 设备上是以像素为单位显示的)
1 static const size_t kBitsPerComponent = 8;
kBitsPerComponent 按命名英语直译过来是每个组件占多少位(Bit),这个不好理解,举个例子,比如RGBA,其中 R (红色)G(绿色)B(蓝色)A(透明度)总共4个组件,每个像素由这4个组件组成,且该变量被赋值为8,所以一个 RGBA 像素就是8 * 4 = 32 Bits。
知道了 kBitsPerComponent 和每个像素有多少组件组成就能计算 kBytesPerPixel 了。计算公式是: (bitsPerComponent * number of components + 7)/ 8。
[UIImage shouldDecodeImage:image]
判断要不要解码,并不是所有的image 都要解码,看 + (BOOL)shouldDecodeImage:(nullable UIImage *)image 函数实现:
1 + (BOOL)shouldDecodeImage:(nullable UIImage *)image { 2 // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error 3 if (image == nil) { 4 return NO; 5 } 6 7 // do not decode animated images 8 if (image.images != nil) { 9 return NO; 10 } 11 12 CGImageRef imageRef = image.CGImage; 13 14 CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef); 15 BOOL anyAlpha = (alpha == kCGImageAlphaFirst || 16 alpha == kCGImageAlphaLast || 17 alpha == kCGImageAlphaPremultipliedFirst || 18 alpha == kCGImageAlphaPremultipliedLast); 19 // do not decode images with alpha 20 if (anyAlpha) { 21 return NO; 22 } 23 24 return YES; 25 }
1.如果 image 等于 nil 返回 NO。 // 防止 "CGBitmapContextCreateImage: invalid context 0X0" 的错误
2.如果 image 是动效图片,即 image.images 不等于 nil,返回 NO。// 不要解码动画图像
3.通过 CGImageGetAlphaInfo(image.CGImage) 获取 CGImageAlphaInfo alpha,如果 alpha 等于
1 kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */ 2 kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */ 3 kCGImageAlphaLast, /* For example, non-premultiplied RGBA */ 4 kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
任一个枚举值,即带有 A(透明度因素),返回 NO。// 不要用alpha 解码图像
注释翻译:
// 当有内存警告的时候,自动释放 bitmap context 和所有的 vars 释放系统内存
// 在iOS 7 ,别忘了调用 [[SDImageCache sharedImageCache] clearMemory];
CGImageRef imageref
1 CGImageRef imageRef = image.CGImage;
CGColorSpaceRef colorspaceRef
1 CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
通过 imageRef 获得颜色空间(CGColorSpaceRef)colorspaceRef,获得没有 alpha 通道的colorspaceRef,下面看 + (CGColorSpaceRef)colorSpaceForImageRef:(CGImageRef)imageRef 函数实现:
1 + (CGColorSpaceRef)colorSpaceForImageRef:(CGImageRef)imageRef { 2 // current 3 CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef)); 4 CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef); 5 6 BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown || 7 imageColorSpaceModel == kCGColorSpaceModelMonochrome || 8 imageColorSpaceModel == kCGColorSpaceModelCMYK || 9 imageColorSpaceModel == kCGColorSpaceModelIndexed); 10 if (unsupportedColorSpace) { 11 colorspaceRef = CGColorSpaceCreateDeviceRGB(); 12 CFAutorelease(colorspaceRef); 13 } 14 return colorspaceRef; 15 }
CGImageGetColorSpace(imageRef)
1 /* Return the color space of `image\', or NULL if `image\' is an image 2 mask. */ 3 4 CG_EXTERN CGColorSpaceRef __nullable CGImageGetColorSpace(CGImageRef cg_nullable image) 5 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
CGColorSpaceGetModel(CGImageGetColorSpace(imageRef))
1 /* Return the color space model of `space\'. */ 2 3 CG_EXTERN CGColorSpaceModel CGColorSpaceGetModel(CGColorSpaceRef cg_nullable space) 4 CG_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
CGColorSpaceCreateDeviceRGB()
这里很重要,判断如果有 alpha 则创建一个 DeviceRGB 颜色的空间。
1 BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown || 2 imageColorSpaceModel == kCGColorSpaceModelMonochrome || 3 imageColorSpaceModel == kCGColorSpaceModelCMYK || 4 imageColorSpaceModel == kCGColorSpaceModelIndexed); 5 if (unsupportedColorSpace) { 6 colorspaceRef = CGColorSpaceCreateDeviceRGB(); 7 CFAutorelease(colorspaceRef); 8 }
这三个函数都是 CoreGraphics 框架里面的。
CGImageGetWidth(imageRef)
1 size_t width = CGImageGetWidth(imageRef);
1 /* Return the width of `image\'. */ 2 3 CG_EXTERN size_t CGImageGetWidth(CGImageRef cg_nullable image) 4 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
CGImageGetHeight(imageRef)
1 size_t height = CGImageGetHeight(imageRef);
1 /* Return the height of `image\'. */ 2 3 CG_EXTERN size_t CGImageGetHeight(CGImageRef cg_nullable image) 4 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
bytesPerRow
1 size_t bytesPerRow = kBytesPerPixel * width;
表示每行占内存多少个字节,计算方法是行宽乘以每个像素在内存占几个字节。
// kCGImageAlphaNone 表示不支持 CGBitmapContextCreate
// 因为这里的原始图像没有 alpha 信息,使用 kCGImageAlphaNoneSkipLast
// 创建bitmap graphics contexts 没有 alpha 信息
1 CGContextRef context = CGBitmapContextCreate(NULL, 2 width, 3 height, 4 kBitsPerComponent, 5 bytesPerRow, 6 colorspaceRef, 7 kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
1 /* Create a bitmap context. The context draws into a bitmap which is `width\' 2 pixels wide and `height\' pixels high. The number of components for each 3 pixel is specified by `space\', which may also specify a destination color 4 profile. The number of bits for each component of a pixel is specified by 5 `bitsPerComponent\'. The number of bytes per pixel is equal to 6 `(bitsPerComponent * number of components + 7)/8\'. Each row of the bitmap 7 consists of `bytesPerRow\' bytes, which must be at least `width * bytes 8 per pixel\' bytes; in addition, `bytesPerRow\' must be an integer multiple 9 of the number of bytes per pixel. `data\', if non-NULL, points to a block 10 of memory at least `bytesPerRow * height\' bytes. If `data\' is NULL, the 11 data for context is allocated automatically and freed when the context is 12 deallocated. `bitmapInfo\' specifies whether the bitmap should contain an 13 alpha channel and how it\'s to be generated, along with whether the 14 components are floating-point or integer. */ 15 16 CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data, 17 size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, 18 CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo) 19 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
创建没有透明因素的 bitmap graphics contexts (主要在参数 colorspaceRef,不包含 alpha 通道,和
kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast )
注意:
这里创建的 contexts 是没有透明因素的。在 UI 渲染的时候,实际上是把多个图层按像素叠加计算的过程,需要对每一个像素进行 RGBA 的叠加计算(R、G、B 分别都要乘以 A)。当某个 layer 的是不透明的,也就是 opaque 为 YES 时,GPU 可以直接忽略掉其下方的图层。这也是调用 CGBitmapContextCreate 时 bitmapInfo 参数设置为忽略掉 alpha 通道的原因。
// 将图像绘制到上下文中,并在没有alpha 信息的情况下检索新位图图像
绘制图像
1 // Draw the image into the context and retrieve the new bitmap image without alpha 2 CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); 3 CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context); 4 UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha 5 scale:image.scale 6 orientation:image.imageOrientation]; 7 8 CGContextRelease(context); 9 CGImageRelease(imageRefWithoutAlpha);
1.CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef)
1 /** Image functions. **/ 2 3 /* Draw `image\' in the rectangular area specified by `rect\' in the context 4 `c\'. The image is scaled, if necessary, to fit into `rect\'. */ 5 6 CG_EXTERN void CGContextDrawImage(CGContextRef cg_nullable c, CGRect rect, 7 CGImageRef cg_nullable image) 8 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
2.CGBitmapContextCreateImage(context)
1 /* Return an image containing a snapshot of the bitmap context `context\'. If 2 context is not a bitmap context, or if the image cannot be created for 3 any reason, this function returns NULL. This is a "copy" operation --- 4 subsequent changes to context will not affect the contents of the 5 returned image. 6 7 Note that in some cases the copy will actually follow "copy-on-write" 8 semantics, so that the actual physical copy of the bits will only occur 9 if the underlying data in the bitmap context is modified. As a 10 consequence, you may wish to use the resulting image and release it 11 before performing more drawing into the bitmap context; in this way, the 12 actual physical copy of the data may be avoided. */ 13 14 CG_EXTERN CGImageRef __nullable CGBitmapContextCreateImage( 15 CGContextRef cg_nullable context) 16 CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
3.根据返回的 CGImageRef 初始化一个 UIImage。
4.CGContextRelease(context)
1 /* Equivalent to `CFRelease(c)\'. */ 2 3 CG_EXTERN void CGContextRelease(CGContextRef cg_nullable c) 4 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
5.CGImageRelease(imageRefWithoutAlpha)
1 /* Equivalent to `CFRelease(image)\'. */ 2 3 CG_EXTERN void CGImageRelease(CGImageRef cg_nullable image) 4 CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
6.return imageWithoutAlpha;
下面看压缩图像的方法:+ (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image, 看方法实现之前先看下几个静态不可变 CGFloat 变量:
1 /* 2 * Defines the maximum size in MB of the decoded image when the flag `SDWebImageScaleDownLargeImages` is set 3 * Suggested value for iPad1 and iPhone 3GS: 60. 4 * Suggested value for iPad2 and iPhone 4: 120. 5 * Suggested value for iPhone 3G and iPod 2 and earlier devices: 30. 6 */ 7 static const CGFloat kDestImageSizeMB = 60.0f; 8 9 /* 10 * Defines the maximum size in MB of a tile used to decode image when the flag `SDWebImageScaleDownLargeImages` is set 11 * Suggested value for iPad1 and iPhone 3GS: 20. 12 * Suggested value for iPad2 and iPhone 4: 40. 13 * Suggested value for iPhone 3G and iPod 2 and earlier devices: 10. 14 */ 15 static const CGFloat kSourceImageTileSizeMB = 20.0f; 16 17 static const CGFloat kBytesPerMB = 1024.0f * 1024.0f; 18 static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel; 19 static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB; 20 static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB; 21 22 static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to overlap the seems where tiles meet.
kDestImageSizeMB 表示需要压缩图像源的大小界限,并且赋值是60 默认的单位是MB。
当然要压缩一张图像的时候,首先要把图像源文件的大小和 kDestImageSizeMB 进行比较,如果源文件大小超过了 kDestImageSizeMB,那么该图像需要被压缩。
SDWebImage 的建议:
* Suggested value for iPad1 and iPhone 3GS: 60.
* Suggested value for iPad2 and iPhone 4: 120.
* Suggested value for iPhone 3G and iPod 2 and earlier devices: 30.
kSourceImageTileSizeMb 表示用于分割源图像方块的大小,这个方块将会被用来分割原图,并且赋值为20 默认单位是MB。
SDWebImage 的建议:
* Suggested value for iPad1 and iPhone 3GS: 20.
* Suggested value for iPad2 and iPhone 4: 40.
* Suggested value for iPhone 3G and iPod 2 and earlier devices: 10.
kBytesPerMB 表示1 MB 有多少字节,相信对此都特别熟悉(1 *1024 * 1024)。
1 static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
kPixelsPerMB 表示1 MB 有多少像素,算法是:1 MB 有多少字节除以 1 个像素占多少字节(4 字节),即:kBytesPerMB / kBytesPerPixel == (1024 * 1024 /4 = 262144 个像素)。
1 static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;
kDestTotalPixels 表示图像需要压缩时的图像像素的界限,算法是:最大支持的压缩图像源有多少 MB 乘以1 MB 有多少像素。即: kDestImageSizeMB * kPixelsPerMB == (60 * 262144 = 15728640 个像素)。
1 static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
kTileTotalPixels 表示用于分割图像源的方块的总像素,算法是:原图方块有多少 MB 乘以1 MB 有多少像素。即:kSourceImageTileSizeMB * kPixelsPerMB == (20 * 262144 =5242880 个像素)。
1 static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;
kDestSeemOverlap 表示重叠像素大小,并赋值为2。
1 static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to overlap the seems where tiles meet.
好了,下面开始看 + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image 函数实现:
1 + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image { 2 if (![UIImage shouldDecodeImage:image]) { 3 return image; 4 } 5 6 if (![UIImage shouldScaleDownImage:image]) { 7 return [UIImage decodedImageWithImage:image]; 8 } 9 10 CGContextRef destContext; 11 12 // autorelease the bitmap context and all vars to help system to free memory when there are memory warning. 13 // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory]; 14 @autoreleasepool { 15 CGImageRef sourceImageRef = image.CGImage; 16 17 CGSize sourceResolution = CGSizeZero; 18 sourceResolution.width = CGImageGetWidth(sourceImageRef); 19 sourceResolution.height = CGImageGetHeight(sourceImageRef); 20 float sourceTotalPixels = sourceResolution.width * sourceResolution.height; 21 // Determine the scale ratio to apply to the input image 22 // that results in an output image of the defined size. 23 // see kDestImageSizeMB, and how it relates to destTotalPixels. 24 float imageScale = kDestTotalPixels / sourceTotalPixels; 25 CGSize destResolution = CGSizeZero; 26 destResolution.width = (int)(sourceResolution.width*imageScale); 27 destResolution.height = (int)(sourceResolution.height*imageScale); 28 29 // current color space 30 CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:sourceImageRef]; 31 32 size_t bytesPerRow = kBytesPerPixel * destResolution.width; 33 34 // Allocate enough pixel data to hold the output image. 35 void* destBitmapData = malloc( bytesPerRow * destResolution.height ); 36 if (destBitmapData == NULL) { 37 return image; 38 } 39 40 // kCGImageAlphaNone is not supported in CGBitmapContextCreate. 41 // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast 42 // to create bitmap graphics contexts without alpha info. 43 destContext = CGBitmapContextCreate(destBitmapData, 44 destResolution.width, 45 destResolution.height, 46 kBitsPerComponent, 47 bytesPerRow, 48 colorspaceRef, 49 kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast); 50 51 if (destContext == NULL) { 52 free(destBitmapData); 53 return image; 54 } 55 CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh); 56 57 // Now define the size of the rectangle to be used for the 58 // incremental blits from the input image to the output image. 59 // we use a source tile width equal to the width of the source 60 // image due to the way that iOS retrieves image data from disk. 61 // iOS must decode an image from disk in full width \'bands\', even 62 // if current graphics context is clipped to a subrect within that 63 // band. Therefore we fully utilize all of the pixel data that results 64 // from a decoding opertion by achnoring our tile size to the full 65 // width of the input image. 66 CGRect sourceTile = CGRectZero; 67 sourceTile.size.width = sourceResolution.width; 68 // The source tile height is dynamic. Since we specified the size 69 // of the source tile in MB, see how many rows of pixels high it 70 // can be given the input image width. 71 sourceTile.size.height = (int)(kTileTotalPixels / sourceTile.size.width ); 72 sourceTile.origin.x = 0.0f; 73 // The output tile is the same proportions as the input tile, but 74 // scaled to image scale. 75 CGRect destTile; 76 destTile.size.width = destResolution.width; 77 destTile.size.height = sourceTile.size.height * imageScale; 78 destTile.origin.x = 0.0f; 79 // The source seem overlap is proportionate to the destination seem overlap. 80 // this is the amount of pixels to overlap each tile as we assemble the ouput image. 81 float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height); 82 CGImageRef sourceTileImageRef; 83 // calculate the number of read/write operations required to assemble the 84 // output image. 85 int iterations = (int)( sourceResolution.height / sourceTile.size.height ); 86 // If tile height doesn\'t divide the image height evenly, add another iteration 87 // to account for the remaining pixels. 88 int remainder = (int)sourceResolution.height % (int)sourceTile.size.height; 89 if(remainder) { 90 iterations++; 91 } 92 // Add seem overlaps to the tiles, but save the original tile height for y coordinate calculations. 93 float sourceTileHeightMinusOverlap = sourceTile.size.height; 94 sourceTile.size.height += sourceSeemOverlap; 95 destTile.size.height += kDestSeemOverlap; 96 for( int y = 0; y < iterations; ++y ) { 97 @autoreleasepool { 98 sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap; 99 destTile.origin.y = destResolution.height - (( y + SDWebImage源码阅读-第三篇SDWebImage源码阅读(十五)UIView+WebCacheOperation
SDWebImage源码阅读NSImage+WebCache