在移动app开发过程中,图片往往是不可或缺的资源。从磁盘上加载一张图片,到显示到屏幕上,中间经过了一些复杂的过程,其中非常重要的一步就是对图片的解压缩。那么,什么是图片解压缩?为什么要对图片解压缩?如何对图片解压缩?下面从这几个问题进行分析。
在解决上面的问题前,首先了解一些简单的概念。

和解压缩相关的概念

jpeg 和 png

  1. jpeg: jpeg 是一种有损压缩的图片格式,不支持透明通道。有损压缩是不可逆的,也就是说,不能从一张有损压缩的图片得到原图片数据。
  2. png: png 是一种无损压缩的图片格式。相对于jpeg来说,png 支持透明通道。因为png是无损压缩的,所以可以从png图片得到原图片数据。
    在iOS开发中,苹果提供了专门的api来 生成jpeg格式的图片和png格式的图片。
NSData * imageData = UIImageJPEGRepresentation(image, 0.9); 第二个参数就是压缩质量,官方推荐的系数是0.9。
NSData *imageData = UIImagePNGRepresentation(image); 

从api 也可以看出 jpeg 是有损压缩,png 是无损压缩。

bmp 格式的图片

BMP,全称是bitmap,也是一种图片格式。bitmap 和 jpeg以及png最大的区别就是,bitmap是无损压缩的。由于是无损压缩的,相同的图片,bitmap格式的图片体积,要比jpeg和png格式的图片大很多。看看苹果开发者文档中对bitmap的描述:

A bitmap image (or sampled image) is an array of pixels (or samples).
Each pixel represents a single point in the image. JPEG, TIFF, and PNG graphics files are examples of bitmap images.

实际上,bitmap图片就是一个像素数组,每一个像素代表图片上的一个点。
显示在屏幕上的图片,实际上就是由一个个的像素点组成的。因此,如果一张jpeg或者png格式的图片想要显示到屏幕上,需要得到该图片的像素数组,这个过程实际上就是解压缩。
这里可能会有个疑惑,既然jpeg、png格式的图片需要解压缩才能显示到屏幕上,为什么不直接使用bitmap格式的图片呢?原因上面也说了,bitmap格式的图片体积要远远大于jpeg和png格式的图片。从一个简单的例子来看下。

这张图片是项目中使用到的一张png格式的图片,大小是2KB,尺寸为48px * 44px。如果一张尺寸为48px*44px,bitmap格式的图片体积是多大呢?
48 * 44 * bytesPerPixel (4) ,大约8KB左右。
在介绍 bytesPerPixel 之前,首先了解一下颜色空间的概念。

颜色空间

在iOS 开发中,一个颜色通常由一组数字来表示。比如1 0 0 ,1 1 1。而颜色空间的作用就是告诉系统如何来解析该颜色。比如说在RGB颜色空间下,1 0 0 表示的红色,而在 BGR 颜色空间下,1 0 0 表示的就是蓝色。由此可知,脱离了颜色空间,那么用来表示颜色的数字将变得毫无意义。iOS 设备中,通常使用的是RGB颜色空间。在程序中需要使用RGB颜色空间时,苹果已经提供了相应的api来创建颜色空间,代码如下:

static CGColorSpaceRef space;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{space = CGColorSpaceCreateDeviceRGB();
});
return space;

核心api 是 CGColorSpaceCreateDeviceRGB:

/* Create a DeviceRGB color space. */CG_EXTERN CGColorSpaceRef cg_nullable CGColorSpaceCreateDeviceRGB(void)CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

通过注释可以很容易明白该方法的作用。
因为绝大多数情况下使用的都是RGB颜色空间,所以创建RGB颜色空间的代码可以使用单例实现,提高效率。

bytesPerPixel

bytesPerPixel : 每个像素所占的字节数。
在RGB颜色空间下,每个颜色分量由8位组成(bitsPerComponent),也就是一个字节。
在RGB颜色空间下,一个颜色由多少个颜色分量组成呢?从字面上看,应该是3个,分别是R、G、B三个分量。实际上,通常情况下一个颜色由4个颜色分量组成,因为还包含alpha通道,也就是透明度。每个颜色分量占一个字节,因此每个像素所占的字节数为4。
回到上面的问题,一张尺寸为48px * 44px的图片,总共有像素点个数为 48 * 44,每个像素点所占的字节数为4,所以该图片的总字节数为 48 * 44 * 4,大小约8KB左右。
一张2KB左右的png格式图片,如果使用bmp格式来存储,大小为8KB,体积的变化是非常惊人的。在移动app开发中,一个app通常需要很多的图片资源,如果图片资源使用bmp格式,那么app包体积会变得非常大,这点是不能容忍的。因此,在app开发中,基本不用bmp格式的图片。

alpha通道

上面已经提到了alpha通道,有些图片是含有alpha通道的,有些图片不含有alpha通道。通过图片信息可以看到该图片是否含有alpha通道。如含有alpha通道的图片信息:

不含alpha通道的图片信息:

在RGB颜色空间中,alpha通道所在的位置有两种,即RGBA和ARGB,即第一位或者最后一位。
iOS开发中,alpha通道的布局信息是一个枚举值,有以下几种情况:

typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {kCGImageAlphaNone,               /* For example, RGB. */kCGImageAlphaPremultipliedLast,  /* For example, premultiplied RGBA */kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */kCGImageAlphaLast,               /* For example, non-premultiplied RGBA */kCGImageAlphaFirst,              /* For example, non-premultiplied ARGB */kCGImageAlphaNoneSkipLast,       /* For example, RBGX. */kCGImageAlphaNoneSkipFirst,      /* For example, XRGB. */kCGImageAlphaOnly                /* No color data, alpha data only */
};

根据注释:
kCGImageAlphaNone : 无alpha通道
kCGImageAlphaOnly:无颜色数据,只有alpha通道
kCGImageAlphaNoneSkipLast、kCGImageAlphaNoneSkipFirst ,有alpha通道,但是忽略了alpha值,即透明度不起作用。两者的区别是alpha通道所在的位置。
kCGImageAlphaLast、kCGImageAlphaFirst ,有alpha通道,且alpha通道起作用,两者的区别是alpha通道所在的位置不同。
kCGImageAlphaPremultipliedLast、kCGImageAlphaPremultipliedFirst ,有alpha通道,且alpha通道起作用。这两个值的区别是alpha通道坐在的位置不同。和kCGImageAlphaLast、kCGImageAlphaFirst的区别是:带有Premultiplied,说明在解压缩的时候,该颜色已经将透明度乘到每个颜色分量上了,这样渲染的时候就不用再处理alpha通道,提高了渲染的效率。
如何判断一张图片是否包含alpha通道?实际上,已经有了相关的api。

CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) ;
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||alphaInfo == kCGImageAlphaPremultipliedFirst ||alphaInfo == kCGImageAlphaLast ||alphaInfo == kCGImageAlphaFirst) {hasAlpha = YES;
}

通过CGImageGetAlphaInfo 可以获取到alpha通道的布局信息,如果布局信息是kCGImageAlphaPremultipliedLast、kCGImageAlphaPremultipliedFirst、kCGImageAlphaLast、kCGImageAlphaFirst中的一个,则说明有alpha通道,否则无alpha通道。
对上面的基础的知识了解后,我们来看一下如何对图片解压缩。

如何对图片解压缩

因为项目中使用的第三方库是YYWebImage,因此,以YYWebImage为例来说明解压缩的过程(实际上,解压缩的过程是类似的)。
图片的解压缩实际上是对图片的一个拷贝,只不过是将一张压缩格式的图片(jpeg、png)拷贝成一张bitmap格式的图片。其中使用到的核心方法是:

/* Create a bitmap context. The context draws into a bitmap which is `width'pixels wide and `height' pixels high. The number of components for eachpixel is specified by `space', which may also specify a destination colorprofile. The number of bits for each component of a pixel is specified by`bitsPerComponent'. The number of bytes per pixel is equal to`(bitsPerComponent * number of components + 7)/8'. Each row of the bitmapconsists of `bytesPerRow' bytes, which must be at least `width * bytesper pixel' bytes; in addition, `bytesPerRow' must be an integer multipleof the number of bytes per pixel. `data', if non-NULL, points to a blockof memory at least `bytesPerRow * height' bytes. If `data' is NULL, thedata for context is allocated automatically and freed when the context isdeallocated. `bitmapInfo' specifies whether the bitmap should contain analpha channel and how it's to be generated, along with whether thecomponents are floating-point or integer. */CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data,size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

该方法的作用是创建一个bitmap的上下文,参数比较多,了解下这些参数的含义。
data : 如果data不为null,则data的大小至少为 bytesPerRow * height 字节;如果data为null,系统会自动的分配所需要的空间。因此,该参数传 null 即可。
width、height:上下文的宽、高,根据注释,传图片的宽、高即可。
bitsPerComponent : 根据上面的介绍,该参数写8即可。
bytesPerRow : 该参数数必须是bytesPerPixel 的整数倍,bytesPerRow 即每行所含有的字节数,大小至少为 width * bytesPerPixel。如果该参数传的是0,系统会自动的帮我们计算,因此该参数传0即可。
space:使用RGB颜色空间即可。
bitmapInfo : 位图的布局信息。主要是指定了alpha通道的信息、颜色分量是否为浮点数、像素格式的字节顺序这三种信息。
alpha通道的布局信息上面已经介绍过了,那么在解压缩时,该使用哪个值呢?根据苹果官方文档的介绍,如果图片无alpha通道,则应该使用kCGImageAlphaNoneSkipFirst,如果图片含alpha通道,则应该使用 kCGImageAlphaPremultipliedFirst。
颜色分量是否为浮点数不用关注,通常是用不到的。
像素格式的字节顺序包含两种信息:大端存储还是小端存储,以及数据是以16位为单位还是以32位为单位。像素格式字节顺序实际上也是一个枚举值:

typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {kCGImageByteOrderMask     ,kCGImageByteOrder16Little ,kCGImageByteOrder32Little ,kCGImageByteOrder16Big    ,kCGImageByteOrder32Big
} CG_AVAILABLE_STARTING(__MAC_10_12, __IPHONE_10_0);

iPhone设备使用的是小端存储方式,根据前面的介绍,4个字节表示一个像素点,所以是以32位为单位。因此像素格式字节顺序的取值应为 kCGImageByteOrder32Little。
实际上,为了防止硬编码,该值通常使用系统提供的一个宏:kCGBitmapByteOrder32Host

#ifdef __BIG_ENDIAN__
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big
#else    /* Little endian. */
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little
#endif

无论是大端存储,还是小端存储,都可以使用kCGBitmapByteOrder32Host。
上面的方法是创建一个bitmap上下文,创建上下文成功后,使用CGContextDrawImage方法将位图绘制在上下文环境中,这个过程CPU会对图片解压缩,最终从bitmap上下文中获取解压缩后的图片,这张图片是可以渲染到屏幕上的。
一个图片解压缩的示例代码:

CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||alphaInfo == kCGImageAlphaPremultipliedFirst ||alphaInfo == kCGImageAlphaLast ||alphaInfo == kCGImageAlphaFirst) {hasAlpha = YES;
}
// BGRA8888 (premultiplied) or BGRX8888
// same as UIGraphicsBeginImageContext() and -[UIView drawRect:]
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, YYCGColorSpaceGetDeviceRGB(), bitmapInfo);
if (!context) return NULL;
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode
CGImageRef newImage = CGBitmapContextCreateImage(context);
CFRelease(context);
return newImage;

需要注意的是,解压缩的操作是非常消耗CPU的,如果解压缩的操作是在主线程中,在列表中有多张图片需要解码时,滑动会有卡顿。因此,现在流行的图片库通常都是在子线程对图片进行解码,然后在主线程对图片进行渲染。

总结

至此,关于图片解压缩相关的内容就介绍完毕了,参考了网上的一些文章,加上自己的理解。如果有不完善、不正确的地方,欢迎大家留言一起交流~

参考文章

  1. http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/
  2. https://blog.ibireme.com/2015/11/02/mobile_image_benchmark/
  3. https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_images/dq_images.html#//apple_ref/doc/uid/TP30001066-CH212-SW3

iOS 图片解压缩的过程相关推荐

  1. ios 图片加载内存尺寸_iOS内存分析上-图片加载内存分析

    简介 对于大多数App来说,内存占用主要就是图片.本文将从实用的角度分析,iOS图片的内存占用.测量.优化等. iOS内存-有什么影响 在移动操作系统设备中,是不能像PC一样进行内存swap的,而随着 ...

  2. iOS程序的Build过程

    原文地址:http://beyondvincent.com/2013/11/21/2013-11-23-123-build-process/#1 声明: 1.本文是我在看完破船的文章之后进行的转载,内 ...

  3. iOS App 的编译过程

    在 iOS 开发的过程中,Xcode 为我们提供了非常完善的编译能力,正常情况下,我们只需要 Command + R 就可以将应用运行到设备上,即使打包也是一个相对愉快的过程. 但正如我们写代码无法避 ...

  4. ios 图片自动轮播

    ios 图片自动轮播 #import "NYViewController.h"#define kImageCount 5@interface NYViewController () ...

  5. ios 图片居中裁剪_iOS实现图片的缩放和居中显示

    直接上代码 // // MoveScaleImageController.h // MoveScaleImage // // Created by on 12-4-24. // Copyright ( ...

  6. android 仿照ios 图片选择,GitHub - wildma/PictureSelector: Android 图片选择器(仿 IOS 图片选择控件)...

    PictureSelector Android 图片选择器(仿 IOS 图片选择控件) 效果图 功能特点 支持通过拍照获取图片 支持通过相册获取图片 支持图片是否裁剪两种场景 支持仿 IOS 底部弹出 ...

  7. iOS图片打马赛克的实现方式--------终极解决方案

    iOS图片打马赛克分辨率丢失,图片编辑完成之后保存原图分辨率方案,绘画时内存暴增导致闪退问题 --------终极解决方案 需求是做一个编辑图片功能,结果好不容易各种搜索实现了功能,结果发现一个无解的 ...

  8. ios 图片裁剪框架_iOS图片裁剪器 – RSKImageCropper

    RSKImageCropper iOS图片裁剪器,类似Contacts应用中的图片定位美化. 基础使用方法 导入类header. #import Just create a view controll ...

  9. 大屏iPhone的适配 +iOS 图片尺寸要求

    摘自:http://blog.ibireme.com/2014/09/16/adapted_to_iphone6/ 苹果公司官网设计介绍到:Retina显示屏的超高像素密度已超过人眼能分辨的范围. R ...

最新文章

  1. R语言使用for循环嵌套ggplot2可视化输出多个可视化结果实战
  2. 用MacBook对交换机进行初始化配置
  3. html px转换,pc端px转换为rem针对屏幕分辨率进行页面适配
  4. Apache Curator之分布式锁原理(二)
  5. 你应该要掌握的7种回归分析方法
  6. 可以自定义公式的计算器_Excel万能个税计算器,税率对比显示,自定义增税点自动计算结果...
  7. JavaScript面向对象之Object类型
  8. 解释OBJECT_ID和OBJECTPROPERTY
  9. 解决linux中tmp目录下的文件被清理的问题
  10. 《移动应用开发技术——Android》课程报告-个人记账系统
  11. 2d开源游戏引擎_前5名:构建出色的CLI,开源2D游戏引擎等
  12. CDA考试-建模分析师-实用性大数据挖掘算法-数据挖掘概述
  13. IT项目管理 第六章 驾驭项目成本
  14. (M)Dynamic Programming:309. Best Time to Buy and Sell Stock with Cooldown
  15. Linux基本命令(Redhat,CentOS)
  16. 明明有本事,为什么难升职?
  17. iPhone4s刷机教程
  18. Lombok使用@Data的大坑,空指针错误
  19. 安卓aab包安装方式
  20. 浅探 进程的家族关系

热门文章

  1. 使用服务网格提升应用和网络安全
  2. Tarjan算法流程和简要证明
  3. cvte面试查漏补缺
  4. Ventoy 制作U盘启动盘 使用教程
  5. QFont/QFontInfo方法功能(QT5.12)
  6. (导数)微分与积分的概念
  7. 机器人素质教育,是时候普及一下了
  8. Folx Pro5 MAC激活密钥序列号下载V5.20.13943
  9. 人是什么?从生物学的角度来说,人就是直立行走无毛动物而已...这是一个多么可怕的世界啊
  10. DeepMind《星际争霸2》AI碾压人类遭Gary Marcus猛怼:通用智能就是空谈