Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

Using the ImageIO Framework with Mac OS X 10.4 Tiger

The ImageIO framework, introduced in Mac OS X 10.4 Tiger, allows applications to read and write popular image file formats. ImageIO works in conjunction with Quartz and is designed for maximum performance, convienient metadata access, and color management. As the definitive way to access image data on Mac OS X, you should consider using ImageIO in your application as a high-performance substitute for Image Importers or other image handling libraries.

This article gets you started working with ImageIO, shows how to read and write data to and from Quartz, and points to some of the capabilities in working with Core Image that you can explore further on your own.

What is ImageIO?

ImageIO serves three main functions:

  • Supply image data for use in Quartz routines. For example, the ImageIO framework can read an image from a file, and prepare the data for use with Quartz routines.
  • Move image data from Quartz to some other destination. For example, ImageIO can write image data back to a file, or to a CFData object.
  • Supply image data to another part of your application. For example, after reading image data into Quartz, you can supply it to Core Image routines.

The ImageIO framework defines two opaque types, CGImageSourceRef, and CGImageDestinationRef, which are used to move data into, and out of Quartz, respectively. These two types can be used only for working with image data (as opposed to PDF data). They are available only in Mac OS X 10.4 and later.

The ImageIO framework is the preferred way to work with image data, and you should use it in your projects whenever you can. It supports a rich set of image formats, including the usual JPEG and other web standard formats. It also supports HDR, and raw camera data. For all the supported image formats, ImageIO provides the fastest decoders and ecoders for the Mac OS X platform.

Other benefits of using the ImageIO framework include: Incremental loading of images, metadata support, and effective use of decompression caching.

Using the ImageIO Framework

Although documented as part of Quartz 2D, the ImageIO framework is actually located within the ApplicationServices framework. Separating ImageIO out of Quartz 2D means that you can link to it in your application without bringing in the entire Quartz 2D API.

Adding the ImageIO framework to your project is easy. First, open the project you want to work with. Then, follow these steps:

  1. Right-click on the Frameworks folder in the project window.
  2. Choose Add > Existing Frameworks from the menu.
    The File Open sheet will appear, starting in the default location: /System/Library.
  3. Click on the Frameworks folder.
  4. From the Frameworks folder, select ApplicationServices.framework.
  5. Click Add.

In the Properties sheet, make sure your project's target is checked and click Add.

To use ImageIO framework routines in your code, import the header file:

#import <ApplicationServices/ApplicationServices.h>

Getting Image Data into Quartz 2D

Before we look at how to read image data with the ImageIO framework, let's take a look at the image formats we can work with.

Determining the Supported Uniform Type Identifiers (UTI) for CGImageSourceRef Objects

The ImageIO framework allows you to work with a multitude of different image formats, so you don't have to worry about knowing how to read each format on your own. You can use the function CGImageSourceCopyTypeIdentifiers to get an array of the supported uniform type identifiers on the system. Listing 1 shows some sample code that will retrieve an array of the supported UTIs, and then print that array to the debugger console:

CFArrayRef types = CGImageSourceCopyTypeIdentifiers();
CFShow(types);

Listing 1:Reading Supported CGImageSourceRef UTIs

Table 1 shows the supported UTIs returned from the CGImageSourceCopyTypeIdentifiers function on a system running Mac OS X 10.4.8:

public.jpeg com.compuserve.gif public.png public.jpeg-2000
com.nikon.raw-image com.pentax.raw-image ( com.sony.arw-raw-image com.adobe.raw-image
public.tiff com.canon.crw-raw-image com.canon.cr2-raw-image com.canon.tif-raw-image
com.sony.raw.image com.olympus.raw-image com.konicaminolta.raw-image com.panasonic.raw-image
com.fuji.raw-image com.adobe.photoshop-image com.adobe.illustrator.ai-image com.adobe.pdf
com.microsoft.ico com.microsoft.bmp com.truevision.tga-image com.sgi.sgi-image
com.apple.quicktime-image com.apple.icns com.apple.pict com.apple.macpaint-image
com.kodak.flashpix-image public.xbitmap-image com.ilm.openexr-image public.radiance

Table 1: Supported CGImageSourceRef UTIs on Mac OS X 10.4.8

These are all the uniform type identifiers for the types of image data that the ImageIO framework knows how to read. Notice that ImageIO can read some camera raw formats as well as the usual formats like jpeg, png, and gif.

Creating a CGImageSourceRef Object

The CGImageSourceRef data type encapsulates the information needed to move image data into Quartz. Once you have created and read image data into a CGImageSourceRef object, you can then pass it to CGImageSoure functions. CGImageSource functions let you to perform operations on the image data, such as getting thumbnails, image properties, and of course, creating a CGImageRef from the source image data.

Listing 2 shows the four functions in the ImageIO framework that let you create a CGImageSourceRef object:

CGImageSourceRef CGImageSourceCreateWithData(CFDataRef data, CFDictionaryRef options);
CGImageSourceRef CGImageSourceCreateWithURL(CFURLRef url, CFDictionaryRef options);
CGImageSourceCreateIncremental(CFDictionaryRef options);
CGImageSourceRef CGImageSourceCreateWithDataProvider(CGDataProviderRef provider, CFDictionaryRef options);

Listing 2: CGImageSourceRef Creation APIs

Code listing 3 shows the simplest case of creating a CGImageSourceRef first from an NSData object, and then from a file. After creating the CGImageSourceRef object, the code then uses it to create a CGImageRef object.

/* Create a CGImageSourceRef from raw data */
CGImageRef CreateCGImageFromData(NSData* data)
{
    CGImageRef        imageRef = NULL;
    CGImageSourceRef  sourceRef;

    sourceRef = CGImageSourceCreateWithData((CFDataRef)data, NULL);
    if(sourceRef) {
        imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
        CFRelease(sourceRef);
    }

    return imageRef;
}

/* Create a CGImageSourceRef from a file */
CGImageRef CreateCGImageFromFile(NSString* path)
{
    NSURL*            url = [NSURL fileURLWithPath:path];
    CGImageRef        imageRef = NULL;
    CGImageSourceRef  sourceRef;

    sourceRef = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
    if(sourceRef) {
        imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
        CFRelease(sourceRef);
    }

    return imageRef;
}

Listing 3: Creating a CGImageSourceRef Object

After creating the CGImageSourceRef object, the code in listing 3 uses the CGImageSourceCreateImageAtIndex function to create a CGImageRef object. Let's look at the signature of that function:

CGImageRef CGImageSourceCreateImageAtIndex(
   CGImageSourceRef isrc, 
   size_t index, 
   CFDictionaryRef options
);

The first argument is the CGImageSourceRef object, which we created from either a URL, or a file. An CGImageSourceRef can contain data for more than one image. That is the purpose of the second argument. The index argument is a zero-based location of the image within the image source object. The options argument allows you to specifiy more image creation options using a CFDictionaryRef. This argument is optional, and may be NULL, as shown in listing 3.

Another way to create an CGImageSourceRef object is to use the CGImageSourceRefCreateIncremental function. With this function, the resulting CGImageSourceRef is empty. You then add data using either the CGImageSourceUpdateDataProvider function, or the CGImageSourceUpdateData function. This way, you create the image as data becomes available, for example, an image viewed over the web.

In listing 3, we saw that an image source can contain more than one image. This is shown by the index argument of the CGImageSourceCreateImageAtIndex function. How do you determine how many images an image source contains? With the CGImageSourceGetCount function. This function takes one argument, a CGImageSourceRef object:

size_t CGImageSourceGetCount(
   CGImageSourceRef isrc
);

Table 2 shows the additional functions you can use for getting information about an image source:

FunctionPurpose
CGImageSourceGetTypeIDReturns the unique type identifier for the image source.
CGImageSourceGetTypeReturns the UTI for the image source.
CGImageSourceCopyTypeIdentifiersReturns an array of supported UTIs.
CGimageSourceGetCountReturns the number of images (not including thumbnails) for the image source.
CGImageSourceCopyPropertiesReturns a dictionary containing the properties of the image source.
CGImageSourceCopyPropertiesAtIndexReturns a dictionary containing the properties of the image at a specified index.
CGImageSourceGetStatusReturns the status of the image source.
CGImageSourceGetStatusAtIndexReturns the status of the image at a specified index.

Table 2: Information Retrieval functions in the ImageIO Framework

Moving Image Data Out of Quartz 2D

First, let's look at the image formats that ImageIO knows how to write.

Determining the Supported UTIs for CGImageDestinationRef Objects

The CGImageDestinationCopyTypeIdentifiers function retrieves all the UTIs for the kinds of image data that the ImageIO framework knows how to write. Listing 4 shows some code to call CGImageDestinationCopyTypeIdentifiers and log the resulting array to the debugger console:

CFArrayRef types = CGImageDestinationCopyTypeIdentifiers();
CFShow(types);

Listing 4: Reading Supported CGImageDestination UTIs

Table 3 shows the results of calling CGImageDestinationCopyTypeIdentifiers on a system running Mac OS X 10.4.8:

public.jpeg com.compuserve.gif public.png public.jpeg-2000
public.tiff com.adobe.photoshop.image com.adobe.pdf com.microsoft.bmp
com.truevision.tga-image com.sgi.sgi-image com.apple.pict com.ilm.openexr-image

Table 3: Supported CGImageDestinationRef UTIs on Mac OS X 10.4.8

Creating a CGImageDestinationRef Object

After you have read the image data and performed the required operations on it, you will want to move the data out of Quartz. The CGImageDestinationRef data type is used to accomplish this task. CGImageDestinationRef objects are similar to CGImageSourceRef objects in that they can represent a variety of image data, including one or more images, thumbnails, as well as storing various properties.

Similar to creating a CGImageSourceRef object, there are three ImageIO functions for creating a CGImageDestinationRef object:

CGImageDestinationRef CGImageDestinationCreateWithData(CFMutableDataRef data, 
	CFStringRef type, size_t count, CFDictionaryRef options);
CGImageDestinationRef CGImageDestinationCreateWithURL(CFURLRef url, CFStringRef type, 
	size_t count, CFDictionaryRef options);
CGImageDestinationRef CGImageDestinationCreateWithDataConsumer(CGDataConsumerRef consumer, 
	CFStringRef type, size_t count, CFDictionaryRef options);

A CGImageDestinationRef object can contain one or more images, as you can see from the count argument of the creation functions. Once you have created a CGImageDestinationRef object, you can add images to it using the CGImageDestinationAddImage function.

void CGImageDestinationAddImage(
   CGImageDestinationRef idst,
   CGImageRef image,
   CFDictionaryRef properties
);

You can also add an image to a destination directly from a source:

void CGImageDestinationAddImageFromSource(
   CGImageDestinationRef idst,
   CGImageSourceRef image,
   size_t index,
   CFDictionaryRef properties
);

Note the two functions are almost identical, except when adding from a CGImageSourceRef object, you must specify the index within the source container, because a source can also contain more than one image.

After you have added all the images to the destination, you must call CGImageDestinationFinalize to write the data to the destination object (URL, data, or data consumer). Once you call this function, you cannot add any more images to the destination.

void CGImageDestinationFinalize(
   CGImageDestinationRef idst
);

Exchanging Image Data Between Quartz 2D and Other
Mac OS X Imaging Technologies

Reading and writing data to and from Quartz is only part of the picture. Mac OS X has many other imaging technologies built in that you can take advantage of. For example, you can take full advantage of the Core Image API to perform near real-time video and still image processing on Quartz image data.

In this section we will branch out from ImageIO just a bit, to demonstrate how you can exchange image data between different imaging technologies on Mac OS X.

Working with Quartz Images in Core Image

The Core Image framework was built into Mac OS X 10.4 to support near real-time operations on video and still image data. You can use Core Image functions to correct color, blur and sharpen, and to perform geometric distortions. The Core Image API is built to use its own data types, namely the CIImage type; it cannot operate on Quartz images, which are of type CGImageRef. You will have to convert your Quartz CGImageRef objects to Core Image CIImage objects.

Quartz 2D does not provide any functions for performing the conversion. They are instead found in the Core Image framework itself, implemented as methods on CIImage. Two methods provide a way to create a CIImage object from a CGImageRef object:

+ (CIImage *)imageWithCGImage:(CGImageRef)image;
+ (CIImage *)imageWithCGImage:(CGImageRef)image options:(NSDictionary *)d;

Converting back to a Quartz image is done using a method on the Core Image CIContext class:

- (CGImageRef *)createCGImage:(CIImage *)im fromRect:(CGRect)r;

For More Information

Updated: 2006-12-05