Chapter 9. Importing and Exporting Images

Computers are riddled with image files in popular formats such as JPEG, PNG, GIF, TIFF, and many others. In addition there are a number of powerful tools available on Mac OS X that graphic artists can use to create image resources for an application. Fortunately, Mac OS X makes it remarkably easy to import many kinds of image files into CGImages that you can draw in your application.

In a similar fashion, it is nice to be able to take the graphics you have created in your application and export them to image files for use by others. The operating system also includes technologies that let you take the images your application creates and export them into graphics files on disk.

This chapter looks at two of the technologies, QuickTime and Image I/O, that Mac OS X offers for importing and exporting images and discusses some of the features that these libraries offer. Some basic code samples are offered that demonstrate the fundamentals of using these technologies to read and write image files.

QuickTime is well known as a technology for recording and presenting multiple media types including video and sound. QuickTime also includes components for reading and writing still image files in a number of formats. Using a few lines of QuickTime code in your application will make it able to read and write image files in several different formats.

Image I/O is a technology Apple introduced in Mac OS X 10.4 Tiger. The Image I/O API provides a mechanism for importing and exporting images from a broad variety of image file types. The library supports many formats including open-source-based floating point image file formats (a.k.a. High Dynamic Range images) like OpenEXR and LogLUV TIFF images. Image I/O includes the ability to import individual images from files in formats that support storing more than one picture per file. The native compressors and decompressors in Image I/O are highly optimized and allow for the progressive loading of image data should the application desire that. The library also includes routines for working with and creating image thumbnails and can help your application read and write image meta data.

Image I/O understands how to make use of the QuickTime importers and exporters as a fallback mechanism. This means that if Image I/O is asked to read or write a file it does not understand natively, it can make use of any QuickTime components that can work with that format. In this way, applications that use the Image I/O API capitalize on the benefits of both libraries.

Image I/O is the preferred mechanism for importing and exporting images on versions of the operating system starting with Mac OS X 10.4 Tiger. This technology is used by all the system services on that operating system that work with images. If your application needs to support older versions of the operating system, you can fall back to using QuickTime, but the additional functionality found in Image I/O will add value when the library is available.

Using QuickTime

QuickTime can be a very imposing API. Often it can be difficult to sift through the many services that QuickTime provides to find the functionality you are interested in. Fortunately, understanding how to import and export images with QuickTime only requires a small number of easily understood routines. This is the functionality in QuickTime that this chapter focuses on.

Import Image Example

QuickTime is a component-based software system. The power of QuickTime comes from having a large number of software components that understand a variety of media formats. To import images, your application must select a graphics import component from the set of components that make up QuickTime and then ask that component to read the image data for you.

An interesting advantage of the QuickTime system is the fact that enterprising software developers can create their own graphics components, including image importers, and extend the system with them. Because all graphics import components can be treated uniformly, if your application understands how to use read images with QuickTime, it can take advantage of all the supported image file types that QuickTime understands.

To use a component, your application must first indicate to the QuickTime library where the image data resides. To do that, the application must first create an entity called a DataRef. A DataRef is little more than a block of memory, a handle, that describes the location of the image data. The type of information contained in the DataRef depends on where the image data lives. If the computer should read the data from a file, the DataRef will contain some kind of file reference (an FSRef, an FSSpec, a URL, or something else). If the data is to be taken from memory, then the DataRef will contain the raw image data itself. The QuickTime API includes a number of routines that help you create DataRefs from popular sources. This example takes the image data from a file and will use the routine QTNewDataReferenceFromFSRef.

A DataRef is usually associated with a type code that indicates what kind of information it contains. The type of the data ref is a 32-bit integer (a four character code) commonly called an OSType. For example, if the DataRef contains an alias handle that points to a file, the type code associated with the DataRef will be ‘alis’.

After creating the DataRef, your application must select an appropriate graphics import component for the image data. If you know the data format of your image, you can ask QuickTime to create an import component that understands that format. For example, if you know that your image data is in PNG format, you might use the OpenADefaultComponent routine to create a PNG importer:

OpenADefaultComponent( GraphicsImporterComponentType,
    kQTFileTypePNG, importer);

Most image file formats have distinguishing features that make it easy for the computer to examine the data and determine if it is in a particular format. The most convenient way to select a graphics importer from QuickTime is to use the routine GetGraphicsImporterForDataRef. When you call this routine, the computer traverses the list of all registered graphics importers and asks each to try and determine whether or not it can decode the image data. If it finds an importer that claims to be able to read the data, it will return that component to your application.

Note

Import Image Example

In some cases, the computer may not be able to determine what kind of importer to use based on the image data itself. This can be particularly problematic when the image data resides in memory. In this case, you can actually tag the DataRef with information that can help the system decide which importer to use. For more information, read “Technical Note 1195 : Tagging Handle and Pointer Data References in QuickTime” at the URL:

http://developer.apple.com/technotes/tn/tn1195.html

After you have selected an appropriate component, getting a CGImage out of that component is as easy as calling GraphicsImportCreateCGImage. This routine simply asks the QuickTime graphics importer to take whatever steps are necessary to load the image and create a CGImage from it.

All of these steps are summarized in code Listing 9.1. This routine is taken from the Chapter 9 sample code project called QuickTime Import. The application is very simple. It allows you to open any image that QuickTime has an importer for and displays that image in a window. The application contains some code for setting up the application and managing the open dialog. That code will not be repeated here. Listing 9.1 simply provides the code that imports image files into a CGImage.

Example 9.1. Importing a CGImage Using QuickTime

void OpenImageFile(FSRef &whichFile)
{

    Handle fileDataRef = NULL;
    OSType dataRefType = 0;
    ComponentInstance graphicsImporter = NULL;
    CGImageRef image = NULL;

    // Create a DataRef from the file reference that was passed in.
    QTNewDataReferenceFromFSRef(&whichFile, 0L,
            &fileDataRef, &dataRefType);

    if(fileDataRef) {

            // Find an appropriate graphics importer for the
            // given data ref.
           GetGraphicsImporterForDataRef(fileDataRef,
                   dataRefType, &graphicsImporter);
    }

    // If the system found an image importer for us to use then
    // try to extract an image from it.
    if(graphicsImporter) {
           GraphicsImportCreateCGImage(graphicsImporter, &image, 0);

           // we're done with the component so close it out
           CloseComponent(graphicsImporter);

           // We're done with the DataRef too
           DisposeHandle(fileDataRef);
    }

    // if we got an image, then create a new window and put
    // the image into it.
    if(image) {
            CreateImageWindow(image);
            CGImageRelease(image);
            image = NULL;
    }
}

The sample project prompts the user to select an image file and then opens a window that displays the image found in the file. After selecting the file, the computer translates the user’s selection into an FSRef, which is passed to the OpenImageFile routine.

Listing 9.1 uses the routine QTNewDataReferenceFromFSRef to create a DataRef from the FSSpec that is passed to the routine. It gets back both the DataRef and the OSType that describes the data it contains. If that routine returns successfully, the computer then tries to create a graphics import component by examining the data from the file using the routine GetGraphicsImporterForDataRef.

If the computer finds a graphics importer, it then calls the routine GraphicsImportCreateCGImage. After creating the image, the code closes the importer and disposes of the DataRef. The code takes the resulting image and passes it to a routine, CreateImageWindow, that opens a new window to display the image.

Export Image Example

Exporting an image from QuickTime is just as easy as importing one. The basic technique is to create a graphic exporter for the type of file you would like to export your image into. You provide the graphic exporter with a DataRef that describes where you would like the computer to put the compressed data. You also provide the CGImage that you would like to export. Once everything is set up, exporting the data is as easy as calling GraphicsExportDoExport.

If you follow the steps listed, you will get a graphics export with the default settings of the graphics exporter. If you want to customize the export settings, QuickTime provides a number of routines that begin with GraphicsExportSetSettings to assist you. The easiest way to set up the settings, however, is to use the routine GraphicsExportRequestSettings to put up a dialog that will modify the settings of the exporter.

The technique of exporting a CGImage into a PNG file is demonstrated by the QuickTime Export sample code. The routine that exports the image through QuickTime is show in Listing 9.2:

Example 9.2. Exporting an Image via QuickTime

void ExportPNGImage(CGImageRef image)
{

    Handle      dataRef;
    OSType      dataRefType;

    GraphicsExportComponent       exporter;
    unsigned long                 exportSize;

    // Open a graphics exporter for PNG files and point it at our image.
    OpenADefaultComponent(GraphicsExporterComponentType,
           kQTFileTypePNG, &exporter);
    GraphicsExportSetInputCGImage(exporter, image);

    // Then request the exporter's settings from the user
    GraphicsExportRequestSettings(exporter, NULL, NULL);

    // We're all set up to save. Ask the user where we
    // should save the file
    CFURLRef saveLocation = CopySaveLocation();
    if(NULL != saveLocation) {
            // Create a data ref to our destination.
            QTNewDataReferenceFromCFURL(saveLocation, 0,
                    &dataRef, &dataRefType);
            CFRelease(saveLocation);
            saveLocation = NULL;

            // Set the destination in the exporter and export the image
            GraphicsExportSetOutputDataReference(exporter,
                    dataRef, dataRefType);
            GraphicsExportDoExport(exporter, &exportSize);

            DisposeHandle(dataRef);
            dataRef = NULL;
    }

    CloseComponent(exporter);
    exporter = NULL;
}

This code sample should look familiar at this point. The code begins by opening a QuickTime component. In this case, you are explicitly asking for a graphics exporter component capable of writing PNG files. After you have the component, you tell the exporter that we will be exporting the CGImage.

Next, call GraphicsExportSetInputCGImage to ask QuickTime to put up a settings dialog for the PNG exporter so the user can change the exporter’s settings.

The routine CopySaveLocation puts up a save file dialog and returns the URL of the location the user has chosen for saving the document. You use this URL to create a destination DataRef for QuickTime. After telling the export component where it should save the image, you call GraphicsExportDoExport to save the new file. After that the code simply cleans up and returns.

Image I/O

Image I/O is part of Core Graphics and follows the Core Graphics API conventions. Strictly speaking, it is not part of Quartz 2D, but it does integrate closely with Quartz 2D and is an invaluable asset. The two primary opaque data types that make up the Image I/O system are CGImageSource and CGImageDestination. The CGImageSource class represents an abstract source for image data. Conceptually, the image source contains one or more images. An image file, a network stream, or a block of memory are all valid image sources. Each CGImageSource knows how to extract images from a particular source. After creating a CGImageSource, you can ask it to return any of those images as a CGImageRef.

Not surprisingly, the CGImageDestination represents an object that knows how to take one or more CGImages, encode them in a particular format, and store the results, or even transfer them elsewhere. After creating an image destination attached to a storage device, your application can pass it one or more images and then ask the destination to encode and save the graphics.

The image source and image destination classes don’t concern themselves with the details of interacting with the file system or the network. Instead the CGImageSource opaque data type uses the familiar CGDataProvider opaque data type that we explored when creating images, and CGImageDestination uses its companion, the CGDataConsumer opaque data type. These objects offer your application a lot of flexibility in deciding where the image data is coming from. It can read from the network or from a local file, or it can generate the image data on-the-fly if that makes sense.

Import Image Example

The first sample will demonstrate using a CGImageSource. This code performs the same task as the QuickTime example earlier in the chapter. It is included with the sample code as part of an application called ImageIOImport. Listing 9.3 recreates the image import routine from listing 9.1 using a CGImageSource instead of a QuickTime import component:

Example 9.3. Importing an Image with a CGImageSource

void OpenImageFile(FSRef &whichFile)
{

    CFURLRef fileURL = CFURLCreateFromFSRef(NULL, &whichFile);
    // Create a data provider for the image source
    CGDataProviderRef dataProvider =
            CGDataProviderCreateWithURL(fileURL);
    CFRelease(fileURL);
    fileURL = NULL;

    // Create the image source itself
    CGImageSourceRef imageSource = CGImageSourceCreateWithDataProvider(
            dataProvider, NULL);
    CGDataProviderRelease(dataProvider);
    dataProvider = NULL;

    // extract the first image (0 based index) from the image source
    CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource,
            0, 0);
    CFRelease(imageSource);
    imageSource = NULL;

    // if we got an image, then create a new window and put
    // the image into it.
    if(image) {
            CreateImageWindow(image);
            CGImageRelease(image);
            image = NULL;
    }
}

This code sample demonstrates the basic technique of using an image source. It first creates a data provider that points to the image data we want to import. It then creates a data source from that data provider. With the data source in hand, the code simply asks for the first image in that source (the images are indexed starting at 0). When the image has been imported, the computer creates a window to display the results.

Note

imagesimportingimporting imageslistingsImporting an Image with a CGImageSourceImage I/Oimporting imagesImporting an Image with a CGImageSource

This code sample ignores a simplifying utility provided by the Image I/O API. The utility routine, CGImageSourceCreateWithURL, will create a data provider and an image source in one step. We avoided this routine for the code sample to make it clear that you can use any data provider you like.

Image I/O includes additional functionality related to the loading of images. Of particular interest may be the fact that it can retrieve image thumbnails from file formats that provide them, and it supports the incremental loading of large images. Those topics will not be covered here, but you can read more about them in Apple’s documentation.

Export Image Example

It’s just as easy to export an image using Image I/O as it is to read one in. By now the process should be pretty obvious. The duality of creating a CGDataProvider for an image import operation is creating a CGDataConsumer for the results of compressing an image. The CGDataConsumer is responsible for taking the compressed image data and doing something with it, be it storing the data on a disk or writing it into a block of memory. Once you’ve decided what to do with the image data, you create a CGImageDestination that connects to the data consumer. You can pass the images you want to store in the destination to that object, and the system will handle compressing the image and shipping it off to the data consumer. Listing 9.4 demonstrates the technique of writing a CGImage into a file using a CGImageDestination.

Example 9.4. Exporting an Image Using Image I/O

void ExportPNGImage(CGImageRef image)
{

    // We're all set up to save. Ask the user where we should save the
file
    CFURLRef saveLocation = CopySaveLocation();
    if(NULL != saveLocation) {
            // Create a data consumer that saves into the file the user
            // selected
            CGDataConsumerRef dataConsumer =
                    CGDataConsumerCreateWithURL(saveLocation);

            // Create an image destination attached to the data consumer
            CGImageDestinationRef imageDestination =
                    CGImageDestinationCreateWithDataConsumer(
                            dataConsumer, kUTTypePNG, 1, NULL);
            CGDataConsumerRelease(dataConsumer);
            // Add an image to the destination
            CGImageDestinationAddImage(imageDestination, image, NULL);

            // Finish the export
            CGImageDestinationFinalize(imageDestination);
            CFRelease(imageDestination);
            imageDestination = NULL;
    }
}

One interesting point to note about this code sample and Image I/O is that you specify the type of file you want to export using a Uniform Type Identifier (UTI). In this case the UTI we use is kUTTypePng. Uniform Type Identifiers are a mechanism for specifying the type of various and sundry things that have distinguishable types. That’s a bit of a vague description for an odd concept. In this case there are a number of different types of files you might export to using Image I/O. You use image file type UTIs to tell Image I/O what type of file you want to use. For more information on UTI’s, you can read Apple’s documentation on the subject at

<http://developer.apple.com/documentation/Carbon/Conceptual/understanding_utis/understand_utis_intro/chapter_1_section_1.html>

Note that after adding the image, the code calls CGImageDestinationFinalize. If you do not call this method, the computer may not finish writing the data to the output properly. Also once you have called this routine, you will be unable to add more files to the CGImageDestination.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.230.102