ios 图片的解压缩

图片的解压缩

最近在看 SDWebimage 的源码,顺便补习下图片的解压缩原理

参考1参考2

我们一班看到的图片,都是如 png jpeg 之类的图片,都是经过编码的,为什么要编码,如果不编码将图片的原始信息传输,那么他的大小会非常非常大,不利用传输,所以经过压缩编码之后,效率更加

位图其实就是一个像素点阵图像,包含的一个一个像素点,将这些像素点组装起来放到屏幕上就可以显示了。

平时我们的使用

UIImageView *imageView = ...;
UIImage *image = [UIImage imageWithContentsOfFile:@"/.../.../path.JPG"];
imageView.image = image;

我们这样取用,UIImage *image = [UIImage imageWithContentsOfFile:@"/.../.../path.JPG"]; 这样其实并没有对图片进行解压缩,当我们赋值给imageview 的image时候,图片马上要渲染到屏幕上的时候,系统会为我们解压缩,并且是在主线程中进行的,是非常耗cpu性能的,所以,你想,我们的tableview中,有很多图片,每次都这样去设置,那能不卡吗?所以现在用到的第三方框架,都是提前给你在子线程解码好了,显示的时候拿来显示就可以了

解码 api

/* Create a bitmap context. The context draws into a bitmap which is `width'
   pixels wide and `height' pixels high. The number of components for each
   pixel is specified by `space', which may also specify a destination color
   profile. 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 bitmap
   consists of `bytesPerRow' bytes, which must be at least `width * bytes
   per pixel' bytes; in addition, `bytesPerRow' must be an integer multiple
   of the number of bytes per pixel. `data', if non-NULL, points to a block
   of memory at least `bytesPerRow * height' bytes. If `data' is NULL, the
   data for context is allocated automatically and freed when the context is
   deallocated. `bitmapInfo' specifies whether the bitmap should contain an
   alpha channel and how it's to be generated, along with whether the
   components 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);

这个接口是创建一个 CGContextRef 上下文,来分析下每个参数的含义

bitsPerComponent:表示每一个成分有多少位组成,Component 就是指颜色分量,例如 RGB 中,指定 R/G/B 这些颜色分量由多少位来表示
bytesPerPixel:表示一个像素点有多少个字节组成,上面的方法注释中提到了一个公式 (bitsPerComponent * number of components + 7)/8,即一个像素点的字节数量与表示当前图像的颜色的颜色分量数量和每个分量的位数有关
bytesPerRow:图像一行有多少字节,上面注释中也提到了,它一般是width * bytes per pixel,很好理解,也就是图像像素宽度与每个像素字节大小的乘积。所以我们可以想到最开始我们说的那个计算位图大小的公式,只要 bytesPerRow 再乘上图像的像素高度 height 即可。
space:即颜色空间,我们平常一直说的 RGB 就是一种颜色空间,另外还有 CMYK 也都是颜色空间,每个像素的信息必须要在一个颜色空间中才有意义。也就是说,我们通过颜色空间告诉系统一个像素上面的颜色信息都是什么意思。显然同样的一个位图每一个像素的信息,在 RGB 颜色中间中表示的意思与在 CMYK 中是不同的,最后渲染出来的图像一定是不一样的。

我们的手机一般支持 RGB 颜色空间,其 bitsPerComponent = 8。那 bytesPerPixel 是多少呢,根据上面的公式,我们首先要知道 RGB 中有多少颜色分量呢?我们可能认为是 3,因为红、绿、蓝就是 3 个分量,实际上一般是4?因为 RGB 颜色空间中,其实还要包含一个 alpha 通道,也就是透明度。所以实际上应该是 ARGB 或者 RGBA,两者的区别就是 alpha 通道在哪里表示,这个下面还会再说。当然也可能是3,那就说明没有 alpha 通道。现在就是知道了 RGB 颜色空间中,实际上是有 4 个分量,所以我们可以算出 bytesPerPixel = (8 * 4 + 7)/8 = 4B。所以我们现在知道为什么最开始的时候我们计算位图的大小的时候,每个像素的大小我们使用的值是 4B 了。事实上不同的颜色空间下,上面这些值都是不同的,但是一般在手机上,我们使用 RGB 颜色空间,所以差不多就是上面的值。

现在我们回到上面那个方法的参数上面:
第一个参数data,根据注释,我们可以赋值为 NULL,这样系统会自动帮我们分配和释放相应大小的空间。
第二个参数和第三个参数即上下文的宽和高,因为我们根据原始的图片来创建上下文进行渲染,所以这两个值就是原始图片的宽和高。
第四个参数bitsPerComponent我们知道了就是每个成分由多少位组成,因为我们默认实在 RGB 颜色空间中,所以一般都是传入 8。
第五个参数bytesPerRow,这个我之前也专门介绍过了,就是像素宽度与每个像素大小的乘积,因为这些值都是知道的,我们可以这么传入。当然这里我们还可以直接传入0,这样系统会帮我们进行计算,而且这样做系统还会帮我们做一些优化,这样更好。
第六个参数space即颜色空间,我们可以直接使用RGB,直接使用系统提供的 API 获取: CGColorSpaceCreateDeviceRGB()
第七个参数bitmapInfo表示位图的布局信息。这个参数实际上系统提供了一个枚举值

颜色空间

颜色空间是对色彩的一种描述方式,主要有6种:RGB、CMY/CMYK、HSV/HSB、HSI/HSL、Lab、YUV/YCbCr。

比如RGB是通过红绿蓝三原色来描述颜色的颜色空间,R=Red、G=Green、B=Blue。RGB颜色空间下,一个像素由R、G、B三个颜色分量表示,每个分量使用的bit 数就是bpc。若每个分量用8位,那么一个像素共用24位表示,24就是像素的深度。

最常用的就是RGB和CMYK。同一个色值在不同的颜色空间下表现出来是不同的颜色。

比如我们拿一个RGB格式的图片去打印,会发现打印出来的颜色和我们在电脑上面看到的有色差,这就是因为颜色空间不同导致的,因为打印机的颜色空间是CMYK。

PBC

然后这个的PBC就是一个像素中每个独立的颜色分量使用的 bit 数。

颜色分量是什么?比如RGB是通过红绿蓝三原色来描述颜色的颜色空间,R=Red、G=Green、B=Blue,也就是红绿蓝。RGB颜色空间下,一个像素就由R、G、B三个颜色分量表示,这个就是颜色分量。每个分量使用的bit 数就是bpc。

如果每个分量用8位,那么一个像素共用24位表示,24就是像素的深度。再加上如果有透明度信息,那就是8888,一共有32位也就是4个字节,就是我们前面说的iOS中每个像素所占的字节数。

BitmapInfo

然后还有BitmapInfo。BitmapInfo就是用来说明每个像素中的bits包含了哪些信息。有以下三个方面:

是否包含Alpha通道,如果包含 alpha ,那么 alpha 信息所处的位置,在像素的最低有效位,比如 RGBA ,还是最高有效位,比如 ARGB ;
如果包含 alpha ,那么每个颜色分量是否已经乘以 alpha 的值,这种做法可以加速图片的渲染时间,因为它避免了渲染时的额外乘法运算。比如,对于 RGB 颜色空间,用已经乘以 alpha 的数据来渲染图片,每个像素都可以避免 3 次乘法运算,红色乘以 alpha ,绿色乘以 alpha 和蓝色乘以 alpha
颜色分量是否为浮点数

iOS中,alpha通道的布局信息是一个枚举值 CGImageAlphaInfo ,有以下几种情况:

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通道,则应该使用kCGImageAlphaNoneSkipFirst,如果图片含alpha通道,则应该使用kCGImageAlphaPremultipliedFirst。

然后就是bitmapInfo。这个参数除了要指定alpha的信息外,就是前面提到的ARGB还是RGBA,另外还需要指定字节顺序。

字节顺序分为两种:小端模式和大端模式。它是由枚举值 CGImageByteOrderInfo 来表示的:

typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {
    kCGImageByteOrderMask     = 0x7000,
    kCGImageByteOrderDefault  = (0 << 12),
    kCGImageByteOrder16Little = (1 << 12),
    kCGImageByteOrder32Little = (2 << 12),
    kCGImageByteOrder16Big    = (3 << 12),
    kCGImageByteOrder32Big    = (4 << 12)
} CG_AVAILABLE_STARTING(10.0, 2.0);

在iOS中使用的是小端模式,在macOS中使用的是大端模式,为了兼容,使用kCGBitmapByteOrder32Host,32位字节顺序,该宏在不同的平台上面会自动组装换成不同的模式。32是指数据以32bit为单位(字节顺序)。字节顺序也以32bit为单位排序。

#ifdef __BIG_ENDIAN__
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big
#else    /* Little endian. */
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little
#endif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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