Image I/O基础
Image I/O框架提供了从源图像读取数据的不透明数据类型(CGImageSourceRef)和图像数据写入到目的地(CGImageDestinationRef),它支持多种图片格式,包括标准的web格式,高动态范围图片和相机原始数据,Image I/O还有许多特性,如:
最快的图片编解码(MAC平台)
增量加载图片的能力
支持图像元数据
有效的缓存
创建图片源和写入目的地的方法有:URLS:可以将指定位置的图片(URL)作为图片数据的提供者或接受者,在Image I/O中,URL在Core Foundation框架中的数据类型是CFURLRef,可以通过(__bridge CFURLRef)NSURL桥接.
Core Foundation对象 CFDataRef和CFMutableDataRef
Quartz data consumer(消费者) (CGDataConsumerRef) 和 data provider(提供者) (CGDataProviderRef)
使用
导入头文件#import <ImageIO/ImageIO.h>
支持的图片格式
Image I/O框架支持大多数常见的图像文件格式,如JPEG、JPEG2000、RAW、TIFF、BMP和PNG。并不是每个平台都支持所有格式。可以调用函数CGImageSourceCopyTypeIdentifiers(创建图片源),CGImageDestinationCopyTypeIdentifiers(写入)来查看支持的格式列表
// 获取、打印所支持的统一类型标识符
- (void)getUniformyTypeIdentifiers{
//数组
CFArrayRef mySourceTypes = CGImageSourceCopyTypeIdentifiers();
//在控制台打印数组
CFShow(mySourceTypes);
CFArrayRef myDestinationTypes = CGImageDestinationCopyTypeIdentifiers();
CFShow(myDestinationTypes);
}
创建和使用图片源
图片源提取了数据访问任务并且节省了通过原始缓存数据中管理数据的需要。数据源可以包含多个图片,缩略图,每张图片的属性和图片文件。当你需要用到图片数据时,图片源是最好的方式。在创建CGImageSource对象后,你可以通过引用这个对象获取图片,缩略图,图片属性和其他图片信息.
创建一张图片通过图片源
Image I/O最经常完成的一个任务就是从图片源中创建图片。如下面的例子所示。该例子展示了怎样从路径名中创建一个图片源并且抽取图片。当你创建图片源对象后,你可以提供一个图片格式的提示
从图片源中创建图片时,必须指定一个索引并且提供属性的字典来指定是否创建缩略图、是否允许缓存等。详见CGImageSource Reference 和 CGImageProperties Reference
提供索引的原因是因为一些图片文件格式允许同个图片源中有多个图片(比如gif)。如果只有一个的话可以传0。可以调用CGImageSourceGetCount来获取数量
- (void)getImageWithImageSourceRef{
NSString *string = [[NSBundle mainBundle]pathForResource:@"videoPlay.png" ofType:nil];
NSURL *url = [NSURL fileURLWithPath:string];
CFStringRef keys[2],values[2];
//将图片缓存成解码格式
keys[0] = kCGImageSourceShouldCache;
values[0] = (CFTypeRef)kCFBooleanTrue;
//在图片格式支持浮点值时使用浮点值
keys[1] = kCGImageSourceShouldAllowFloat;
values[1] = (CFTypeRef)kCFBooleanTrue;
/**
创建一个不可变字典
@param allocator#> 为字典分配内存,传NULL或kCFAllocatorDefault使用当前默认的分配器 description#>
@param keys#> key的数组。如果numValues参数为0,则这个值可能是NULL。这个函数没有改变或释放这个数组。该值必须是有效的C数组 description#>
@param values#> value的数组 description#>
@param numValues#> 键值对数目。>=0 && >=实际数目。 description#>
@param keyCallBacks#> 键的回调。 description#>
@param valueCallBacks#> 值的回调 description#>
@return CF字典
*/
CFDictionaryRef options = CFDictionaryCreate(NULL, (const void**)keys, (const void **)values, 2, &kCFTypeDictionaryKeyCallBacks,& kCFTypeDictionaryValueCallBacks);
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)url, options);
if (imageSource == NULL) {
fprintf(stderr, "Image source is NULL.");
}
CFRelease(options);
size_t count = CGImageSourceGetCount(imageSource);
NSLog(@"图片数量:%zu",count);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
if (imageRef == NULL) {
fprintf(stderr, "Image not created from image source.");
}
self.imageView.image = [UIImage imageWithCGImage:imageRef];
}
通过图片源创建一张缩略图
一些图片源文件包含可以检索的缩略图。如果缩略图还没有出现,Image I/O将提供创建它们的选项。可以指定最大缩略图大小以及是否对缩略图进行转换。
使用kCGImageSourceCreateThumbnailWithTransform,指定缩略图是否应该旋转和缩放,以匹配整张图片的方向和像素宽高比。
CGImageRef MyCreateThumbnailImageFromData (NSData * data, int imageSize)
{
CGImageRef myThumbnailImage = NULL;
CGImageSourceRef myImageSource;
CFDictionaryRef myOptions = NULL;
CFStringRef myKeys[3];
CFTypeRef myValues[3];
CFNumberRef thumbnailSize;
//通过NSData创建图片源
myImageSource = CGImageSourceCreateWithData((CFDataRef)data,
NULL);
//确认图片源存在
if (myImageSource == NULL){
fprintf(stderr, "Image source is NULL.");
return NULL;
}
// 创建CFNumber对象. kCFNumerType
thumbnailSize = CFNumberCreate(NULL, kCFNumberIntType, &imageSize);
//配置缩略图选项
myKeys[0] = kCGImageSourceCreateThumbnailWithTransform;
myValues[0] = (CFTypeRef)kCFBooleanTrue;
myKeys[1] = kCGImageSourceCreateThumbnailFromImageIfAbsent;
myValues[1] = (CFTypeRef)kCFBooleanTrue;
myKeys[2] = kCGImageSourceThumbnailMaxPixelSize;
myValues[2] = (CFTypeRef)thumbnailSize;
myOptions = CFDictionaryCreate(NULL, (const void **) myKeys,
(const void **) myValues, 2,
&kCFTypeDictionaryKeyCallBacks,
& kCFTypeDictionaryValueCallBacks);
//创建缩略图
myThumbnailImage = CGImageSourceCreateThumbnailAtIndex(myImageSource,
0,
myOptions);
//ref类型的需要释放
CFRelease(thumbnailSize);
CFRelease(myOptions);
CFRelease(myImageSource);
if (myThumbnailImage == NULL){
fprintf(stderr, "Thumbnail image not created from image source.");
return NULL;
}
return myThumbnailImage;
}
增量显示一张图片
如果有非常大的图片,或者正在Web上加载图片,可以创建一个逐步加载的图片源,动态的绘制图片。
步骤:
- 创建CFData对象来累加图片数据
- 通过调用函数CGImageSourceCreateIncremental创建增量图片源
- 添加图片数据到CFData对象。
- 调用函数CGImageSourceUpdateData,将CFData对象和指定数据参数是否包含整张图片亦或者是部分的图片数据的布尔值传递过去。事实上,数据参数必须包含完整的图片文件数据,以便能加载到那个点。
- 如果已经积累了足够的图像数据,通过调用CGImageSourceCreateImageAtIndex创建一个图像,绘制部分图像,然后释放它。
- 调用函数CGImageSourceGetStatusAtIndex检查所有的图像数据。如果图像是完整的,这个函数返回kCGImageStatusComplete。如果图像不完整,请重复步骤3和步骤4,直到它结束为止。
- 释放增量图像源。
@interface ViewController ()<NSURLSessionDataDelegate>{
CGImageSourceRef _incrementallyImgSource;
NSMutableData *_recieveData;
bool _isLoadFinished;
long long _expectedLeght;
}
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建一个空的CGImageSource
_incrementallyImgSource = CGImageSourceCreateIncremental(NULL);
_recieveData = [[NSMutableData alloc]init];
_isLoadFinished = false;
// 使用代理方法需要设置代理,但是session的delegate属性是只读的,要想设置代理只能通过这种方式创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration
:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
// 创建任务(因为要使用代理方法,就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://cdn.eso.org/images/large/eso0934a.jpg"]]];
// 启动任务
[task resume];
}
//对应的代理方法如下:
// 1.接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
_expectedLeght = response.expectedContentLength;
NSLog(@"expected Length: %lld", _expectedLeght);
NSString *mimeType = response.MIMEType;
NSLog(@"MIME TYPE %@", mimeType);
NSArray *arr = [mimeType componentsSeparatedByString:@"/"];
if (arr.count < 1 || ![[arr objectAtIndex:0] isEqual:@"image"]) {
NSLog(@"not a image url");
}
// 允许处理服务器的响应,才会继续接收服务器返回的数据
completionHandler(NSURLSessionResponseAllow);
}
// 2.接收到服务器的数据(可能调用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 处理每次接收的数据
[_recieveData appendData:data];
_isLoadFinished = false;
if (_expectedLeght == _recieveData.length) {
_isLoadFinished = true;
}
// 每次收到数据的时候调用CGImageSourceUpdateData更新imageSource的数据,接着调用CGImageSourceCreateImageAtIndex获取最新的图片
CGImageSourceUpdateData(_incrementallyImgSource, (CFDataRef)_recieveData, _isLoadFinished);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_incrementallyImgSource, 0, NULL);
self.imageView.image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
}
// 3.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 请求完成,成功或者失败的处理
if (!error) {
NSLog(@"下载完毕!");
CFRelease(_incrementallyImgSource);
}
}
@end
显示图片属性
数码照片包含了丰富的信息:图像尺寸(dimensions),分辨率(resolution),方向9orientation0,颜色配置(color profile),光圈(aperture),测光模式(metering mode),焦距(focal length),创建日期,关键字,标题(caption),以及更多的信息。通过调用CGImageSourceCopyPropertiesAtIndex,我们可以拿到这些信息,这将对于图像处理和图像编辑非常有用.
- (void)getImageSourceProperty{
NSString *path = [[NSBundle mainBundle]pathForResource:@"gif.gif" ofType:nil];
NSURL *imageFileURL = [NSURL fileURLWithPath:path];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)imageFileURL, NULL);
if (imageSource) {
NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache:@NO};
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
CFShow(imageProperties);
if (imageProperties) {
CFStringRef width = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
NSLog(@"width:%@",width);
CFStringRef height = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
NSLog(@"height:%@",height);
CFDictionaryRef exif = CFDictionaryGetValue(imageProperties, kCGImagePropertyExifDictionary);
if (exif) {
NSString *dateTakenString = (NSString *)CFDictionaryGetValue(exif, kCGImagePropertyExifDateTimeOriginal);
NSLog(@"Date Taken: %@", dateTakenString);
}
CFDictionaryRef tiff = CFDictionaryGetValue(imageProperties, kCGImagePropertyTIFFDictionary);
if (tiff) {
NSString *cameraModel = (NSString *)CFDictionaryGetValue(tiff, kCGImagePropertyTIFFModel);
NSLog(@"Camera Model: %@", cameraModel);
}
CFDictionaryRef gps = CFDictionaryGetValue(imageProperties, kCGImagePropertyGPSDictionary);
if (gps) {
NSString *latitudeString = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLatitude);
NSString *latitudeRef = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLatitudeRef);
NSString *longitudeString = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLongitude);
NSString *longitudeRef = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLongitudeRef);
NSLog(@"GPS Coordinates: %@ %@ / %@ %@", longitudeString, longitudeRef, latitudeString, latitudeRef);
}
CFRelease(imageProperties);
}
CFRelease(imageSource);
} else {
NSLog(@"Error loading image");
}
}
CGImageDestination
CGImageDestination抽象化了数据写入任务并且省去了从缓存器管理数据的步骤。CGImageDestination可以呈现一张或多张图片。它包含缩略图和每张图片的属性。创建CGImageDestination对象可以通过(URL, CFData或者Quartz数据)参数,然后添加图片数据,设置图片属性,完成操作后,调用CGImageDestinationFinalize.
设置属性
CGImageDestinationSetProperties添加了属性字典(CFDictionaryRef)。虽然属性的设置是可选的,但是通常都会设置属性来完成自己的需求。举个例子,如果你的程序允许用户添加图片关键字或者改变饱和度,曝光值等,就可将这些值保存在字典中,调用这个函数。
Image I/O定义了一系列可扩展的键来指定压缩质量,背景颜色合成的EXIF字典键,颜色模型值,GIF字典键,尼康和佳能相机键,等等。
在设置属性时,你有两个选择。可以创建CFDictionary对象或者创建NSDictionary对象。然后在传递可选字典到CGImageDestinationSetProperties时将它当做CFDictionaryRef.
float compression = 1.0; //设置压缩比
int orientation = 4; // 设置朝向bottom, left.
CFStringRef myKeys[3];
CFTypeRef myValues[3];
CFDictionaryRef myOptions = NULL;
myKeys[0] = kCGImagePropertyOrientation;
myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
myKeys[1] = kCGImagePropertyHasAlpha;
myValues[1] = kCFBooleanTrue;
myKeys[2] = kCGImageDestinationLossyCompressionQuality;
myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, 3,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
// 记得Release不需要的变量
写入一张图片通过CGImageDestinationRef
你可以创建一个CGImageDestinationRef对象通过以下函数:
CGImageDestinationCreateWithURL,
CGImageDestinationCreateWithData, CGImageDestinationCreateWithDataConsumer
你需要提供所生成图像文件的UTI
创建完成CGImageDestinationRef,你可以调用CGImageDestinationAddImage 或者 CGImageDestinationAddImageFromSource方法添加一张图片,如果目标文件的格式支持多张图片,你可以重复调用,添加完成调用CGImageDestinationFinalize方法,来表明结束添加.此时,不可再次进行添加图片
- (void) writeCGImage: (CGImageRef) image toURL: (NSURL*) url withType: (CFStringRef) imageType andOptions: (CFDictionaryRef) options
{
CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef)url, imageType, 1, nil);
//添加数据和图片
CGImageDestinationAddImage(myImageDest, image, options);
//最后调用,完成数据写入
CGImageDestinationFinalize(myImageDest);
//释放
CFRelease(myImageDest);
}
创建一张动图
Image I/O框架可以用来创建动画图像。创建一个动画形象的时候,调用CGImageDestinationAddImage添加每一帧动画。还必须指定控制动画执行方式的一些属性。
- (void)gifSynthesize{
//1.获取数据
NSMutableArray *tmpArray = [[NSMutableArray alloc]init];
for (int i = 0; i < 4; i ++) {
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"Documents%d",i]];
[tmpArray addObject:image];
}
//2.创建gif文件
NSArray *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentStr = [document objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *textDict = [documentStr stringByAppendingString:@"/gif"];
[manager createDirectoryAtPath:textDict withIntermediateDirectories:YES attributes:nil error:nil];
NSString *path = [textDict stringByAppendingString:@"test1.gif"];
NSLog(@"path:%@",path);
//3.配置gif的属性
CGImageDestinationRef destination;
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)path, kCFURLPOSIXPathStyle, false);
destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, tmpArray.count, NULL);
NSDictionary *frameDic = [NSDictionary dictionaryWithObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:@(0.3),(NSString *)kCGImagePropertyGIFDelayTime,nil] forKey:(NSString *)kCGImagePropertyGIFDelayTime];
NSMutableDictionary *gifParmdict = [NSMutableDictionary dictionaryWithCapacity:2];
[gifParmdict setObject:@(YES) forKey:(NSString *)kCGImagePropertyGIFHasGlobalColorMap];
[gifParmdict setObject:(NSString *)kCGImagePropertyColorModelRGB forKey:(NSString *)kCGImagePropertyColorModel];
[gifParmdict setObject:@(8) forKey:(NSString *)kCGImagePropertyDepth];
[gifParmdict setObject:@(0) forKey:(NSString *)kCGImagePropertyGIFLoopCount];
NSDictionary *gifProperty = [NSDictionary dictionaryWithObject:gifParmdict forKey:(NSString *)kCGImagePropertyGIFDictionary];
//4.单帧添加到图片
for (UIImage *image in tmpArray) {
CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameDic);
}
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)gifProperty);
//释放
CGImageDestinationFinalize(destination);
CFRelease(destination);
}