介绍下iOS中ImageIO的概览与应用
Image I/O Programming Guide
介绍
ImageIO框架提供了读取与写入图片数据的基本方法,使用它可以直接获取到图片文件的内容数据,ImageIO具有很多特性 Mac平台上最快的图像解码器和编码器
逐步加载图像的能力`
支持图像元数据有效缓存` ImageIO框架中包含6个头文件:
- ImageIO.CGImageAnimation
- ImageIO.CGImageDestination 负责写入图片数据。
- ImageIO.CGImageMetadata 图片文件元数据类
- ImageIO.CGImageProperties 定义了框架中使用的字符串常量和宏。
- ImageIO.CGImageSource 负责读取图片数据。
ImageIO.ImageIOBase 预处理逻辑
获取支持的图片格式
ImageIO 框架了解大多数常见的图像文件格式,例如JPEG,JPEG2000,RAW,TIFF,BMP和PNG。并非每个平台都支持所有格式
通过 CGImageSourceCopyTypeIdentifiers 和 CGImageDestinationCopyTypeIdentifiers 可以看到支持的图片格式。
1 | //返回Image IO 支持的统一类型标识符(UTI)数组作为图像源。 |
基本使用|获取图片 缩略图
在平时开发中,我们通常使用UIImage来读取图片,UIImage支持的图片包括png与jpg等,但是类似ico图标,UIImage默认是无法显示的,
可以通过ImageIO框架来在iOS系统中使用ico图标,示例如下
1 | func getImage() -> UIImage? { |
获取缩略图
1 | func downsample(imageAt imageURL:URL,to pointSize:CGSize,scale:CGFloat)->UIImage{ |
渐进渲染大图
渐进式解码(Progressive Decoding),即不需要完整的图像流数据,允许解码部分帧(大部分情况下,会是图像的部分区域),对部分使用了渐进式编码的格式(参考:渐进式编码)),则更可以解码出相对模糊但完整的图像。比如说,JPEG支持三种方式的渐进式编码,包括Baseline,interlaced,以及progressive
使用ImageIO框架可以实现大图渐进渲染的效果,一般在对大图片进行网络请求时,可以获取一部分数据就加载一部分数据
创建一个空的CGImageSource,然后在每次收到数据的时候调用CGImageSourceUpdateData更新imageSource的数据,接着调用CGImageSourceCreateImageAtIndex获取最新的图片即可
1 | //demo使用计时器模拟 |
读取图像格式元数据
创建好CGImageSource之后,我们是可以立即解码。但是很多情况下,我们需要获取一些相关的图像信息,包括图像的格式,图像数量,EXIF元数据等。在真正解码之前,我们可以拿到这些数据,进行一些处理,之后再开始解码过程。
其中,这些信息可以直接在CGImageSource上获取:
- 图像格式:CGImageSourceGetType
- 图像数量(动图):CGImageSourceGetCount
其他的,需要通过获取属性列表来查询。对于图像容器的属性(EXIF等),我们需要使用CGImageSourceCopyProperties即可,然后根据不同的Key去获取对应的信息。
其实苹果还有一套CGImageSourceCopyMetadataAtIndex,对应的数据不是字典,而是一个CGImageMetadata,再通过其他方法去取。这套API使用起来也是可以的,读取数据和前者是完全兼容一致的,优点是能够进行自定义扩展(比如说你有非标准的图像信息想自己添加和删除)。一般来说使用前者就足够了。
1 | CGImageSourceCopyPropertiesAtIndex方法都会返回一个字典,字典中可能包含如下有意义的键: |
显示GIF
- 首先使用ImageIO库中的CGImageSource加载Gif文件。
- 通过CGImageSource获取到Gif文件中的总的帧数,以及每一帧的显示时间。
- 通过CAKeyframeAnimation来完成Gif动画的播放。
1 | func getGif(url:URL){ |
创建动画图片
- 首先,它创建一对字典来保存动画属性。第一个字典指定动画PNG在停止到最后一帧之前应重复其动画的时间。第二个字典指定序列中每个帧使用的帧延迟。
- 创建图像目标之后,代码将设置目标图像的文件属性,然后一次添加一个帧。
- 最后,CGImageDestinationFinalize调用该方法以完成动画PNG。
1 | func createAnimatePng(fileURL:CFURL,kUTTypePNG:CFString,imageForFrame){ |
图片编码 | CGImageDestination
imageIO中使用CGImageDestination对静态图的编码,基本可以分为以下步骤:
- 创建CGImageDestination
- 添加图像格式元数据(可选)和CGImage
- 编码得到NSData,清理
1. 创建CGImageDestination
CGImageDestination的创建也有三个接口,你需要提供一个输出的目标来输出解码后的数据。同时,由于编码需要提供文件格式,你需要指明对应编码的文件格式,用的是UTI Type。对于静态图来说,第三个参数的数量都写1即可。
- CGImageDestinationCreateWithData:指定一个可变二进制数据作为输出
- CGImageDestinationCreateWithURL:指定一个文件路径作为输出
- CGImageDestinationCreateWithDataConsumer:指定一个DataConsumer作为输出
2. 添加图像格式元数据(可选)和CGImage
接下来就是添加图像了,由于CGImage只是包含基本的图像信息,很多额外信息比如说EXIF都已经丢失了,如果我们需要,可以添加对应的元信息。不像解码那样提供了两个API分别获取元信息和图像。使用的接口是CGImageDestinationAddImage。
当然,如果有自定义的元信息,可以通过另外的CGImageDestinationAddImageAndMetadata来添加CGImageMetadata,这个上面解码也说到过,这里就不解释了。
此外,还有一个ImageIO最强大的功能,叫做CGImageDestinationAddImageFromSource(这个东西可以媲美vImageConvert_AnyToAny,后续教程会谈到),这个能够从一个任意的CGImageSource,添加一个图像帧到任意一个CGImageDestination。这个一般的用途,就是专门给图像转换器用的,比如说从图像格式A,转换到图像格式B。我们不需要先解码到A的UIImage,再通过编码到B的NSData,直接在中间就进行了转换。能够极大地提升转换效率(Image/IO底层就是通过vImage,传的是Bitmap的引用,没有额外的消耗)。不过这篇教程侧重于Image/IO的编码和解码,转换可以自行参考处理,不再详细说明了。
3. 编码得到NSData,清理
当添加完成所有需要编码的CGImage之后,最后一步,就是进行编码,得到图像格式的数据。这里直接用一个方法CGImageDestinationFinalize即可,编码得到的数据,会写入最早初始化时提供的Data或者DataConsumer。