SDWebImage源码阅读(三)UIImage+GIF
UIImage+GIF 是UIImage 类的一个GIF 分类,在之前的版本里面这个分类是用了处理GIF 动态图片的但是会有内存暴增的bug。在当前 '4.0.0-beta2' 的版本里GIF 动态图片处理放在了UIImage+MultiFormat 这个分类里面,而当前这个GIF 的分类的功能只是将动态图片作为静态图片来处理,如果是静态图片的NSData 数据则转化为静态UIImage 直接返回,如果是动态图片的NSData 数据,则把图像的第1帧图像转换化为静态UIImage 返回。
首先看UIImage+GIF.h 文件:
1 @interface UIImage (GIF) 2 3 /** 4 * Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image 5 */ 6 + (UIImage *)sd_animatedGIFWithData:(NSData *)data; 7 8 /** 9 * Checks if an UIImage instance is a GIF. Will use the `images` array 10 */ 11 - (BOOL)isGIF; 12 13 @end
定义了一个实例方法和一个类方法:
1 + (UIImage *)sd_animatedGIFWithData:(NSData *)data;
兼容的方法创建一个动画UIImage 从一个NSData,它仅仅只包含第一帧图像。
判断一个UIImage 实例是否是GIF 图片:
1 - (BOOL)isGIF;
1 - (BOOL)isGIF { 2 return (self.images != nil); 3 }
检查一个UIImage 的实例是否是GIF,将使用“images” 数组判断。
UIImage+GIF.m 文件:
主要研究上面类方法的实现。
学习研究之前先做一些拓展:
size_t 类型:
size_t 类型定义在cstddef 头文件中,该文件是C标准库的头文件stddef.h 的C++ 版。它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
例如:bitset 的size 操作返回bitset对象中二进制位中的个数,返回值类型是size_t。
size_t 是标准C库中定义的,应为unsigned int,在64位系统中为 long unsigned int。
使用:
实现方式:
在C++ 中,设计 size_t 就是为了适应多个平台的。size_t 的引入增强了程序在不同平台上的可移植性。
size_t 是针对系统定制的一种数据类型,一般是整型,因为 C/C++ 标准只定义最低的位数,而不是必需的固定位数。而且在内存里,对数的高位对齐存储还是低位对齐存储各系统都不一样。为了提高代码的可移植性,就有必要定义这样的数据类型。一般这种类型都会定义到它具体占几位内存等。当然,有些是编译器或系统已经给定义好的。
经测试发现,在32位系统中size_t是4字节的,而在64位系统中,size_t 是8字节的,这样利用该类型可以增强程序的可移植性。
详细解释:
在编译的过程中size_t类型的a 值会被编译他的补码。所以在使用size_t 类型数据的过程中尤其要注意,特别是在逻辑表达式中使用到该类型,稍不注意可能带来很严重的后果。
注:正数的补码:与原码相同;负数的补码:符号位为1,其余位为该数绝对值的原码按位取反,然后整个数加1。
参考链接:http://blog.csdn.net/JIEJINQUANIL/article/details/50981834
http://jeremybai.github.io/blog/2014/09/10/size-t
http://blog.csdn.net/zhanghaotian2011/article/details/7974891
http://www.cnblogs.com/kaituorensheng/p/3239446.html
http://blog.csdn.net/bigloomy/article/details/6563870
下面看 + (UIImage *)sd_animatedGIFWithData:(NSData *)data; 方法实现:
1 + (UIImage *)sd_animatedGIFWithData:(NSData *)data { 2 if (!data) { 3 return nil; 4 } 5 6 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); 7 8 size_t count = CGImageSourceGetCount(source); 9 10 UIImage *staticImage; 11 12 if (count <= 1) { 13 staticImage = [[UIImage alloc] initWithData:data]; 14 } else { 15 // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category. 16 // this here is only code to allow drawing animated images as static ones 17 #if SD_WATCH 18 CGFloat scale = 1; 19 scale = [WKInterfaceDevice currentDevice].screenScale; 20 #elif SD_UIKIT 21 CGFloat scale = 1; 22 scale = [UIScreen mainScreen].scale; 23 #endif 24 25 CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL); 26 #if SD_UIKIT || SD_WATCH 27 UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp]; 28 staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f]; 29 #elif SD_MAC 30 staticImage = [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize]; 31 #endif 32 CGImageRelease(CGImage); 33 } 34 35 CFRelease(source); 36 37 return staticImage; 38 }
这里主要使用了 ImageIO 框架下的 <ImageIO/CGImageSource.h>,并在 UIImage+GIF.m 文件开始通过 #import <ImageIO/ImageIO.h>,引入ImageIO 框架。
1.判断传入的data 如果是nil 则直接返回nil。
2.使用CGImageSource.h 的:CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL),创建一个CGImageSourceRef 对象source。
1 /* Create an image source reading from `data'. The `options' dictionary 2 * may be used to request additional creation options; see the list of keys 3 * above for more information. */ 4 5 IMAGEIO_EXTERN CGImageSourceRef __nullable CGImageSourceCreateWithData(CFDataRef __nonnull data, CFDictionaryRef __nullable options) IMAGEIO_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_4_0);
3.使用CGImageSource.h 的:CGImageSourceGetCount(source),获得source 里面的图片数量count。
1 /* Return the number of images (not including thumbnails) in the image 2 * source `isrc'. */ 3 4 IMAGEIO_EXTERN size_t CGImageSourceGetCount(CGImageSourceRef __nonnull isrc) IMAGEIO_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_4_0);
4.创建一个UIImage 实例staticImage。(从名字里面能大概猜出创建该实例的用意)
5.如果count 小于等于1,即表示该data 数据是一个静态图片的NSData 数据,则把data 转化为UIImage 并赋值给staticImage。
6.如果count 大于1,即表示该data 数据是一个动态图片的NSData 数据。如果当前是WATCH 平台开发通过:
1 CGFloat scale = 1; 2 scale = [WKInterfaceDevice currentDevice].screenScale;
获取当前设备的screenScale,如果是iOS/TV 平台开发(包含UIKit 框架)通过:
1 CGFloat scale = 1; 2 scale = [UIScreen mainScreen].scale;
获得当前屏幕的scale,并赋值给float 类型的scale 变量,用于控制返回的动态图片的第一帧图像的大小。
7.使用CGImageSource.h 的:CGImageSourceCreateImageAtIndex(source, 0, NULL),获得source 里面index 为0 时的CGImageRef 实例CGImage。
1 /* Return the image at `index' in the image source `isrc'. The index is 2 * zero-based. The `options' dictionary may be used to request additional 3 * creation options; see the list of keys above for more information. */ 4 5 IMAGEIO_EXTERN CGImageRef __nullable CGImageSourceCreateImageAtIndex(CGImageSourceRef __nonnull isrc, size_t index, CFDictionaryRef __nullable options) IMAGEIO_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_4_0);
8.如果是iOS/TV/WATCH 平台开发,根据上面获取的CGImage 和scale 做参数,使用:
1 + (UIImage *)imageWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation NS_AVAILABLE_IOS(4_0);
创建一个UIImage 实例frameImage。
9.把frameImage 放在一个NSArray 里面作参数,使用:
1 + (nullable UIImage *)animatedImageWithImages:(NSArray<UIImage *> *)images duration:(NSTimeInterval)duration NS_AVAILABLE_IOS(5_0);
创建一个images 为一张图片,duration 为0.0f的动态图片并赋值给staticImage。如果是MAC 平台开发,依然CGImage 作参数,使用:
1 [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize];
创建一个UIImage 实例赋值给staticImage。
10.使用CGImage.h 的CGImageRelease(CGImage) 释放CGImage。
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);
11.使用CFBase.h 的CFRelease(source) 释放source。
1 CF_EXPORT 2 void CFRelease(CFTypeRef cf);
12.返回staticImage。
END