imageDestination将数据写入的任务抽象化,并且不需要我们直接去管理原始缓冲区中的数据。imageDestination可以表示单个图像或者多个图像。他可以包含缩略图以及每个图像的属性。在适当地创建一个CGImageDestination
之后(URL、CFData对象、Quartz数据消耗者),我们可以添加图像数据并设置图像属性。当添加数据完成之后,调用CGImageDestinationFinalize
函数。
设置imageDestination的属性
在imageDestination中使用函数CGImageDestinationSetProperties添加一个属性字典(CFDictionaryRef)到图像中。尽管设置属性是可选的,但是仍然有很多情形是需要我们去设置的,比如在程序中,如果我们允许用户添加关键字到图像中,或者改变图像饱和度和曝光等等其他值时,那么就需要我们把信息存在选项字典中。
Image I/O定义了一个比较宽泛的keys集合,用来指定诸如压缩质量、背景合成色、exif字典的key、颜色模式值、GIF字典key、尼康和佳能的相机key等等等等。见CGImageProperties。
当在创建字典时,我们有两个选择,我们可以创建一个CFDictionary对象或者也可以创建一个NSDctionary对象,只是在把NSDictionary作为参数传递给CGImageDestinationSetProperties函数时将其转换为CFDictionary类型(CFDictionary和NSDictionary是通用的,或者使用桥接也行)。下面的代码片段中为三个属性分配了键值对,然后把这些属性放进新创建的字典中。由于这只是一个代码片段,并非完整的代码,所以并没有把CFNumber和CFDictionary的销毁代码展示出来。但是当我们自己在写的时候,如果不再使用这些对象需要调用CFRelease来释放它们。
当我们在创建一个键值对字典时,我们需要查看相关的文档(见CGImageDestination Reference和CGImageProperties Reference)来确定相应值的期望数据类型。就像我们在下面的样例中看到的,浮点数等数字通常需要封装到CFNumber对象中。当我们使用CoreFoundation下的字典值时,我们可以在创建字典的时候放入kCFTypeDictionaryKeyCallBacks和kCFTypeDictionaryValueCallBacks值来支持回调。(具体内容详见CFDictionary Reference)
float compression = 1.0; // Lossless compression if available.
int orientation = 4; // Origin is at 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 the CFNumber and CFDictionary objects when you no longer need them.
把图像写入到ImageDestination
为了把图像写入到ImageDestination中,首先就需要通过调用CGImageDestinationCreateWithURL,CGImageDestinationCreateWithData或者CGImageDestinationCreateWithDataConsumer这些函数来创建一个ImageDestination对象。我们需要让获得的图像文件(resulting image file)支持UTI。如果需要的话,我们需要提供一个UTI或者与之等价的常数,我把具体的对应也搬运过来以便查看。
UTI | 常量 |
---|---|
public.image | kUTTypeImage |
public.png | kUTTypePNG |
public.jpeg | kUTTypeJPEG |
public.jpeg-2000 (OS X only) | kUTTypeJPEG2000 |
public.tiff | kUTTypeTIFF |
com.apple.pict (OS X only) | kUTTypePICT |
com.compuserve.gif | kUTTypeGIF |
在创建好了ImageDestination之后,我们就可以直接调用CGImageDestinationAddImage或者CGImageDestinationAddImageFromSource把图像添加到ImageDestination中。如果目标图像文件支持多个图像,我们反复地去添加图像。调用CGImageDestinationFinalize函数告诉Image I/O我们已经结束添加图像了。一旦结束之后,我们就不能添加任何数据到ImageDestination。
下方代码展示了我们如何去实现一个写入图像文件的方法。尽管这个方法是告诉我们如何通过Objective-C来使用ImageDestination,我们依然可以很简单的在C程序的函数中使用ImageDestination。
- (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。而且还必须要指定动画如何展示的属性参数。
下方代码展示了如何创建一个动态PNG图像。首先创建一对儿字典来保存动画属性。第一个字典指定动态的png图像需要在最后一帧结束之前应该循环的时间。第二个字典指定了在执行序列中每个帧的延迟。在创建了ImageDestination之后,该代码设置了目标图像的文件属性,然后一次添加一帧。最后在动态PNG结束之后调用CGImageDestinationFinalize。
let loopCount = 1
let frameCount = 60
var fileProperties = NSMutableDictionary()
fileProperties.setObject(kCGImagePropertyPNGDictionary, forKey: NSDictionary(dictionary: [kCGImagePropertyAPNGLoopCount: frameCount]))
var frameProperties = NSMutableDictionary()
frameProperties.setObject(kCGImagePropertyPNGDictionary, forKey: NSDictionary(dictionary: [kCGImagePropertyAPNGDelayTime: 1.0 / Double(frameCount)]))
guard let destination = CGImageDestinationCreateWithURL(fileURL, kUTTypePNG, frameCount, nil) else {
// Provide error handling here.
}
CGImageDestinationSetProperties(destination, fileProperties.copy() as? NSDictionary)
for i in 0..<frameCount {
autoreleasepool {
let radians = M_PI * 2.0 * Double(i) / Double(frameCount)
guard let image = imageForFrame(size: CGSize(width: 300, height: 300)) else {
return
}
CGImageDestinationAddImage(destination, image, frameProperties)
}
}
if !CGImageDestinationFinalize(destination) {
// Provide error handling here.
}