This section provides examples of how you can read and write picture data for the purpose of converting it to PDF or another Quartz-compatible format. Some general strategies include the following:
As you convert data, you may find that some PICTs don’t convert well. In these cases, you’ll want to first convert the PICT to a pixel picture first. See “Avoiding PICT Wrappers for Bitmap Images.”
For PICTs that are wrappers for JPEG data, use the function CGImageCreateWithJPEGDataProvider for the JPEG data rather than working with the PICT.
If your application uses predefined PICT files or resources that are drawn repeatedly, you can convert them into PDF and place them in the Resources folder inside the application bundle. See “Converting QDPict Pictures Into PDF Documents.”
Use Quartz data providers to work with QDPict data. See “Creating a QDPict Picture From Data in Memory” and “Creating a QDPict Picture From a PICT File.”
If you need to draw PICT data scaled, evaluate which type of scaling is best for your purposes. See “Scaling QDPict Pictures.”
A popular strategy used by QuickDraw developers is to create a PICT wrapper around a bitmap image. With a bitmap image inside a PICT container, the picture acts as a transport mechanism for the image. If the bitmap is a JPEG or other image data, it’s best to create a CGImage from that data. For example, you can draw the bitmap to a bitmap graphics context and then create a CGImage by calling the function CGBitmapContextCreateImage (available starting in Mac OS X v10.4). If the bitmap is JPEG or PNG data, you can use CGImageCreateWithJPEGDataProvider or CGImageCreateWithPNGDataProvider to create a CGImage.
PICT uses a vector-based format. If you use the CopyBits function to create a PICT representation of a bitmap image by opening a QuickDraw picture, and copying an image onto itself (by specifying the same pixel map as source and destination), then you replace the vector-based format with at bit-based one. In general, the wrapper strategy in QuickDraw is not a good one. As you move your code to Quartz, you’ll want to convert PICTs to PDF documents. There is no need to create PICT wrappers to do so.
PDF is the format used to copy-and-paste between applications in Mac OS X. It’s also the metafile format for Quartz because PDF is resolution independent. Although you can use a PDF wrapper for a bitmap image (just as PICT has been used) if you wrap a PDF with a bitmap image, the bitmap is limited by resolution at which it was created.
To convert existing PICT images to PDF documents, you can use the QuickDraw QDPict API. This API is declared in the interface file QDPictToCGContext.h in the Application Services framework. Note that if a QuickDraw picture contains drawing operations such as CopyBits that use transfer modes that don’t have an analogue in PDF, the PDF representation may not look exactly the same.
The QDPict API includes these data types and functions:
QDPictRef—An opaque type that represents picture data in the Quartz drawing environment. An instance of this type is called a QDPict picture.
QDPictCreateWithProvider and QDPictCreateWithURL,—Functions that create QDPict pictures using picture data supplied with a Quartz data provider or with a PICT file.
QDPictDrawToCGContext—A function that draws a QDPict picture into a Quartz graphics context. If redrawing performance is an issue, draw the PICT into a PDF graphics context, save it as a PDF document, and then use the PDF document with the Quartz routines for drawing PDF data, such as the function CGContextDrawPDFPage.
To create a QDPict picture from picture data in memory, you call QDPictCreateWithProvider and supply the data using a Quartz data provider. When you create the provider, you pass it a pointer to the picture data—for example, by dereferencing a locked PicHandle.
When using the functions QDPictCreateWithURL and QDPictCreateWithProvider, the picture data must begin at either the first byte or the 513th byte. The picture bounds must not be an empty rectangle.
Listing 4-1 shows how to implement this method using two custom functions—a creation function, and a release function associated with the data provider. A detailed explanation of each numbered line of code follows the listing.
Listing 4-1 Routines that create a QDPict picture from PICT data
QDPictRef MyCreateQDPictWithData (void *data, size_t size) |
{ |
QDPictRef picture = NULL; |
CGDataProviderRef provider = |
CGDataProviderCreateWithData (NULL, data, size, MyReleaseProc);// 1 |
if (provider != NULL) |
{ |
picture = QDPictCreateWithProvider (provider);// 2 |
CFRelease (provider); |
} |
return picture; |
} |
void MyReleaseProc (void *info, const void *data, size_t size)// 3 |
{ |
if (info != NULL) { |
/* release private information here */ |
}; |
if (data != NULL) { |
/* release picture data here */ |
}; |
} |
Here’s what the code does:
Creates a Quartz data provider for your picture data. The parameters are private information (not used here), the address of the picture data, the size of the picture data in bytes, and your custom release function.
Creates and returns a QDPict picture unless the picture data is not valid.
Handles the release of any private resources when the QDPict picture is released. This is a good place to deallocate the picture data, if you’re finished using it.
To create a QDPict picture from picture data in a PICT file, you call QDPictCreateWithURL and specify the file location with a Core Foundation URL. Listing 4-2 shows how to implement this method using an opaque FSRef file specification.
Listing 4-2 A routine that creates a QDPict picture using data in a PICT file
QDPictRef MyCreateQDPictWithFSRef (const FSRef *file) |
{ |
QDPictRef picture = NULL; |
CFURLRef url = CFURLCreateFromFSRef (NULL, file); |
if (url != NULL) |
{ |
picture = QDPictCreateWithURL (url); |
CFRelease(url); |
} |
return picture; |
} |
Listing 4-3 shows how to write a function that converts a QDPict picture into a PDF document stored in a file. A detailed explanation of each numbered line of code follows the listing. (Source code to create the URL and the optional PDF auxiliary information dictionary is not included here.)
Listing 4-3 Code that converts a picture into a single-page PDF document
void MyConvertQDPict (QDPictRef picture, CFURLRef url, |
CFDictionaryRef dict) |
{ |
CGContextRef context = NULL; |
CGRect bounds = QDPictGetBounds (picture); |
bounds.origin.x = 0; |
bounds.origin.y = 0; |
context = CGPDFContextCreateWithURL (url, &bounds, dict);// 1 |
if (context != NULL) |
{ |
CGContextBeginPage (context, &bounds);// 2 |
(void) QDPictDrawToCGContext (context, bounds, picture);// 3 |
CGContextEndPage (context);// 4 |
CGContextRelease (context);// 5 |
} |
} |
Here’s what the code does:
Creates a PDF graphics context that directs the PDF content stream to a URL. If the URL is a file, the filename should end with the .pdf extension. The second parameter uses the picture bounds to specify the media box. The third parameter is an optional PDF auxiliary information dictionary, which contains the title and creator of the PDF document.
Begins a new page. In a PDF context, all drawing outside of an explicit page boundary is ignored. Here the page size (or media box) is the picture bounds, but you could specify any page size.
Draws the picture. The drawing rectangle is identical to the picture bounds, so there is no change of scale.
Ends the current PDF page.
Releases the PDF context, which finalizes the PDF content stream and finishes creating the file.
Note: Quartz provides an opaque type called CGPDFDocumentRef for working with existing PDF documents. When you need to examine, draw, or print a PDF page, you create an object of this type. CGPDFDocumentRef isn’t needed here because the objective is to create a PDF document, not to use it.
When drawing a picture in a Quartz context, you have two ways to change the horizontal or vertical scale of the picture:
Create a drawing rectangle by applying the change of scale to the bounds rectangle returned by QDPictGetBounds and pass this drawing rectangle to QDPictDrawToCGContext. When the picture is rendered, patterns are not scaled along with other graphic elements. This is the same behavior as that of the DrawPicture function. For example, compare the original picture in Figure 4-1 with the scaled picture in Figure 4-2.
Before drawing the picture, apply the appropriate affine transformation—for example, by calling CGContextScaleCTM. When the picture is rendered, the entire picture is scaled, including patterns. The effect is equivalent to viewing the picture with the Preview application and clicking the Zoom In button. Compare the original in Figure 4-1 with the scaled picture in Figure 4-3 to see how this looks.
Listing 4-4 shows how to implement both types of scaling. A detailed explanation of each numbered line of code follows the listing.
Listing 4-4 A routine that uses two ways to scale a QDPict picture
void MyScaleQDPict (QDPictRef picture, CFURLRef url) |
{ |
float scaleXY = 2.0; |
CGRect bounds = QDPictGetBounds (picture);// 1 |
float w = (bounds.size.width) * scaleXY; |
float h = (bounds.size.height) * scaleXY; |
CGRect scaledBounds = CGRectMake (0, 0, w, h); |
bounds.origin.x = 0; |
bounds.origin.y = 0; |
CGContextRef context = CGPDFContextCreateWithURL (url, NULL, NULL);// 2 |
if (context != NULL) |
{ |
/* page 1: scale without affecting patterns */ |
CGContextBeginPage (context, &scaledBounds); |
(void) QDPictDrawToCGContext (context, scaledBounds, picture);// 3 |
CGContextEndPage (context); |
/* page 2: scale everything */ |
CGContextBeginPage (context, &scaledBounds); |
CGContextScaleCTM (context, scaleXY, scaleXY);// 4 |
(void) QDPictDrawToCGContext (context, bounds, picture);// 5 |
CGContextEndPage (context); |
CGContextRelease (context); |
} |
} |
Here’s what the code does:
Creates a Quartz rectangle that represents the origin and size of the picture in user space. The resolution is 72 units per inch and the origin is (0,0).
Creates a PDF context that renders into a file. The choice of PDF is arbitrary—you can draw QDPict pictures in any type of Quartz graphics context.
Draws the picture into a scaled drawing rectangle. Patterns are not scaled along with the other graphic elements in the picture.
Applies the scaling transform to the current transformation matrix (CTM) in the graphics context. This scaling affects all subsequent drawing.
Draws the picture into a drawing rectangle with the same dimensions. This time the picture is scaled by the CTM, including patterns.
Last updated: 2006-09-05