iOS图片加载浅析

要讲图片格式还先得从图像的基本数据结构说起。在计算机中, 图像是由一个个像素点组成,像素点就是颜色点,而颜色最简单的方式就是用RGB或RGBA表示, 如图所示。


图片格式

如果有A通道就表明这个图像可以有透明效果。


有透明度的图片

图片格式介绍

JPEG 是目前最常见的图片格式,它诞生于 1992 年,是一个很古老的格式。它只支持有损压缩,其压缩算法可以精确控制压缩比,以图像质量换得存储空间。由于它太过常见,以至于许多移动设备的 CPU都支持针对它的硬编码与硬解码。jpg是最常见的图像格式,图像占用的存储较小,但是牺牲了图像的质量。

PNG 诞生在 1995 年,比 JPEG 晚几年。它本身的设计目的是替代 GIF 格式,所以它与 GIF 有更多相似的地方。PNG 只支持无损压缩,所以它的压缩比是有上限的。相对于 JPEG 和 GIF 来说,它最大的优势在于支持完整的透明通道。png图像的大小是jpg图像大小的数倍,png为可移植网络图形格式,也是一种位图文件存储格式,可以进行无损压缩。

GIF 诞生于 1987 年,随着初代互联网流行开来。它有很多缺点,比如通常情况下只支持 256 种颜色、透明通道只有 1 bit、文件压缩比不高。它唯一的优势就是支持多帧动画,凭借这个特性,它得以从 Windows 1.0 时代流行至今,而且仍然大受欢迎。

在上面这些图片格式诞生后,也有不少公司或团体尝试对他们进行改进,或者创造其他更加优秀的图片格式,比如 JPEG 小组的 JPEG 2000、微软的 JPEG-XR、Google 的 WebP、个人开发者发布的 BPG、FLIF 等。它们相对于老牌的那几个图片格式来说有了很大的进步,但出于各种各样的原因,只有少数几个格式能够流行开来。下面三种就是目前实力比较强的新兴格式了:

APNG 是 Mozilla 在 2008 年发布的一种图片格式,旨在替换掉画质低劣的 GIF 动画。它实际上只是相当于 PNG 格式的一个扩展,所以 Mozilla 一直想把它合并到 PNG 标准里面去。然而 PNG 开发组并没有接受 APNG 这个扩展,而是一直在推进它自己的 MNG 动图格式。MNG 格式过于复杂以至于并没有什么系统或浏览器支持,而 APNG 格式由于简单容易实现,目前已经渐渐流行开来。Mozilla 自己的 Firefox 首先支持了 APNG,随后苹果的 Safari 也开始有了支持, Chrome 目前也已经尝试开始支持 ,可以说未来前景很好。

WebP 是 Google 在 2010 年发布的图片格式,希望以更高的压缩比替代 JPEG。它用 VP8 视频帧内编码作为其算法基础,取得了不错的压缩效果。它支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,并且没有版权问题,是一种非常理想的图片格式。借由 Google 在网络世界的影响力,WebP 在几年的时间内已经得到了广泛的应用。看看你手机里的 App:微博、微信、QQ、淘宝、网易新闻等等,每个 App 里都有 WebP 的身影。Facebook 则更进一步,用 WebP 来显示聊天界面的贴纸动画。

iOS 底层是用 ImageIO.framework 实现的图片编解码。目前 iOS 原生支持的格式有:JPEG、JPEG2000、PNG、GIF、BMP、ICO、TIFF、PICT,自 iOS 8.0 起,ImageIO 又加入了 APNG、SVG、RAW 格式的支持。在上层,开发者可以直接调用 ImageIO 对上面这些图片格式进行编码和解码。对于动图来说,开发者可以解码动画 GIF 和 APNG、可以编码动画 GIF。

两个平台在导入第三方编解码库时,都多少对他们进行了一些修改,比如 Android 对 libjpeg 等进行的调整以更好的控制内存,iOS 对 libpng 进行了修改以支持 APNG,并增加了多线程编解码的特性。除此之外,iOS 专门针对 JPEG 的编解码开发了 AppleJPEG.framework,实现了性能更高的硬编码和硬解码,只有当硬编码解码失败时,libjpeg 才会被用到。

JPEG

目前比较知名的 JPEG 库有以下三个

libjpeg:开发时间最早,使用最广泛的 JPEG 库。由于 JPEG 标准过于复杂和模糊,并没有其他人去实现,所以这个库是 JPEG 的事实标准。

libjpeg-turbo:一个致力于提升编解码速度的 JPEG 库。它基于 libjpeg 进行了改造,用 SIMD 指令集 (MMX、SSE2、NEON) 重写了部分代码,官网称相对于 libjpeg 有 2 到 4 倍的性能提升。

MozJPEG: 一个致力于提升压缩比的 JPEG 库。它是 Mozilla 在 2014 年发布的基于 libjpeg-turbo 进行改造的库,相对于 libjpeg 有 5% ~ 15%  的压缩比提升,但相应的其编码速度也慢了很多。

除了上面这三个库,苹果自己也开发了一个 AppleJPEG,但并没有开源。其调用了芯片提供的 DSP 硬编码和硬解码的功能。虽然它不如上面这三个库功能完善,但其性能非常高。在我的测试中,其编解码速度通常是 libjpeg-turbo 的 1~2 倍。可惜的是,目前开发者并不能直接访问这个库。

PNG

相对于 JPEG 来说,PNG 标准更为清晰和简单,因此有很多公司或个人都有自己的 PNG 编码解码实现。但目前使用最广的还是 PNG 官方发布的 libpng 库。iOS 和 Android 底层都是调用这个库实现的 PNG 编解码。

图片加载工作流程

概括来说,从磁盘中加载一张图片,并将它显示到屏幕上,中间的主要工作流如下:


 假设我们使用 **+imageWithContentsOfFile:** 方法从磁盘中加载一张图片,这个时候的图片并没有解压缩;

 然后将生成的 UIImage 赋值给 UIImageView ;

 接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的变化;

 在主线程的下一个 run loop 到来时,Core Animation 提交了这个隐式的 transaction ,这个过程可能会对图片进行 copy 操作,而受图片是否字节对齐等因素的影响,这个 copy 操作可能会涉及以下部分或全部步骤:

    分配内存缓冲区用于管理文件 IO 和解压缩操作;

    将文件数据从磁盘读到内存中;

    将压缩的图片数据解码成未压缩的位图形式,这是一个非常耗时的 CPU 操作;

    最后 Core Animation 使用未压缩的位图数据渲染 UIImageView 的图层。

为什么需要解压缩

既然图片的解压缩需要消耗大量的 CPU 时间,那么我们为什么还要对图片进行解压缩呢?是否可以不经过解压缩,而直接将图片显示到屏幕上呢?答案是否定的。要想弄明白这个问题,我们首先需要知道什么是位图:

其实,位图就是一个像素数组,数组中的每个像素就代表着图片中的一个点。我们在应用中经常用到的 JPEG 和 PNG 图片就是位图。下面,我们来看一个具体的例子以240 X 240像素为例,这是一张 PNG 图片


test.png

文件大小为37KB,37,888 字节

也就是说,这张文件大小为 37,888 B 的 PNG 图片解压缩后的大小是 230,400B ,是原始文件大小的 6.08 倍。那么这个 230,400B 是怎么得来的呢?与图片的文件大小或者像素有什么必然的联系吗?事实上,解压缩后的图片大小与原始文件大小之间没有任何关系,而只与图片的像素有关:

解压缩后的图片大小 = 图片的像素宽 30 * 图片的像素高 30 * 每个像素所占的字节数 4

事实上,不管是 JPEG 还是 PNG 图片,都是一种压缩的位图图形格式。只不过 PNG 图片是无损压缩,并且支持 alpha 通道,而 JPEG 图片则是有损压缩,可以指定 0-100% 的压缩比。值得一提的是,在苹果的 SDK 中专门提供了两个函数用来生成 PNG 和 JPEG 图片:

 // return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format

UIKIT_EXTERN NSData * __nullable UIImagePNGRepresentation(UIImage * __nonnull image);

 // return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)                         

UIKIT_EXTERN NSData * __nullable UIImageJPEGRepresentation(UIImage * __nonnull image, CGFloat compressionQuality);   

苹果对 JPEG 有硬编码和硬解码,保存成 JPEG 会大大缩减编码解码时间,也能减小文件体积。所以如果图片不包含透明像素时,UIImageJPEGRepresentation(0.9) 是最佳的图片保存方式,其次是 UIImagePNGRepresentation()。 因此,在将磁盘中的图片渲染到屏幕之前,必须先要得到图片的原始像素数据,才能执行后续的绘制操作,这就是为什么需要对图片解压缩的原因。

Webp

WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。

WebP图片相比于JPG,拥有:

1、更小的文件尺寸;

2、更高的质量——与其他相同大小不同格式的压缩图像比较。

3、目标图像的质量和文件大小之间存在明显的折中关系。在很多情况下,可以很大程度降低图像的大小,而用户却几乎不会注意到其中的差别。

根据Google的测试,目前WebP与JPG相比较,编码速度慢10倍,解码速度慢1.5倍。

在编码方面,一般来说,我们可以在图片上传时生成一份WebP图片或者在第一次访问JPG图片时生成WebP图片,对用户体验的影响基本忽略不计,主要问题在于1.5倍的解码速度是否会影响用户体验

测试数据折线图如下:


折线图

从折线图可以看到,WebP虽然会增加额外的解码时间,但由于减少了文件体积,缩短了加载的时间,页面的渲染速度加快了。同时,随着图片数量的增多,WebP页面加载的速度相对JPG页面增快了。所以,使用WebP基本没有技术阻碍,还能带来性能提升以及带宽节省。

JavaScript检测是否支持WebP代码如下:(出自Google官方文档)

function check_webp_feature(feature, callback) {

    var kTestImages = {

        lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",

        lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",

        alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",

        animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"

    };

    var img = new Image();

    img.onload = function () {

        var result = (img.width > 0) && (img.height > 0);

        callback(feature, result);

    };

    img.onerror = function () {

        callback(feature, false);

    };

    img.src = "data:image/webp;base64," + kTestImages[feature];

    }

在浏览器向服务器发起请求时,对于支持WebP图片的浏览器,会在请求头Accept中带上image/webp的信息,服务器便能识别到浏览器是否支持WebP,在服务器中处理图片。

webp在iOS设备上

当前的iOS不支持webp,不知道以后会不会支持,所以从网络上拿到一个webp格式的图片后,并不能直接显示出来,需要把data数据转化为jpg或者png来显示。

使用SDWebImage中带的WebP

> 在podfile中加入

> pod 'SDWebImage/WebP

可以在SDWebImage中加入UIImage的WebP类别,同时会引入libwebp。在使用SD加载图片时,会判定图片的格式,如果为webp,调用Google的libwebp库进行解析。

调用过程如下:

 sd_setImageWithURL ->

URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error ->

sd_imageWithData->

sd_imageWithWebPData

处理成功后会返回一个UIImage。

在WebView中使用Webp格式图片

如果有一些web页中使用了webp格式的图片,仅用上述方法是不行的,我推荐的做法是写一个自定义的URLSession protocol, 继承自NSURLProtocol, 然后注册。

 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error

{

  if (error == nil)

  {

        if ([task.currentRequest.URL.absoluteString hasSuffix:@"webp"])

        {

            NSLog(@"webp will changed:%@",task.currentRequest.URL);

            UIImage *imgData = [UIImage sd_imageWithData:self.imageData];

            NSData *transData = UIImageJPEGRepresentation(imgData, 0.8f);

            self.beginAppendData = NO;

            self.imageData = nil;

            [self.client URLProtocol:self didLoadData:transData];

        }

        [self.client URLProtocolDidFinishLoading:self];

    }

    else if ( [[error domain] isEqual:NSURLErrorDomain] && ([error code] == NSURLErrorCancelled) )

    {

        [self.client URLProtocol:self didFailWithError:error];

    }

    else

    {

        [[self client] URLProtocol:self didFailWithError:error];

    }

Demo地址

参考链接:

http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/

https://blog.csdn.net/HerbenLam/article/details/53432004

https://blog.ibireme.com/2015/11/02/ios_image_tips/

https://zh.wikipedia.org/zh-hans/WebP

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

推荐阅读更多精彩内容

  • 移动端图片格式调研 图片通常是移动端流量耗费最多的部分,并且占据着重要的视觉空间。合理的图片格式选用和优化可以为你...
    AngeloD阅读 1,235评论 0 5
  • 图片通常是移动端流量耗费最多的部分,并且占据着重要的视觉空间。合理的图片格式选用和优化可以为你节省带宽、提升视觉效...
    傻傻小萝卜阅读 745评论 1 9
  • 今天早上五点还没到就醒了,真应了那句话叫醒你的不是闹钟是梦想,我从没像加入葆婴这般的渴望成功,感受到成功,我知道我...
    Hi_张阅读 282评论 3 2