一、解码方式:
1、CGContextDrawImage方式(CoreGraphics)
它接受一个原始的位图参数 imageRef ,最终返回一个新的解压缩后的位图 newImage ,中间主要经过了以下三个步骤:
使用 CGBitmapContextCreate 函数创建一个位图上下文;
使用 CGContextDrawImage 函数将原始位图绘制到上下文中;
使用 CGBitmapContextCreateImage 函数创建一张新的解压缩后的位图。
2、CGImageGetDataProvider方式(ImageIO)
1.CGImageSourceCreateWithData(data) 创建ImageSource。
2.CGImageSourceCreateImageAtIndex(source) 创建一个未解码的 CGImage。
3.CGImageGetDataProvider(image) 获取这个图片的数据源。
4.CGDataProviderCopyData(provider) 从数据源获取直接解码的数据。
ImageIO 解码发生在最后一步,这样获得的数据是没有经过颜色类型转换的原生数据(比如灰度图像)。
1.NSString *resource = [[NSBundlemainBundle] pathForResource:@"the_red_batman"ofType:@"png"];
2. NSData *data = [NSData dataWithContentsOfFile:resource options:0error:nil];
3. CFDataRef dataRef = (__bridge CFDataRef)data;
4. CGImageSourceRef source =CGImageSourceCreateWithData(dataRef, nil);
5. CGImageRef cgImage =CGImageSourceCreateImageAtIndex(source, 0, nil);
6. CFDataRef rawData =CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
7.UIImage *image = [UIImageimageWithCGImage:cgImage];
二 图片的空间消耗
1磁盘空间或者通过internet传输所消耗的空间
2解压缩空间,通常是长X宽X高X4字节(RGBA)
3当显示在一个view中时,view本身也需要空间来存储layer
对于这里的第一个问题,有一个可能的优化方法:将压缩的文件拷贝到内存中不如映射到内存中,NSData有能力来假设一块磁盘空间是在内存中的,这样当访问这个图片时实际上就是从磁盘访问而不是从内存。据说CGImage知道哪种访问方式是最高效的,UIImage只是将CGImage封装了一下。
三 图片加载时间
对于“将这些像素显示到屏幕上最快要多久?”这个问题,显示一个图片所消耗的时间由以下三个因素决定:
1从磁盘上alloc/initUIImage的时间
2解压缩的时间
3 将解压缩后的比特转换成CGContext的时间,通常需要改变尺寸,混合,抗锯齿工作。
四、一些知识点
1.我要是用 imageWithData 能不能避免缓存呢?
不能。通过数据创建 UIImage 时,UIImage 底层是调用 ImageIO 的 CGImageSourceCreateWithData( ) 方法。该方法有个参数叫ShouldCache,在 64 位的设备上,这个参数是默认开启的。这个图片也是同样在第一次显示到屏幕时才会被解码,随后解码数据被缓存到 CGImage 内部。与 imageNamed 创建的图片不同,如果这个图片被释放掉,其内部的解码数据也会被立刻释放。
2.怎么能避免缓存呢?
1). 手动调用 CGImageSourceCreateWithData( ) 来创建图片,并把ShouldCache 和 ShouldCacheImmediately 关掉。这么做会导致每次图片显示到屏幕时,解码方法都会被调用,造成很大的 CPU 占用。
2). 把图片用 CGContextDrawImage( ) 绘制到画布上,然后把画布的数据取出来当作图片。这也是常见的网络图片库的做法。
3. 我能直接取到图片解码后的数据,而不是通过画布取到吗?
1.CGImageSourceCreateWithData(data) 创建ImageSource。
2.CGImageSourceCreateImageAtIndex(source) 创建一个未解码的 CGImage。
3.CGImageGetDataProvider(image) 获取这个图片的数据源。
4.CGDataProviderCopyData(provider)从数据源获取直接解码的数据。
4. 如何判断一个文件的图片类型?
通过读取文件或数据的头几个字节然后和对应图片格式标准进行比对。我在这里写了一个简单的函数,能很快速的判断图片格式。
5. 怎样像浏览器那样边下载边显示图片?
(http://blog.ibireme.com/2015/11/02/ios_image_tips/)
demo: https://github.com/smileEvday/SvIncrementallyImage
首先,图片本身有 3 种常见的编码方式:
第一种是 baseline,即逐行扫描。默认情况下,JPEG、PNG、GIF 都是这种保存方式。
第二种是 interlaced,即隔行扫描。PNG 和 GIF 在保存时可以选择这种格式。
第三种是 progressive,即渐进式。JPEG 在保存时可以选择这种方式。
在下载图片时,首先用CGImageSourceCreateIncremental(NULL) 创建一个空的图片源,随后在获得新数据时调用
CGImageSourceUpdateData(data,false) 来更新图片源,
最后在用CGImageSourceCreateImageAtIndex() 创建图片来显示。
你可以用 PINRemoteImage 或者我写的 YYWebImage 来实现这个效果。SDWebImage 并没有用 Incremental 方式解码,所以显示效果很差。
6.如何把 GIF 动图保存到相册?
iOS 的相册是支持保存 GIF 和 APNG 动图的,只是不能直接播放。用 [ALAssetsLibrarywriteImageDataToSavedPhotosAlbum:metadata:completionBlock] 可以直接把 APNG、GIF 的数据写入相册。如果图省事直接用 UIImageWriteToSavedPhotosAlbum() 写相册,那么图像会被强制转码为 PNG。
7.强制解压缩
当第一次使用图片时,iOS会解压它。通常这个解压缩后的版本将滞留一段时间(内存允许)。尽管这么做没什么意义,但你可以通过将图片渲染成一个新的图片来解压缩这个图片。这样你将在一小段时间内获得两个解压缩的版本。
- (void)decompressImage:(UIImage *)image
{
UIGraphicsBeginImageContext(CGSizeMake(1, 1));
[imagedrawAtPoint:CGPointZero];
UIGraphicsEndImageContext();
}
这一段代码会解压缩这个image,即时它只有一个像素。
奇怪的是如果UIImage只是通过initWithContentsOfFile创建的,我不能始终保持这个解压缩的版本。所以我必须使用ImageIO framework(iOS 4之后可用) 中提供的一个选项来显式保持这个解压缩的版本:
NSDictionary *dict = [NSDictionarydictionaryWithObject: [NSNumber numberWithBool:YES]
forKey:(id)kCGImageSourceShouldCache];
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)url,NULL);
CGImageRef cgImage =CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)dict);
UIImage *retImage = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CFRelease(source);
这样初始化图片就可以让解压缩仅发生一次:第一次解压缩消耗很长一段时间,第二次完全不消耗。这其中的关键就是kCGImageSourceShouldCache,你可以为CGImageSource和CGImageSourceCreateImageAtIndex使用这个选项,头文件中是这样说明的:
Specifies whether the image should becached in a decoded form. The value of this key must be a CFBooleanRef; thedefault value is kCFBooleanFalse.
如果这个选项设置为NO,绘制图片的时间又会随着解压缩时间增长,如果设置为YES就仅仅解压缩一次。
参考资料
l iOS中的imageIO与image解码
http://ios.jobbole.com/87233/
l 谈谈 iOS 中图片的解压缩
http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/
l iOS/OSX 下对各类图片的编解码、图片裸数据(bitmapdata)的获取、根据裸数据(bitmap)重构图片,及源码
http://blog.csdn.net/hbw1992322/article/details/51957285
l imageIO完成渐进加载图片
http://blog.csdn.net/dyllove98/article/details/9707681
l iOS中ImageIO框架详解与应用分析https://my.oschina.net/u/2340880/blog/838680
l 图片处理:Image I/O学习笔记http://www.jianshu.com/p/4dcd6e4bdbf0
l Image I/O 编程指引http://blog.csdn.net/king2716/article/details/
l Image I/O编程指南 http://supershll.blog.163.com/blog/static/37070436201298111139748/