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 ReferenceCGImageProperties Reference)来确定相应值的期望数据类型。就像我们在下面的样例中看到的,浮点数等数字通常需要封装到CFNumber对象中。当我们使用CoreFoundation下的字典值时,我们可以在创建字典的时候放入kCFTypeDictionaryKeyCallBackskCFTypeDictionaryValueCallBacks值来支持回调。(具体内容详见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中,首先就需要通过调用CGImageDestinationCreateWithURLCGImageDestinationCreateWithData或者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.
}

results matching ""

    No results matching ""