TweetFollow Us on Twitter

Apr 00 QTToolkit

Volume Number: 16 (2000)
Issue Number: 4
Column Tag: QuickTime Toolkit

Quick on the Draw

By Tim Monroe

Using QuickTime's Graphics Importers and Exporters

Introduction

QuickTime is perhaps best known for its ability to play movies, but over the years it has gained the ability to handle quite a large number of other types of media, including text, MIDI data, virtual reality, sprite animation, vector-defined images, and streamed audio and video. QuickTime also provides a very powerful set of capabilities for managing still images (or graphics) stored in any of a large number of common image file formats. For instance, you can use QuickTime's application programming interfaces to open and display PICT, TIFF, JPEG, BMP, PhotoShop, GIF, PNG, and FlashPix files (to name only a handful of the supported formats). Moreover, you can use QuickTime to save an image in any one of a number of image formats, including PICT, TIFF, JPEG, BMP, PhotoShop, and PNG. These two aspects of handling images - reading them from files and writing them to files - are handled by parts of QuickTime known as graphics importers and graphics exporters. In this article, we'll investigate the most fundamental ways of using graphics importers and exporters to manipulate still images.

The most obvious benefit that graphics importers and exporters provide is to insulate our applications from the nitty-gritty details of any particular image file format and image compression scheme. We can use exactly the same routines to open and display a JPEG file as we use to open and display a PICT file. In fact, our application never really needs to know what kind of image file it has opened. We can always determine this information about the image file if we want to, but for most uses that's information we simply don't need. Graphics importers and exporters provide a host of other services. We can change the image matrix used by a graphics importer when drawing an image, thereby drawing the image at a different size or orientation. We can also change the graphics mode used by a graphics importer; this allows us, for instance to composite the image on top of other images or on top of QuickTime movies.

In this article, we'll see how to open an image file, read the image from it, and draw that image in a window on the screen. We'll also see how to alter the image matrix to draw the image at different sizes and orientations. Finally, we'll see how to export the image into some other type of image format. To wrap this all up into a neat package, let's set ourselves the goal of replicating as much of the functionality of QuickTime's PictureViewer application as possible. Figure 1 shows PictureViewer's Image menu, and Figure 2 shows the Image menu of our application, which we'll call QTGraphics.


Figure 1. The Image menu from the PictureViewer application.


Figure 2. The Image menu from the QTGraphics application.

As you can see, these menus are quite similar. The main difference is that QTGraphics adds a check mark in the menu to indicate the current size of the image window. This is a useful feature and it's relatively simple to add, given the way we've chosen to handle our images. Another difference is that QTGraphics does not support the "Fill Screen" menu command. The reason for this is simply that fitting an image to the current screen size is extremely platform-specific and not terribly enlightening, at least in terms of using graphics importers. So I've left the implementation of that feature as an exercise for the reader. Finally, the Image menu of QTGraphics contains the Export item, which PictureViewer places in its File menu (where it more properly belongs).

Along the way, we'll take a few small detours to explore some other features of graphics importers and exporters. We'll consider briefly how to handle image files that contain more than one image, and we'll take a quick look at how to distinguish image files from other kinds of files. So let's get started.

Importing Images

A graphics import component, or graphics importer, is a software component that provides services for handling still images. Graphics importers were introduced in QuickTime version 2.5 to facilitate opening and drawing images. Subsequent versions of QuickTime have added new graphics importers and expanded the capabilities of existing importers. For instance, QuickTime 4 added the ability to work with files that contain multiple images or multiple image layers (such as FlashPix files or PhotoShop files).

It's very simple to open an image file and display the image (or images) contained in the file using the application programming interfaces provided by graphics importers. Suppose that we've got a file system specification (that is, an FSSpec) that picks out an image file. Then we can use the QTGraph_ShowImageFromFile function defined in Listing 1 to display that image in a window on the screen.


Listing 1: Opening an image file and displaying its image.

QTGraph_ShowImageFromFile


void QTGraph_ShowImageFromFile (FSSpec *theFSSpec)
{	
	GraphicsImportComponent		myImporter = NULL;
	Rect						myRect;
	WindowPtr					myWindow = NULL;

	GetGraphicsImporterForFile(theFSSpec, &myImporter);
	if (myImporter != NULL) {
		GraphicsImportGetNaturalBounds(myImporter, &myRect);
		MacOffsetRect(&myRect, 50, 50);
		myWindow = NewCWindow(NULL, &myRect, NULL, true,
							noGrowDocProc, (WindowPtr)-1L, true, 0);
		if (myWindow != NULL) {
		GraphicsImportSetGWorld(myImporter, (CGrafPtr)myWindow, NULL);
			GraphicsImportDraw(myImporter);
			CloseComponent(myImporter);
		}
	}
}

QTGraph_ShowImageFromFile first calls the GetGraphicsImporterForFile function, which opens the specified image file and determines which graphics import component (if any) can handle the image data contained in that file. If GetGraphicsImporterForFile finds a suitable graphics importer, it returns an instance of that importer in the myImporter parameter. We'll use that graphics importer instance, of type GraphicsImportComponent, in all our subsequent calls to graphics importer functions. The type GraphicsImportComponent is simply a component instance:

typedef ComponentInstance 	GraphicsImportComponent;

As a result, we shall eventually need to close the component instance by calling CloseComponent (as shown in the final line of Listing 1).

GetGraphicsImporterForFile uses a fairly straightforward algorithm for deciding what kind of image a file contains. First, on Macintosh systems, it looks at the file's type. For instance, if the specified file is of type 'PICT', then GetGraphicsImporterForFile opens an instance of the PICT image importer. Otherwise, if it cannot find a suitable importer based on the file's type, GetGraphicsImporterForFile inspects the filename extension, if it has one. So, for example, a file whose name ends with '.gif' would be imported as a GIF image. Finally, if neither of these steps reveals the type of the image in the file, then GetGraphicsImporterForFile sequentially opens an instance of each available graphics importer and instructs it to examine some of the data in the file to see whether it can handle that kind of data. (Image files often contain a header that helps identify the type of image data contained in them.) This process continues until some importer reports that it can handle the image data or until the list of available importers is exhausted. As you can imagine, this final step (called validating the image data) might take a fair amount of time to complete. If you are fairly certain that either the file type or filename extension will tell you what kind of image file you've got (perhaps because they are files you had previously created), then you can use the function GetGraphicsImporterForFileWithFlags, specifying the kDontUseValidateToFindGraphicsImporter flag to suppress the validation step.

For a single file, seeing whether GetGraphicsImporterForFile returns a non-NULL importer - even allowing it to perform the validation step - is a perfectly reasonable way of determining whether that file contains an image (as shown in Listing 2). But for a large number of files, using GetGraphicsImporterForFile in this way would be too slow. Later on, we'll see a faster way of finding the image files among a large number of files.


Listing 2: Determining whether a file contains an image.

QTUtils_IsImageFile

Boolean QTUtils_IsImageFile (FSSpec *theFSSpec)
{	
	GraphicsImportComponent		myImporter = NULL;

	GetGraphicsImporterForFile(theFSSpec, &myImporter);
	if (myImporter != NULL)
		CloseComponent(myImporter);

	return(myImporter != NULL);
}

Let's return to Listing 1. If GetGraphicsImporterForFile returns a graphics importer instance to us, then we want to use it to draw the image in a window on the screen. We first call GraphicsImportGetNaturalBounds to determine the natural size of the image, which we shall use to set the size of the image window. GraphicsImportGetNaturalBounds always returns a rectangle whose top-left corner is at the origin - that is, at the point (0, 0). We then offset the rectangle a little bit down and to the right, so that (on Macintosh computers) the window's title bar is not obscured by the menu bar. Then we call NewCWindow to create a window in which we can draw the image.

Before we can draw the image into that window, we need to set the graphics port of the graphics importer instance, by calling GraphicsImportSetGWorld (in the same way that, in past articles, we needed to call SetMovieGWorld to set the graphics port of a movie). Finally, we can draw the image into the window by calling GraphicsImportDraw. Figure 3 shows the result of calling QTGraph_ShowImageFromFile for the sample image installed in the QuickTime folder.


Figure 3. An image file opened using the QTGraph_ShowImageFromFile function (Macintosh).

As you might imagine, QTGraph_ShowImageFromFile runs equally well under Windows. Figure 4 shows the result of executing it on a Windows computer.


Figure 4. An image file opened using the QTGraph_ShowImageFromFile function (Windows).

So far then, using barely a handful of graphics importer functions, we've opened an image file and drawn it at its natural size in a window on the screen. This is impressive. But of course the QTGraph_ShowImageFromFile function doesn't represent a complete solution for opening and displaying image files. For one thing, the title bar of the image window doesn't have a name, which violates good user interface practice. More importantly, the image won't be redrawn if the image window is wholly or partially obscured by some other window and then uncovered. If we want to replicate the functionality of the PictureViewer application, we need to do a bit more work to make things work just right. We could of course start from scratch and develop a new image-handling application, but that might be a lot of work. Instead, let's see how easy it would be to modify the QTShell application that we've been considering for the past several QuickTime Toolkit articles to handle image files as well as movie files. I think we'll be pleasantly surprised.

Expanding the Application Framework

Believe it or not, the basic QTShell application can already open and display image files, without directly using any graphics importer functions. Try it: launch QTShell and select the Open menu item in the File menu; then select an image file that you've got lying around and, voilà, the image opens in a window. How can this be? And more to the point: if the basic application we've developed so far can already open image files, why bother with graphics importers?

Let's consider first how it is that QTShell manages to open and display image files. When the user selects the Open item in the File menu, our function QTFrame_OpenMovieInWindow calls the function StandardGetFilePreview to display to the user a list of files that our application can open. The types of files displayed in that list are determined by the type list passed to StandardGetFilePreview. QTShell passes just one file type, kQTFileTypeMovie, like this:

OSType 						myTypeList = kQTFileTypeMovie;
short							myNumTypes = 1;
StandardFileReply		myReply;

StandardGetFilePreview(NULL, myNumTypes, (ConstSFTypeListPtr) &myTypeList, &myReply);

The kQTFileTypeMovie constant tells StandardGetFilePreview to list any kinds of files that QuickTime can open as a movie. On the Macintosh, this includes all files whose file type is 'MooV', and on Windows it includes all files whose file extension is '.mov'. On both platforms, this constant also causes StandardGetFilePreview to list any files that can be translated into the QuickTime movie format using a movie importer, which is a software component that can convert certain kinds of files into movies. For example, QuickTime includes a movie importer that can convert AIFF audio files into sound-only QuickTime movies. So AIFF files will be shown in the list of files displayed by StandardGetFilePreview when we include kQTFileTypeMovie in the file type list.

At this point, if the user selects one of the files in the list displayed by StandardGetFilePreview, QTShell calls the OpenMovieFile and NewMovieFromFile functions to get the movie contained in the selected file. If NewMovieFromFile determines that the selected file is not a QuickTime movie file, then it looks for a movie importer that can handle the data in the file. For an image file, NewMovieFromFile invokes a movie importer that opens the image file (using, you guessed it, a graphics importer) and converts it into a movie. The resulting movie has a default duration of 1/15 second.

The net result of all this importing and converting is that what began life as still image is now being treated by QTShell as a movie. In particular, our application framework happily attaches a movie controller bar to the converted image, as shown in Figure 5. This provides an easy way to resize the image, to be sure, but it's probably not the behavior we'd like our applications to exhibit. After all, the user probably thought that he or she was opening an image file, not a movie.


Figure 5. An image file opened by a movie importer.

So we want to expand our basic application framework so that it can handle still images without importing them as movies. That is to say, we want to be able to open and manage still images using the graphics importer routines discussed earlier. This involves sprinkling a grand total of just over twenty lines of code here and there in our existing framework files.

The first thing we need to do is add a field to the window object record associated with each window that our application opens. The window object record now looks like this:

typedef struct {
	WindowReference						fWindow;	
	Movie										fMovie;
	MovieController 					fController;
	GraphicsImportComponent 		fGraphicsImporter;
	FSSpec										fFileFSSpec;
	short										fFileResID;
	short										fFileRefNum;
	Boolean									fCanResizeWindow;
	Boolean									fIsDirty;
	Boolean									fIsQTVRMovie;
	QTVRInstance							fInstance;
	OSType										fObjectType;
	Handle										fAppData;
} WindowObjectRecord, *WindowObjectPtr, **WindowObject;

The field fGraphicsImporter holds the graphics importer instance, if any, that is being used to manage the still image in the specified window. If we do things right, then either fGraphicsImporter or fMovie will be non-NULL, but not both. This allows us to tell, by inspecting the window object record, whether a window contains a still image or a movie.

The next thing we need to do is prevent our application from calling OpenMovieFile and NewMovieFromFile on any graphics files we open. We can accomplish this by attempting to open a file as a still image before attempting to open it as a movie. So, we'll add these lines to the QTFrame_OpenMovieInWindow function:

// see if the FSSpec picks out an image file; if so, skip the movie-opening code
myErr = GetGraphicsImporterForFile(&myFSSpec, &myImporter);
if (myImporter != NULL)
	goto gotImageFile;

Then we'll save the value returned by GetGraphicsImporterForFile in the new field in the window object record, like this:

(**myWindowObject).fGraphicsImporter = myImporter;

The last change we need to make to the QTFrame_OpenMovieInWindow routine is to set the graphics world for the graphics importer:

myPort = (CGrafPtr)QTFrame_GetPortFromWindowReference(myWindow);
if (myImporter != NULL)
	GraphicsImportSetGWorld(myImporter, myPort, GetGWorldDevice(myPort));

Elsewhere in our application's source code, we need to make a couple of changes whenever we determine the size of a window. There are two occasions when this happens. First, in the Macintosh code that handles window dragging, we need to pass the rectangle enclosing the dragged window to the DragAlignedWindow function. Previously, when our windows always contained movies, we could call GetMovieBox to get this rectangle. Now we'll have to call either GetMovieBox or GraphicsImportGetBoundsRect, depending on whether the window being dragged contains a movie or a still image:

if ((**myWindowObject).fMovie != NULL)
	GetMovieBox((**myWindowObject).fMovie, &myRect);
						
if ((**myWindowObject).fGraphicsImporter != NULL)
GraphicsImportGetNaturalBounds((**myWindowObject).fGraphicsImporter, &myRect);

Similarly, when we set the size of a window (in our function QTFrame_SizeWindowToMovie), we need to distinguish between graphics windows and movie windows. Here we'll call GraphicsImportGetNaturalBounds if the window belongs to a still image and not to a movie:

if (myImporter != NULL) {
	GraphicsImportGetNaturalBounds(myImporter, &myMovieBounds);
	goto gotBounds;
}

Notice that our application framework does not attach a grow box or scroll bars to an image window and so does not allow the user to resize the image window. The function QTFrame_SizeWindowToMovie (which now, alas, is misnamed) is called for an image window only once, when the image is first opened. That's why we call GraphicsImportGetNaturalBounds to set the image size there. Next, we need to make sure that an image window gets redrawn correctly if any part of it gets covered up and then uncovered. Whenever this happens, an update event is issued for the affected window (on the Macintosh), or a WM_PAINT message is sent to the affected window's window procedure (on Windows). In both cases, we can simply call GraphicsImportDraw to redraw the image, like this:

if (myWindowObject != NULL)
	if ((**myWindowObject).fGraphicsImporter != NULL)
		GraphicsImportDraw((**myWindowObject).fGraphicsImporter);

The final thing we need to do is remember to close the graphics importer when the window containing the still image is being closed; so we'll add these lines to the function QTFrame_CloseWindowObject:

if ((**theWindowObject).fGraphicsImporter != NULL) {
	CloseComponent((**theWindowObject).fGraphicsImporter);
	(**theWindowObject).fGraphicsImporter = NULL;
}

That completes the changes we need to make to our basic application framework in order to support still images in addition to movies. (Now wasn't that a pleasant surprise?)

Transforming Images

Now that we've modified our application framework to handle both image and movie files, we can proceed with our main concern in this article, which is to replicate most of the capabilities of the PictureViewer application. Recall that we want to support the selection of items in the Image menu shown in Figure 2. Listing 3 shows how we'll handle those selections: we'll just call some application-defined function in response to any of the selected menu items.


Listing 3: Handling the items in the Image menu.

QTApp_HandleMenu

Boolean QTApp_HandleMenu (UInt16 theMenuItem)
{
	WindowObject		myWindowObject = NULL;
	Boolean				myIsHandled = false
	
	myWindowObject = QTFrame_GetWindowObjectFromFrontWindow();
	
	switch (theMenuItem) {
	
		case IDM_HALF_SIZE:
		case IDM_NORMAL_SIZE:
		case IDM_DOUBLE_SIZE:
		case IDM_FILL_SCREEN:
			QTGraph_ScaleImage(myWindowObject, theMenuItem);
			myIsHandled = true;
			break;
			
		case IDM_ROTATE_LEFT:
		case IDM_ROTATE_RIGHT:
			QTGraph_RotateImage(myWindowObject, theMenuItem);
			myIsHandled = true;
			break;
			
		case IDM_FLIP_HORIZ:
		case IDM_FLIP_VERT:
			QTGraph_FlipImage(myWindowObject, theMenuItem);
			myIsHandled = true;
			break;
			
		case IDM_EXPORT_IMAGE:
			QTGraph_ExportImage(myWindowObject);
			myIsHandled = true;
			break;

		default:
			break;
			
	} // switch (theMenuItem)
	
	return(myIsHandled);
}

So our work will be done once we define the four QTGraph_ functions called in QTApp_HandleMenu. With the exception of QTGraph_ExportImage, which we'll consider later, all these functions operate primarily by manipulating the image matrix, which determines the orientation and size of the image drawn by the graphics importer. So let's take a minute to get acquainted with image matrices.

Working with Image Matrices

An image matrix (or image transformation matrix) is a 3-by-3 array of numbers that specifies how to map points from one two-dimensional coordinate space into another two-dimensional coordinate space. QuickTime defines the MatrixRecord data type for working with matrices:

struct MatrixRecord {
	Fixed 							matrix[3][3];
};

Pay attention to the fact that the elements of a MatrixRecord structure are of type Fixed, which represents a fixed-point number (that is, a number consisting of an integer part and a fractional part, where there are a fixed number of bytes allotted to each part of the number). In a Fixed data type, the two high-order bytes are the integer part of the fixed-point number and the two low-order bytes are the fractional part of the fixed-point number. So, for example, the number 1.5 would be represented as 0x00018000, and 2.25 would be represented as 0x00024000. The header file FixMath.h contains a number of useful functions for converting between long and fixed-point data types. We'll use the Long2Fix and FixRatio functions to help us specify the appropriate fixed-point values in our image matrices. FixMath.h also defines the constant fixed1, which we'll use instead of calling Long2Fix(1).

There are a number of standard transformations that we can perform on an image, such as enlarging or shrinking it, rotating it about some point, moving it horizontally or vertically, and skewing it (that is, essentially, grabbing a corner of it and stretching it). Each of these types of transformations is associated with some modification to the 3-by-3 image matrix. For instance, to translate an image horizontally, we only need to modify the [2][0] component of the image matrix. Similarly, to change the width of an image, we only need to modify the [0][0] component of the image matrix. To save us from having to remember all this, QuickTime provides a number of functions that we can use to set up a matrix to perform some desired transformation. For present purposes, we shall use the ScaleMatrix, RotateMatrix, TranslateMatrix, and SetIdentityMatrix functions. (The identity matrix is the matrix that transforms an image into the identical image; in other words, the identity matrix leaves the image unchanged.)

Let's consider a few concrete examples here. Suppose that we have an image whose top-left corner is located at the point (0, 0). Then we can double the width and height of that image by executing this line of code:

ScaleMatrix(&myMatrix, Long2Fix(2), Long2Fix(2), 0, 0);

The last two parameters specify the horizontal and vertical coordinates of the anchor point of the scaling (that is, the point that stays anchored during the scaling operation). For simplicity, we'll always anchor our scaling to the point (0, 0), which we'll maintain as the top-left corner of the image. Next, let's see how to flip an image horizontally. It turns out that we can do this simply by scaling the image by a factor of -1 in the horizontal direction. This effectively just flips the image across the y axis:

ScaleMatrix(&myMatrix, Long2Fix(-1), fixed1, 0, 0);

But there is a complication here. If we flip the image across the y axis in this manner, the top-left corner of the image is no longer located at (0, 0). Instead, it's located at (-n, 0), where n is equal to the width (in pixels) of the image. If we want the top-left corner of the image to remain at (0, 0), then we need to translate the image back to the right after we've flipped it horizontally:

ScaleMatrix(&myMatrix, Long2Fix(-1), fixed1, 0, 0);
TranslateMatrix(&myMatrix, Long2Fix(myRect.right), 0);

(Here we assume that myRect is the rectangle that encloses the image.) Note that the matrix functions ScaleMatrix and TranslateMatrix modify the matrix they are passed so that it incorporates the indicated transformation. In other words, ScaleMatrix adds (or in mathematical jargon, concatenates) the specified scaling to the existing transformations in myMatrix. So calling ScaleMatrix and then TranslateMatrix results in a matrix that scales and then translates the image. With matrices, the order of operations is important; doing a translation before a scaling almost always results in a different image than doing the scaling before the translation. Figure 6 shows the result of scaling and then translating an image to flip it horizontally.


Figure 6. Scaling and translating to flip an image horizontally.

Flipping Images

Now we've got the important ammunition that we need to handle the "Flip Horizontal" and "Flip Vertical" menu items in the Image menu. In a nutshell, we need to get the current image matrix, concatenate a scale matrix and a translate matrix to that image matrix, and then set the revised matrix as the new image matrix. We can use the GraphicsImportGetMatrix function to get the current image matrix and the GraphicsImportSetMatrix function to set the image matrix.

Recall, however, that we also need to know the rectangle enclosing the image, so that we can translate the flipped image horizontally or vertically to return its top-left corner to the origin. A graphics importer component provides the GraphicsImportGetBoundsRect function, which returns the current size of the image after the existing image matrix has been applied to it. The rectangle returned by GraphicsImportGetBoundsRect always has its left and top fields set to 0, so we can use the right and bottom fields to determine the width and height of the image. Listing 4 shows our QTGraph_FlipImage function.


Listing 4: Flipping an image horizontally or vertically.

QTGraph_FlipImage
OSErr QTGraph_FlipImage (WindowObject theWindowObject, UInt16 theFlipDirection)
{
	GraphicsImportComponent		myImporter = NULL;
	MatrixRecord				myMatrix;
	Rect						myRect;
	OSErr						myErr = paramErr;
	
	if (theWindowObject == NULL)
		goto bail;

	myImporter = (**theWindowObject).fGraphicsImporter;
	if (myImporter == NULL)
		goto bail;

	myErr = GraphicsImportGetMatrix(myImporter, &myMatrix);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsImportGetBoundsRect(myImporter, &myRect);
	if (myErr != noErr)
		goto bail;

	switch (theFlipDirection) {
		case IDM_FLIP_HORIZ:
			ScaleMatrix(&myMatrix, Long2Fix(-1), fixed1, 0, 0);
			TranslateMatrix(&myMatrix, Long2Fix(myRect.right), 0);
			break;

		case IDM_FLIP_VERT:
			ScaleMatrix(&myMatrix, fixed1, Long2Fix(-1), 0, 0);
			TranslateMatrix(&myMatrix, 0, Long2Fix(myRect.bottom));
			break;

		default:
			return(paramErr);
	}

	myErr = GraphicsImportSetMatrix(myImporter, &myMatrix);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsImportDraw(myImporter);

bail:
	return(myErr);
}

Notice that we call GraphicsImportDraw before exiting QTGraph_FlipImage so that the image in the window gets redrawn using its new matrix.

Rotating Images

Let's move on to consider the "Rotate Left" and "Rotate Right" menu items in the Image menu. In general, we can handle them in pretty much the same way that we handled the Flip commands in QTGraph_FlipImage: get the current image matrix, construct a new matrix for the desired rotation, add a translation matrix to return the top-left corner of the rotated image to the origin, and then set the new image matrix. (See Figure 7.) But there is one important difference here: the size of the image changes when we rotate it 90 degrees clockwise or counterclockwise (unless of course the original image is square). So we'll need to resize the window that contains the image before we redraw the image.


Figure 7. Rotating and translating to rotate an image clockwise.

This complication is actually fairly easy to handle. We simply need to call GraphicsImportGetBoundsRect a second time, after we've set the new image matrix. GraphicsImportGetBoundsRect will then return to us the rectangle that encloses the image, after the new matrix has been applied to the image. This gives us the new height and width for our image window, which we can resize by calling SizeWindow. As before, we'll finish up by calling GraphicsImportDraw to redraw the image in its new rotated orientation. Listing 5 shows our function QTGraph_RotateImage for rotating images.


Listing 5: Rotating an image.

QTGraph_RotateImage

OSErr QTGraph_RotateImage (WindowObject theWindowObject, UInt16 theImageRotation)
{
	GraphicsImportComponent		myImporter = NULL;
	MatrixRecord							myMatrix;
	Rect											myRect;
	OSErr										myErr = paramErr;

	if (theWindowObject == NULL)
		goto bail;

	myImporter = (**theWindowObject).fGraphicsImporter;
	if (myImporter == NULL)
		goto bail;

	myErr = GraphicsImportGetMatrix(myImporter, &myMatrix);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsImportGetBoundsRect(myImporter, &myRect);
	if (myErr != noErr)
		goto bail;

	switch (theImageRotation) {
		case IDM_ROTATE_LEFT:
			RotateMatrix(&myMatrix, Long2Fix(-90), 0, 0);
			TranslateMatrix(&myMatrix, 0, Long2Fix(myRect.right));
			break;

		case IDM_ROTATE_RIGHT:
			RotateMatrix(&myMatrix, Long2Fix(90), 0, 0);
			TranslateMatrix(&myMatrix, Long2Fix(myRect.bottom), 0);
			break;

		default:
			return(paramErr);
	}

	myErr = GraphicsImportSetMatrix(myImporter, &myMatrix);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsImportGetBoundsRect(myImporter, &myRect);
	if (myErr != noErr)
		goto bail;

	// set the new window size
	SizeWindow(
QTFrame_GetWindowFromWindowReference((**theWindowObject).fWindow),
				myRect.right,
				myRect.bottom,
				false);

	myErr = GraphicsImportDraw(myImporter);

bail:
	return(myErr);
}

Scaling Images

The last image transformation we want to handle is scaling the image to a new size. As with rotating, scaling to a new size requires us to worry about setting a new window size after we've updated the image matrix, but we can use the same code as before to handle that. The added complication that we meet now is that the "Half Size", "Normal Size", and "Double Size" menu items specify absolute sizes, not relative sizes. That is to say, selecting the "Half Size" menu item should result in an image that is half its natural size, not half the current size of the image. So we can't just concatenate a scaling matrix onto the existing image matrix.

There are several ways we could handle this complication. For my money, the most natural way is to keep track of the current size of the image and then perform the appropriate relative scaling when we get a request to change its size. So we'll put the single field fCurrentSize into the application data structure associated with each window:

typedef struct ApplicationDataRecord {
	UInt16					fCurrentSize;
} ApplicationDataRecord, *ApplicationDataPtr, **ApplicationDataHdl;

Then we can handle the "Half Size" menu command like this:

case IDM_HALF_SIZE:
	if ((**myAppData).fCurrentSize == IDM_NORMAL_SIZE)
		ScaleMatrix(&myMatrix, FixRatio(1, 2), FixRatio(1, 2), 0, 0);

	if ((**myAppData).fCurrentSize == IDM_DOUBLE_SIZE)
		ScaleMatrix(&myMatrix, FixRatio(1, 4), FixRatio(1, 4), 0, 0);
	break;

As you can see, we scale the image relatively by a factor of one-half or one-fourth, depending on whether the image currently is at its normal size or is twice its normal size. (Of course, if the image is already at half its normal size, we don't add any new scaling to the image matrix.) Listing 6 shows our complete function for handling image size changes.


Listing 6: Changing the size of an image.

QTGraph_ScaleImage

OSErr QTGraph_ScaleImage (WindowObject theWindowObject, UInt16 theImageSize)
{
	ApplicationDataHdl			myAppData = NULL;
	GraphicsImportComponent		myImporter = NULL;
	MatrixRecord				myMatrix;
	Rect						myRect;
	OSErr						myErr = paramErr;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject (theWindowObject);
	if (myAppData == NULL)
		goto bail;

	myImporter = (**theWindowObject).fGraphicsImporter;
	if (myImporter == NULL)
		goto bail;

	myErr = GraphicsImportGetMatrix(myImporter, &myMatrix);
	if (myErr != noErr)
		goto bail;

	switch (theImageSize) {
		case IDM_HALF_SIZE:
			if ((**myAppData).fCurrentSize == IDM_NORMAL_SIZE)
		ScaleMatrix(&myMatrix, FixRatio(1, 2), FixRatio(1, 2), 0, 0);

			if ((**myAppData).fCurrentSize == IDM_DOUBLE_SIZE)
				ScaleMatrix(&myMatrix, FixRatio(1, 4), FixRatio(1, 4), 0, 0);
			break;

		case IDM_NORMAL_SIZE:
			// reset everything
			SetIdentityMatrix(&myMatrix);
			break;

		case IDM_DOUBLE_SIZE:
			if ((**myAppData).fCurrentSize == IDM_HALF_SIZE)
		ScaleMatrix(&myMatrix, Long2Fix(4), Long2Fix(4), 0, 0);

			if ((**myAppData).fCurrentSize == IDM_NORMAL_SIZE)
		ScaleMatrix(&myMatrix, Long2Fix(2), Long2Fix(2), 0, 0);
			break;

		case IDM_FILL_SCREEN:
			// left as an exercise to the reader
			break;

		default:
			return(paramErr);
	}

	(**myAppData).fCurrentSize = theImageSize;

	myErr = GraphicsImportSetMatrix(myImporter, &myMatrix);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsImportGetBoundsRect(myImporter, &myRect);
	if (myErr != noErr)
		goto bail;
	
	SizeWindow(	
		QTFrame_GetWindowFromWindowReference((**theWindowObject) fWindow),
				myRect.right,
				myRect.bottom,
				false);

	myErr = GraphicsImportDraw(myImporter);

bail:
	return(myErr);
}

Notice that the code for handling the "Normal Size" menu item is refreshingly simple: it just calls SetIdentityMatrix to reset the image matrix to the identity matrix. This has the effect of setting the image to its natural size and orientation, thereby undoing any scaling, rotation, and flipping the user may previously have performed.

As mentioned earlier, one advantage of keeping track of the current size of the image is that we have an easy way to add a check mark to the correct item in the Image menu. Our function QTApp_AdjustMenus contains a few lines that look essentially like this:

mySize = (**myAppData).fCurrentSize;
for (myItem = IDM_HALF_SIZE; myItem <= IDM_FILL_SCREEN; myItem++)
	QTFrame_SetMenuItemCheck(myMenu, myItem, mySize == myItem);

Working with Multi-Image Files

Before moving on to consider graphics exporters, let's take a minute to see how we can work with files that contain more than one image. As I mentioned earlier, this capability is new with QuickTime 4. In particular, QuickTime 4 added the ability to import (but not export) graphics in the FlashPix format defined by the Digital Imaging Group. Also, QuickTime 4 added support for opening and displaying multiple images contained in PhotoShop files (which store layers as separate images in a single file). Earlier versions of QuickTime could open PhotoShop files but imported all the layers into a single image.

You can determine how many images a particular graphics file contains by calling the GraphicsImportGetImageCount function. And you can select which of those images is displayed by calling the GraphicsImportSetImageIndex function. Listing 7 illustrates how to display all the images contained in a specified file. The QTGraph_ShowImagesFromFile function is simply a modified version of QTGraph_ShowImageFromFile (defined in Listing 1) that loops through all the images in the image file, pausing a few seconds to display each image.


Listing 7: Displaying all images in a graphics file

QTGraph_ShowImagesFromFile
void QTGraph_ShowImagesFromFile (FSSpec *theFSSpec)
{	
	GraphicsImportComponent		myImporter = NULL;
	Rect						myRect;
	unsigned long				myCount, myIndex, myIgnore;
	WindowPtr					myWindow = NULL;

	GetGraphicsImporterForFile(theFSSpec, &myImporter);
	if (myImporter != NULL) {
		// determine how many images are in the specified file
		GraphicsImportGetImageCount(myImporter, &myCount);
		
		// loop thru all images the image file, drawing each into a window
		for (myIndex = 1; myIndex <= myCount; myIndex++) {
			// set the image index we want to display
			GraphicsImportSetImageIndex(myImporter, myIndex);

			GraphicsImportGetNaturalBounds(myImporter, &myRect);
			MacOffsetRect(&myRect, 50, 50);

			myWindow = NewCWindow(NULL, &myRect, NULL, true,
							noGrowDocProc, (WindowPtr)-1L, true, 0);
			if (myWindow != NULL) {
				GraphicsImportSetGWorld(myImporter, (CGrafPtr)myWindow, NULL);
				GraphicsImportDraw(myImporter);
				Delay(120, &myIgnore);		// wait 2 seconds
				DisposeWindow(myWindow);
			}
		}

		CloseComponent(myImporter);
	}
}

Exporting Images

A graphics export component, or graphics exporter, is a software component that provides services for saving a still image in a new format. While graphics importers allow us to read in images that are stored in any of a wide number of formats, graphics exporters allow us to write out images into any of a fairly large number of formats. Currently the formats supported by QuickTime's graphics exporters are a subset of those supported by the graphics importers; but in theory there is nothing (other than programming resources and possibly also legal restrictions) preventing the importers and exporters from supporting the same formats.

Graphics exporters are relative newcomers to the QuickTime scene, having been introduced in their full glory only in version 4. But QuickTime has supported some means of exporting images from as early as version 2.5, which included the GraphicsImportSaveAsPicture and GraphicsImportSaveAsQuickTimeImageFile functions. QuickTime 3.0 added a handful of additional functions, including GraphicsImportExportImageFile and GraphicsImportDoExportImageFileDialog (which we'll investigate further below). These functions provided much-needed services for converting images from one format to another, but they did not provide a very extensive set of capabilities for customizing the export process or the features of the exported images. The graphics exporters introduced in QuickTime 4 expanded the available export formats and greatly increased the ability of applications to manipulate images during export. Moreover, the newly defined interface for graphics exporters opened the door for third parties to create custom graphics exporters.

Here we'll take a look at two very basic ways of exporting image files. First we'll show how to use GraphicsImportDoExportImageFileDialog to present a standard export image dialog box to the user. Then we'll take a quick look at exporting images without involving the user, first using GraphicsImportExportImageFile and then using the graphics exporter APIs.

Exporting Images Using the Export Image Dialog Box

If you're running PictureViewer and have opened an image file, you can select the "Export..." item in the File menu to save the image in a new format. When you select that item, you are presented with the export image dialog box shown in Figure 8.


Figure 8. The export image dialog box.

As you can see, this dialog box allows you to select an output filename and location, a file type, and some image format options. For instance, if you select the JPEG format for the exported file and click the Option button, you'll see the dialog box that's shown in Figure 9.


Figure 9. An options dialog box.

It's actually quite easy to add this capability to our QTGraphics application. QuickTime provides the GraphicsImportDoExportImageFileDialog function, which we can use to get QuickTime to display the export image dialog box and handle all the low-level details of creating the output file and writing the appropriate image data to that file. Indeed, GraphicsImportDoExportImageFileDialog even takes the trouble to append the correct filename suffix to the output file name (although the user can change or remove that suffix, if desired).

GraphicsImportDoExportImageFileDialog takes seven parameters. The first, of course, is the graphics importer that we are using to manage the image to be exported in a new format. The second parameter is a file system specification that indicates a suggested location and name for the output file. We'll have QTGraphics pass in the specification of the image that's open in the window (that is, the file being exported). Since presumably the user will select a new image format and GraphicsImportDoExportImageFileDialog will adjust the filename suffix automatically, we shouldn't run into any danger of overwriting the existing image file.

The third parameter to GraphicsImportDoExportImageFileDialog is a Pascal string that is displayed as a prompt in the export image dialog box. Like PictureViewer, we'll pass in the string "Save Image As:". The fourth parameter is a universal procedure pointer (UPP) to a modal dialog filter function, which can intercept events passed to the dialog box. In QTGraphics, we'll pass a UPP to the function QTGraph_ExportImageFileDialogEventFilter, which in turn calls the filter function QTFrame_StandardModalDialogEventFilter defined in the file MacFramework.c. The important work done by QTFrame_StandardModalDialogEventFilter is to look for update events that are targeted at one of our application's windows; if it finds any, it calls QTFrame_HandleEvent to dispatch the update event to the correct movie or image window. In practice, this means that any part of the movie or image window that is obscured by some other window and then uncovered will be redrawn, even if the export image dialog box is still displayed on the screen. It's interesting to note that PictureViewer does not seem to have a modal dialog filter function installed here, since it doesn't redraw its image windows while the export image dialog box is displayed. (On Windows, update messages are sent directly to the affected window, so we don't need to install a filter function here; if you look at the QTGraph_ExportImageFileDialogEventFilter function, you'll see that on Windows it simply returns false to indicate that it didn't handle the event.)

The last three parameters to GraphicsImportDoExportImageFileDialog are used to communicate back to its caller some information about the newly created file. Since we don't care about that information, we'll just pass NULL in all three parameters. Listing 8 shows our function QTGraph_ExportImage for exporting images.


Listing 8: Exporting an image into a user-selected format.

QTGraph_ExportImage

OSErr QTGraph_ExportImage (WindowObject theWindowObject)
{
	GraphicsImportComponent		myImporter = NULL;
	StringPtr 					myPrompt = 
					QTUtils_ConvertCToPascalString(kExportImagePrompt);
	ModalFilterYDUPP			myFilterProcUPP = NULL;
	OSErr						myErr = paramErr;

	if (theWindowObject == NULL)
		goto bail;

	myImporter = (**theWindowObject).fGraphicsImporter;
	if (myImporter == NULL)
		goto bail;

	myFilterProcUPP = 
	NewModalFilterYDProc(QTGraph_ExportImageFileDialogEventFilter);

	myErr = GraphicsImportDoExportImageFileDialog(
						myImporter,
						&(**theWindowObject).fFileFSSpec,
						myPrompt
						myFilterProcUPP,
						NULL,
						NULL,
						NULL);	
bail:
	DisposeRoutineDescriptor(myFilterProcUPP);
	free(myPrompt);
	return(myErr);
}

Exporting Images Directly

Sometimes we don't want to use the export image dialog box to help us export an image to a file. For instance, the user might want to convert a large number of image files from one format to another, using the same settings for all the conversions. In this case, it would be tedious, at best, for the user to have to configure the desired settings in the export image dialog box for each image. Luckily, QuickTime supports more direct ways of exporting files. We can use the graphics exporter functions introduced in QuickTime 4, or (if we want our application to run in versions of QuickTime as early as 3.0) we can use the GraphicsImportExportImageFile function.

GraphicsImportExportImageFile is very easy to use and requires virtually no set-up. We need to pass it the graphics importer instance attached to the image that we want to export, along with the desired output file format and a file system specification that picks out the output file. Here's how we might export an image to a JPEG file:

GraphicsImportExportImageFile(myImporter, kQTFileTypeJPEG, 0, &myExportedFile, smSystemScript);

The third parameter here specifies the output file's creator; by passing 0 in that parameter, we get the default creator for the specified file type. More often than not, the default creator is set to the PictureViewer application. The fourth parameter specifies the output file, which is created by GraphicsImportExportImageFile if it doesn't already exist. If the file does already exist, its data is replaced by the image being exported.

When exporting the image data to the output file, GraphicsImportExportImageFile applies the current image matrix to the source image. This generally means that the exported image looks just like the image currently displayed on the screen. The only exception to this is when the top-left corner of the image is not located at (0, 0); in that case, GraphicsImportExportImageFile temporarily adjusts the image matrix so that the resulting image has a top-left corner that is located at (0, 0).

To export an image directly using the graphics exporter programming interfaces, we first need to open an instance of a graphics exporter. That is to say, we need to open a component whose component type is GraphicsExporterComponentType and whose component subtype is the file type of the exported image file. For instance, to export an image as a JPEG file, we could call OpenADefaultComponent like this:

OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypeJPEG, &myExporter);

If OpenADefaultComponent finds a graphics export component that can handle JPEG files, it returns an instance of that exporter to us in the myExporter parameter. We'll use that instance in all of our subsequent calls to the graphics exporter.

Now that we've found a suitable graphics exporter, we need to do three things before we can call the GraphicsExportDoExport function to do the actual exporting. First, we need to tell the graphics exporter where to get the image data to export. Since our image is being handled by a graphics importer, we can call GraphicsExportSetInputGraphicsImporter to tell the exporter to use the importer as the source of the image data. Second, we need to tell the graphics exporter where to put the exported data. For the present, we'll just give it a file, by calling GraphicsExportSetOutputFile. Finally, we might need to change some of the default export settings. For instance, the graphics exporter automatically gives an appropriate file type and creator to any files exported on Macintosh systems; if those settings are not appropriate, we could call GraphicsExportSetOutputFileTypeAndCreator to set some other type or creator. In the QTGraph_ExportImageWithoutDialog function, defined in Listing 9, we'll explicitly set the output image quality to be codecNormalQuality.


Listing 9: Exporting an image into a specified format.

QTGraph_ExportImage

OSErr QTGraph_ExportImageWithoutDialog (WindowObject theWindowObject, OSType theType)
{
	GraphicsImportComponent			myImporter = NULL;
	GraphicsExportComponent			myExporter = NULL;
	FSSpec											myExportedFile;
	StringPtr 									myName = 
										QTUtils_ConvertCToPascalString("temp");
	OSErr											myErr = paramErr;

	if (theWindowObject == NULL)
		goto bail;

	myImporter = (**theWindowObject).fGraphicsImporter;
	if (myImporter == NULL)
		goto bail;

	// create an FSSpec for the file containing the exported image
	myErr = FSMakeFSSpec(0, 0, myName, &myExportedFile);
	if ((myErr != noErr) && (myErr != fnfErr))
		goto bail;

	// get a graphics exporter of the desired type
	myErr = OpenADefaultComponent(GraphicsExporterComponentType, theType, &myExporter);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsExportSetInputGraphicsImporter(myExporter, &myImporter);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsExportSetOutputFile(myExporter, &myExportedFile);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsExportSetCompressionQuality(myExporter, codecNormalQuality);
	if (myErr != noErr)
		goto bail;

	myErr = GraphicsExportDoExport(myExporter, NULL);

bail:
	if (myExporter != NULL)
		CloseComponent(myExporter);

	free(myName);
	return(myErr);
}

One thing worth noticing here is that (just like GraphicsImportExportImageFile) GraphicsExportDoExport will create the specified output file, if it doesn't already exist. So we don't need to call FSpCreate to create the file picked out by myExportedFile.

Finding Image Files

Before we close up shop, let's return to a topic we touched on earlier, namely how to list to the user the files that our application is capable of opening. Recall that the QTShell application passes to StandardGetFilePreview the single file type kQTFileTypeMovie; this results in a list that includes all files that are of type 'MooV' or are of a type that can be imported by one of QuickTime's movie importers. As we saw, this list will also include all image files that QuickTime can open, since QuickTime includes a movie importer that can import image files.

This is well and good for an application that wants to be able to open both movie files and image files. But it's less satisfactory for an application like PictureViewer that is concerned solely with image files. However, StandardGetFilePreview supports another constant, kQTFileTypeQuickTimeImage, which tells it to list all files that are of type 'qtif' or are of a type that can be imported by one of QuickTime's graphics importers. If I were a betting man, I'd wager a hefty sum that PictureViewer includes some source code that looks a lot like this:

OSType 						myTypeList = kQTFileTypeQuickTimeImage;
StandardFileReply		myReply;

StandardGetFilePreview(NULL, 1, 
								(ConstSFTypeListPtr)&myTypeList, &myReply);

For an application like QTShell or QTGraphics that knows how to handle both movies and images, we can specify that we want both movie files and image files listed in the file-opening dialog box, like this:

OSType myTypeList[] = {kQTFileTypeMovie, kQTFileTypeQuickTimeImage}; StandardFileReply myReply; StandardGetFilePreview((FileFilterUPP)theFilterProc, 2, (ConstSFTypeListPtr)myTypeList, &myReply);

As we've seen, adding kQTFileTypeQuickTimeImage here is overkill, but it's worth putting it in, if only to remind us that we're going to get image files as well as movie files.

But now a problem presents itself. What if we want to make our application compatible with Carbon, the set of programming interfaces and libraries that support execution on both Mac OS X and certain versions of the "classic" Macintosh operating system (namely Mac OS 8 and Mac OS 9)? The Standard File Package is no longer available to Carbon applications, and its replacement, Navigation Services, does not provide any special processing for the constants kQTFileTypeMovie and kQTFileTypeQuickTimeImage.

To make a long story short, what we need to do is provide a UPP to a file filter function when calling NavGetFile, the Navigation Services replacement for StandardGetFilePreview. This filter function is passed information about each file that is a candidate for being listed in the file-opening dialog box; the filter should return true for each file that our application wants to appear in that list of files.

Suppose for the moment that we want our application to be able to open only image files. Could we just call the QTUtils_IsImageFile function defined in Listing 2 to determine whether the candidate file should be listed? The answer, as I've already hinted earlier, is "no". In fact that test would work just fine in separating the image files from the non-image files, but it would take far too long to do so. (Even on a relatively fast machine, using QTUtils_IsImageFile in our filter function to pick out image files would take several minutes.)

A better strategy is first to build up a list of all the file types that we want our application to be able to open and then, in the file filter function called by NavGetFile, simply look to see whether a candidate file has a type that's in that list. This is the strategy adopted by QTShell and its descendants, including QTGraphics. To build up this list of file types, we can iterate through all available graphics importers and ask them what kinds of file types they can handle. Listing 10 shows a part of a function QTFrame_AddComponentFileTypes that does this, using Component Manager functions to find all the available graphics import components.


Listing 10: Finding all image file types.

QTFrame_AddComponentFileTypes

ComponentDescription		myFindCompDesc = {0, 0, 0, 0, 0};
ComponentDescription		myInfoCompDesc = {0, 0, 0, 0, 0};
Component							myComponent = NULL;

myFindCompDesc.componentType = GraphicsImporterComponentType;
myFindCompDesc.componentFlags = 0;
myFindCompDesc.componentFlagsMask = movieImportSubTypeIsFileExtension;

myComponent = FindNextComponent(myComponent, &myFindCompDesc);
while (myComponent != NULL) {
	GetComponentInfo(myComponent, &myInfoCompDesc, NULL, NULL, NULL);
	gValidFileTypes[*theNextIndex] = myInfoCompDesc.componentSubType;
	*theNextIndex += 1;

	myComponent = FindNextComponent(myComponent, &myFindCompDesc);
}

Notice that the componentFlags and componentFlagsMask fields of the component description that we're using to find graphics importers are set up to exclude any graphics importers whose subtype is a file extension. That's because we want to find only those importers whose subtype is a Macintosh file type. (One consequence of this, however, is that our file filter function will exclude files that don't have a recognized file type but do have a recognized filename extension. So our Navigation Services code doesn't quite offer the full functionality of the Standard File Package version.)

Once we've built the list of image types that the available graphics importers can handle, we can use that list inside of our file filter function. We don't need to go into the details of that here; if you're interested, take look at the function QTFrame_GetOneFileWithPreview in the file ComFramework.c.

Conclusion

We've actually accomplished quite a lot in this article. We've seen how to make sure that image files appear in the list of files displayed by the file-opening dialog box (either using StandardGetFilePreview or NavGetFile). We've learned how to find a graphics importer capable of opening any of those files and how use a graphics importer to draw an image in a window on the screen. We've also learned how to perform some simple transformations on an image, by altering the image matrix used by the graphics importer. Finally, we've investigated several methods of exporting images into new image formats. So we've successfully managed to upgrade our existing QTShell application into QTGraphics, our poor-man's PictureViewer.

Nevertheless, we've barely even begun to cover the many capabilities provided by graphics importers and exporters. If you take a glance into ImageCompression.h (where the graphics importer and exporter functions are declared), you'll encounter a large number of functions that we haven't mentioned at all. There are graphic importer functions that allow us to work with data references instead of files (so we could open and display images addressed using URLs). There are functions that allow us get and set the image clipping region, and the source and destination rectangles, and the image quality. With graphics exporters, the situation is even more daunting; of the six dozen graphics exporter functions declared in ImageCompression.h, we have discussed exactly 4 of them here.

Of course, this is only good news, since it means that QuickTime provides us with a very wide array of image importing and exporting services. As our image-handling needs grow, it's likely that QuickTime already provides services we can use to manage those needs.

Credits

Some of the code in QTGraphics is based on code written by Sam Bushell for his "Improve Your Image" presentation at the recent QuickTime Live! conference; Sam also provided many helpful comments on this article.

References

You can find the most up-to-date documentation on graphics importers and exporters at <http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/rmImporter.htm> and <http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/rmExporter.htm>


Tim Monroe has worked at Apple for over 10 years, first as a technical writer in the Inside Macintosh group and later as a software engineer in the QuickTime group. Currently he is developing sample code and utilities for the QuickTime software development kit. You can reach him at monroe@apple.com.

 
AAPL
$119.00
Apple Inc.
+1.40
MSFT
$47.75
Microsoft Corpora
+0.28
GOOG
$540.37
Google Inc.
-0.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

Skype 7.2.0.412 - Voice-over-internet ph...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
OneNote 15.4 - Free digital notebook fro...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more

Latest Forum Discussions

See All

Lucha Amigos (Games)
Lucha Amigos 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Forget Ninja Turtles, and meet Wrestlers Turtles! Crazier, Spicier and…Bouncier! Sling carapaces of 7 Luchadores to knock all... | Read more »
Raby (Games)
Raby 1.0.3 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.3 (iTunes) Description: ***WARNING - Raby runs on: iPhone 5, iPhone 5C, iPhone 5S, iPhone 6, iPhone 6 Plus, iPad Mini Retina, iPad Mini 3, iPad 4, iPad Air,... | Read more »
Oddworld: Stranger's Wrath (Games)
Oddworld: Stranger's Wrath 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: ** PLEASE NOTE: Oddworld Stranger's Wrath requires at least an iPhone 4S, iPad 2, iPad Mini or iPod Touch 5th gen... | Read more »
Bounce On Back (Games)
Bounce On Back 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Dwelp (Games)
Dwelp 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: === 50% off for a limited time, to celebrate release === Dwelp is an elegant little puzzler with a brand new game mechanic. To complete a... | Read more »
Make Way for Fat Chicken, from the Maker...
Make Way for Fat Chicken, from the Makers of Scrap Squad Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Relevant Games has announced they will be releasing their reverse tower defense game, | Read more »
Tripnary Review
Tripnary Review By Jennifer Allen on November 26th, 2014 Our Rating: :: TRAVEL BUCKET LISTiPhone App - Designed for the iPhone, compatible with the iPad Want to create a travel bucket list? Tripnary is a fun way to do exactly that... | Read more »
Ossian Studios’ RPG, The Shadow Sun, is...
Ossian Studios’ RPG, The Shadow Sun, is Now Available for $4.99 Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Mmmm, Tasty – Having the Angry Birds for...
The very first Angry Birds debuted on iOS back in 2009. When you sit back and tally up the number of Angry Birds games out there and the impact they’ve had on pop culture as a whole, you just need to ask yourself: “How would the birds taste... | Read more »
Rescue Quest Review
Rescue Quest Review By Jennifer Allen on November 26th, 2014 Our Rating: :: PATH BASED MATCH-3Universal App - Designed for iPhone and iPad Guide a wizard to safety by matching gems. Rescue Quest might not be an entirely original... | Read more »

Price Scanner via MacPrices.net

Black Friday: 15% off iTunes Gift Cards
Staples is offering 15% off $50 and $100 iTunes Gift Cards on their online store as part of their Black Friday sale. Click here for more information. Shipping is free. Best Buy is offering $100... Read more
BEVL Releases Dock Tailored for iPhone 6 and...
Seattle based BEVL has released their first product: an iPhone dock that is divergent in build quality, rock-solid function and visual simplicity to complement the iPhone. BEVL is now accepting... Read more
Black Friday: $150 off 13-inch Retina MacBook...
 Best Buy has 13-inch 2.6GHz Retina MacBook Pros on sale for $150 off MSRP on their online store as part of their Black Friday sale. Choose free shipping or free local store pickup (if available).... Read more
Black Friday: $300 off 15-inch Retina MacBook...
 B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for $300 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina... Read more
Black Friday: Up to $140 off MacBook Airs, fr...
 B&H Photo has 2014 MacBook Airs on sale for up to $140 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 11″ 128GB MacBook Air: $799 $100... Read more
Black Friday: 13-inch 2.5GHz MacBook Pro on s...
 Best Buy has the 13″ 2.5GHz MacBook Pro on sale for $899.99 on their online store as part of their Black Friday sale. Choose free shipping or free instant local store pickup (if available). Their... Read more
Black Friday: 21-inch 1.4GHz iMac on sale for...
 Best Buy has the 21″ 1.4GHz iMac on sale for $899.99 on their online store as part of their Black Friday sale. Their price is $200 off MSRP. Choose free shipping or free local store pick up. Price... Read more
Black Friday iPad Air 2 sale prices, $100 off...
 Best Buy has iPad Air 2s on sale for $100 off MSRP on their online store for Black Friday. Choose free shipping or free local store pickup (if available). Sale prices available for online orders... Read more
2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has the new 1.4GHz Mac mini on sale for $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new model. Adorama... Read more
Early Black Friday pricing on 27-inch 5K iMac...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.