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.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »
Play Together teams up with Sanrio to br...
I was quite surprised to learn that the massive social network game Play Together had never collaborated with the globally popular Sanrio IP, it seems like the perfect team. Well, this glaring omission has now been rectified, as that instantly... | Read more »
Dark and Darker Mobile gets a new teaser...
Bluehole Studio and KRAFTON have released a new teaser trailer for their upcoming loot extravaganza Dark and Darker Mobile. Alongside this look into the underside of treasure hunting, we have received a few pieces of information about gameplay... | Read more »

Price Scanner via MacPrices.net

Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more
B&H has 16-inch MacBook Pros on sale for...
Apple 16″ MacBook Pros with M3 Pro and M3 Max CPUs are in stock and on sale today for $200-$300 off MSRP at B&H Photo. Their prices are among the lowest currently available for these models. B... Read more
Updated Mac Desktop Price Trackers
Our Apple award-winning Mac desktop price trackers are the best place to look for the lowest prices and latest sales on all the latest computers. Scan our price trackers for the latest information on... Read more
9th-generation iPads on sale for $80 off MSRP...
Best Buy has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80 off MSRP on their online store for a limited time. Prices start at only $249. Sale prices for online orders only, in-store prices... Read more
15-inch M3 MacBook Airs on sale for $100 off...
Best Buy has Apple 15″ MacBook Airs with M3 CPUs on sale for $100 off MSRP on their online store. Prices valid for online orders only, in-store prices may vary. Order online and choose free shipping... Read more
24-inch M3 iMacs now on sale for $150 off MSR...
Amazon is now offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150... Read more
15-inch M3 MacBook Airs now on sale for $150...
Amazon is now offering a $150 discount on Apple’s new M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook Air/8GB/256GB: $1149.99, $... Read more

Jobs Board

Early Preschool Teacher - Glenda Drive/ *Appl...
Early Preschool Teacher - Glenda Drive/ Apple ValleyTeacher Share by Email Share on LinkedIn Share on Twitter Read more
Retail Assistant Manager- *Apple* Blossom Ma...
Retail Assistant Manager- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 04225 Job Area: Store: Management Read more
Housekeeper, *Apple* Valley Village - Cassi...
Apple Valley Village Health Care Center, a senior care campus, is hiring a Part-Time Housekeeper to join our team! We will train you for this position! In this role, Read more
Sonographer - *Apple* Hill Imaging Center -...
Sonographer - Apple Hill Imaging Center - Evenings Location: York Hospital, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now See Read more
Senior Software Engineer - *Apple* Fundamen...
…center of Microsoft's efforts to empower our users to do more. The Apple Fundamentals team focused on defining and improving the end-to-end developer experience in Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.