SDWebImage 源码解读之工具类

本章开始将介绍SDWebImage库在原有类的基础上增加的拓展方法来完善图片下载功能,涉及的类有NSData+ImageContentType,UIImage+MultiFormat,UIImage+GIF。首先解释一下涉及到的一些基本概念和方法:

•什么是文件头

文件头是指位于文件数据流开头的一段承担一定任务的数据,一般都在开头的部分。文件有很多类型,所以这时候我们就需要利用文件头携带的信息去进行判断。

例如,用十六进制编辑器查看一张gif格式的图片的文件头如下(部分数据):

<47494638 39619001 2c01f672 007b4f27 866952a6 8839a58e 55b59d7d cbb439c3 af49e8dc 1ee9de2d faec1feb e026ece1...965a6ec9 65975e7e 09669862 8e496699 66d21408 003b>

47494638即为gif图片格式的文件头标识。

•如何根据文件头判断图片格式

图片格式           文件头

JPEG (jpg)      FFD8FFE1

PNG (png)      89504E47

GIF (gif)          47494638

TIFF(tiff)    49492A00或4D4D002A

WebP              524946462A73010057454250

重点来解释一下webp这种特殊格式,它是由12个字节组成的文件头,把这些字节通过ASCII编码,我们会得到如下内容:

•Image I/O 基础

Image I/O framework提供不透明数据类型(opaque data types),从CGImageSourceRef获取图片数据,将图片数据写入到CGImageDestinationRef。它提供一个范围很广的图片格式,包含web格式,动态图,原始相机数据。是MAC平台最快速图片编码和解码操作,能够加载多张图片的功能,支持图片元数据,并且能有效的缓存图片数据。

一个Image Sources抽象出来了图片数据,它不止包含一个图像,缩略图,还有各个图像的特征和图片文件。这些都是通过CGImageSource实现。当从Image Sources中创建图片时,可以提供一个index和dictionary(利用键值对)来创建一个缩略图或者是允许缓存。在创建图片的时候,也需提供一个index值来索引图片,因为Image Sources中可能是多张图片,如果参数时0,那么只有一个图片。可以通过CGImageSourceGetCount来获得图片在Image Sources中的数量。

当图片从网络中获取的时候,可能由于过大,数据缓慢,这时候就需要渐进式加载图片来显示。主要通过CFData对象来实现:

1.创建一个CFData去添加image data.

2.创建一个渐进式图片资源,通过 CGImageSourceCreateIncremental

3.获取图片数据到CFData中

4.调用CGImageSourceUpdateData函数,传递CFData和一个bool值,去描述这个数据是否包含全部图片数据或者只是部分数据。无论什么情况,这个data包含已经积累的全部图片文件。

如果已经有足够的图片数据,可以通过函数绘制CGImageSourceCreateImageAtIndex部分图片,然后记得要Release掉它。检查是否已经有全部的图片数据通过使用CGImageSourceGetStatusAtIndex函数。如果图片是完整的,函数返回值为kCGImageStatusComplete。否则继续3,4步骤,直到获得全部数据。Release掉渐进式增长的image source。

一.SDWebImage 源码解读之NSData+ImageContentType

先贴下源码:

现在来看源码的话,就简单易懂了。

1.获取二进制数据开头一个字节长度的数据(只需比对文件头的第一个字节数据即可)

[data getBytes:&c length:1];

2.根据文件头判断图片格式

如果第一个字节为FF,就可以判断该文件是一张格式为jpeg的图片,依此论推。

3.针对webp这种特殊格式,先判断data的字节长度是否> 12,如果不大于12,说明不是webp格式;

如果> 12,进而将十六进制转为文本字符串,如果该字符串开头是RIFF,并且结尾是WEBP,就判定该文件类型为webp格式的图片。

总结:从该类来看,SDWebImage判断图片的类型的方法还是比较容易理解,主要考察的是对数据流的文件头知识的了解,对于我来说,之前这块没有进行资料了解的情况下会有点看不懂,理解之后就能够理解这样做的原因。

二.SDWebImage 源码解读之UIImage+GIF

该类中拓展了三个方法,都是针对gif格式图片的加载做完善。

+ (UIImage *)sd_animatedGIFNamed:(NSString *)name;//根据名字获取本地的gif图片(该方法比较简单,自行查看即可,下面不进行涉及)

+ (UIImage *)sd_animatedGIFWithData:(NSData *)data;//根据图片二进制数据获取图片对象

- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size;//根据给定的大小缩放gif图片

现在先来看看关键方法的代码,上面三个方法都是基于此方法进行的:

该方法是通过Image I/O framework提供的一些API接口去获取gif图片中指定帧的图片基本数据,从这些数据中返回当前帧图片的动画时间。CGImageSource是一个负责读取图片数据的类,外部接口通过Image I/O framework提供的一些API接口可以将图片的数据转化成CGImageSource。CGImageSourceCopyPropertiesAtIndex(source, index, nil)方法是获取gif图片数据中指定帧的图片数据的API接口,该接口会返回一个CFDictionaryRef类型的图片数据,通过(__bridge NSDictionary *)的方法就能转化成我们熟悉的NSDictionary类型。在该代码段中,获取到帧图片的gifProperties属性数据之后,根据kCGImagePropertyGIFUnclampedDelayTime和kCGImagePropertyGIFDelayTime去获取帧图片的动画时间,之后对这个动画时间做一个最小值的处理,默认都为0.1s。

现在讲讲第二个方法,根据图片二进制数据获取图片对象:

在该方法中,使用CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL)方法,将图片的二进制数据转化成CGImageSourceRef source 类型,这样便可以使用Image I/O framework提供的一些API接口,CGImageSourceGetCount(source)该方法是用于获取图片中的帧数,如果帧数小于等于1,则直接将图片数据进行转化成UIImage类型不进行操作,如果大于1之后,会对gif图片中所有的帧图片进行遍历,保存到图片数组images中,并统计所有帧动画的总时长,如果最后的总时长为0的情况下就设置默认每帧0.1s的总时长,之后利用UIImage的[UIImage animatedImageWithImages:images duration:duration]方法,将所有的帧动画重新组合成一个UIImage对象。下面讲讲第三个方法:

上边的方法能够实现把图片的尺寸修剪为size,剪裁的前提是根据给出的大小与原图片的宽跟高进行对比,求出宽比,高比中的最大值,然后计算出最大比之后的图片大小,也就是scaledSize的值,同时计算出对应的裁剪开始位置thumbnailPoint,利用UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);方法按比例对每一帧的图片进行绘制。

三.SDWebImage 源码解读之UIImage+MultiFormat

该类中拓展了一个方法,+ (UIImage *)sd_imageWithData:(NSData *)data;,该方法的作用是用来将图片的二进制数据转化成iOS中能够直接使用的UIImage对象,支持多种图片格式进行转化,比UIImage+GIF.h类具有更好的拓展性。先看看该功能的实现思路。

该方法通过调用NSData+ImageContentType.h类中的sd_contentTypeForImageData方法去获取图片的类型,如果是gif类型的图片就是用UIImage+GIF.h中的+ (UIImage *)sd_animatedGIFWithData:(NSData *)data方法将二进制数据转化成图片。如果是webo格式的图片会调用sd_imageWithWebPData进行转化(该方法在SDWebImage库中并没有找到对应实现不知道作者是在哪里进行了实现操作),其他类型的则会使用+ (UIImage *)imageWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation;方法进行转化,下面的那个方法是利用Image I/O framework库中提供的API方法在获取到的图片数据信息中找出图片的转向,进行绘制。这个属性时为了防止进行剪切或者缩放后图片颠倒或者旋转的问题,保持原图片的转向。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容