< Previous PageNext Page > Hide TOC

Graphics Contexts

A graphics context represents a drawing destination. It contains drawing parameters and all device-specific information needed to render the paint on a page to the destination, whether the destination is a window in an application, a bitmap, a PDF document, or a printer. You can obtain a graphics context by using Quartz context creation functions or by using higher-level functions provided in the Carbon, Cocoa, or Printing frameworks. Quartz provides creation functions for various flavors of Quartz graphics contexts including bitmap and PDF. The Carbon and Cocoa frameworks provide functions for obtaining window graphics contexts. The Printing framework provides functions that obtain a graphics context appropriate for the kind of destination printer you are using.

This chapter shows you how to create a graphics context for a variety of drawing destinations. A graphics context is represented in your code by the data type CGContextRef, which is an opaque data type. After you obtain a graphics context you can use Quartz 2D functions to draw to the context, perform operations (such as translations) on the context, and change graphics state parameters, such as line width and fill color.

Creating a Window Graphics Context

The Quartz 2D API provides no functions to obtain a windows graphics context. Instead, you use the Cocoa framework to obtain a context for a window created in Cocoa, and the Carbon framework to obtain a context for a window created in Carbon. Obtaining a window graphics context from the Cocoa framework is fairly straightforward. The approach for obtaining a window graphics context from the Carbon framework depends on whether you are creating a new Mac OS X application or moving a QuickDraw-based application to Quartz 2D.

Window Graphics Context in Cocoa

You obtain a Quartz graphics context from within the drawRect: routine of a Cocoa application using the following line of code:

CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

The method currentContext returns the NSGraphicsContext instance of the current thread. The method graphicsPort returns the low-level, platform-specific graphics context represented by the receiver, which is a Quartz graphics context. (Don’t get confused by the method names; they are historical.) For more information see NSGraphicsContext.

After you obtain the graphics context, you can call any of the Quartz 2D drawing functions in your Cocoa application. You can also mix Quartz 2D calls with Cocoa drawing calls. You can see an example of Quartz 2D drawing to a Cocoa view by looking at Figure 2-1. The drawing consists of two overlapping rectangles, an opaque red one and a partially transparent blue one. You’ll learn more about transparency in “Color and Color Spaces.” The ability to control how much you can “see through” colors is one of the hallmark features of Quartz 2D.


Figure 2-1  A view in the Cocoa framework that contains Quartz drawing

A view in the Cocoa framework that contains Quartz drawing

To create the drawing in Figure 2-1, you first create a Cocoa application Xcode project. In Interface Builder, drag a Custom View to the window and subclass it. Then write an implementation for the subclassed view, similar to what Listing 2-1 shows. For this example, the subclassed view is named MyQuartzView. (You can name it whatever you like.) The drawRect: method for the view contains all the Quartz drawing code. A detailed explanation for each numbered line of code appears following the listing.

Note: The drawRect: method of the NSView class is invoked automatically each time the view needs to be drawn. To find out more information about overriding the drawRect: method, see NSView Class Reference.

Listing 2-1  Code that draws to a window graphics context

@implementation MyQuartzView
 
- (id)initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    return self;
}
 
- (void)drawRect:(NSRect)rect
{
    CGContextRef myContext = [[NSGraphicsContext // 1
                                currentContext]graphicsPort];
   // ********** Your drawing code here ********** // 2
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);// 3
    CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100 ));// 4
    CGContextSetRGBFillColor (myContext, 0, 0, 1, .5);// 5
    CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200));// 6
  }
 
@end

Here’s what the code does:

  1. Obtains a graphics context for the view.

  2. This is where you insert your drawing code. The four lines of code that follow are examples of using Quartz 2D functions.

  3. Sets a red fill color that’s fully opaque. For information on colors and alpha (which sets opacity), see “Color and Color Spaces.”

  4. Fills a rectangle whose origin is (0,0) and whose width is 200 and height is 100. For information on drawing rectangles, see “Paths.”

  5. Sets a blue fill color that’s partially transparent.

  6. Fills a rectangle whose origin is (0,0) and which width is 100 and height is 200.

Window Graphics Context in Carbon: HIView

If you are using the Carbon framework to create a new application for Mac OS X, you will want to use the HIToolbox API, and HIView in particular, for Quartz 2D drawing. HIView is the Quartz-based object-oriented view system available for implementing Carbon user interface elements in Mac OS X. You use Carbon events to obtain a window graphics context from an HIView by installing an event handler that responds to a draw event (kEventControlDraw). As long as the HIView is in a composited window, you can obtain the event parameter kEventParamCGContextRef from the draw event. Draw events for windows that are not composited do not contain this event parameter, which means you must use a composited window. If you can’t, see “Windows Graphics Context: QuickDraw.”

You need to perform these steps to draw to an HIView:

  1. In Xcode, create a Carbon application.

  2. Open the .nib file provided by Xcode and place an HIView in the main window.

    Compositing must be turned on for the window. It’s on by default, so make sure you don’t turn it off.

  3. Assign a signature and an ID to the view, as shown in Figure 2-2.

    You don’t have to use the signature and ID in the figure. Make note of what you assign to these items. You need to declare constants in your code that have these exact values. Otherwise, your code won’t draw to the view.

    Figure 2-2  A unique signature and control ID for an HIView

    A unique signature and control ID for an HIView
  4. In your application code, declare constants for the signature and ID.

  5. Install an event handler on the HIView that you want to draw to. The handler must process the event whose class and kind are {kEventClassControl, kEventControlDraw}.

  6. In your event handler, obtain a graphics context by calling the Carbon Event Manager function GetEventParameter and passing the constant kEventParamCGContextRef.

Listing 2-2 and Listing 2-3 show code that implements the previous steps. Listing 2-2 is the main routine and Listing 2-3 implements the event handler for the HIView. The handler obtains a graphics context for an HIView and then draws into that view. A detailed explanation for each numbered line of code appears following each listing.

Listing 2-2  The main routine in a Carbon drawing application

#define kMyHIViewSignature 'mVue'// 1
#define kMyHIViewFieldID 130
 
int main (int argc, char* argv[])
{
    IBNibRef            nibRef;
    OSStatus            err;
    WindowRef       myMainWindow;
    HIViewRef       myHIView;// 2
    static const EventTypeSpec  myHIViewSpec[] = {kEventClassControl,// 3
                                kEventControlDraw };
    static const HIViewID       myHIViewID = { kMyHIViewSignature,// 4
                                 kMyHIViewFieldID };
 
 
    err = CreateNibReference (CFSTR("main"), &nibRef);
    require_noerr (err, CantGetNibRef);
 
    err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar"));
    require_noerr (err, CantSetMenuBar);
 
    err = CreateWindowFromNib (nibRef, CFSTR("MainWindow"), &myMainWindow);
    require_noerr (err, CantCreateWindow );
    DisposeNibReference(nibRef);
 
    HIViewFindByID (HIViewGetRoot(myMainWindow), myHIViewID, &myHIView);    // 5
    err = InstallEventHandler (GetControlEventTarget (myHIView), // 6
                            NewEventHandlerUPP (MyDrawEventHandler),
                            GetEventTypeCount (myHIViewSpec),
                            myHIViewSpec,
                            (void *) myHIView,
                            NULL);
    ShowWindow (myMainWindow);
 
    RunApplicationEventLoop();
 
CantCreateWindow:
CantSetMenuBar:
CantGetNibRef:
 
    return err;
}

Here’s what the code does:

  1. Declares constants for the signature and ID that you assign to the HIView in Interface Builder. Make sure they match exactly and that the combination is unique to your application.

  2. Declares a variable for an HIViewRef data type to reference the HIView. You need this to set up the event handler.

  3. Declares an event specification for the draw event. This is the event your HIView event handler responds to. Your event handler can respond to as many events as you’d like. This example handles only the draw event so that you can see exactly what needs to be done to handle drawing.

  4. Declares an HIView ID using the constants for the signature and ID that you previously assigned and that uniquely identify the HIView in your application.

  5. Obtains the reference to the HIView you placed in the window.

  6. Calls the Carbon Event Manager function to install your event handler on the HIView.

Listing 2-3  An event handler for an HIView

OSStatus MyDrawEventHandler (EventHandlerCallRef myHandler,
                        EventRef event, void *userData)
{
    OSStatus status = noErr;
    CGContextRef myContext;
    HIRect      bounds;
 
    status = GetEventParameter (event, // 1
                            kEventParamCGContextRef,
                            typeCGContextRef,
                            NULL,
                            sizeof (CGContextRef),
                            NULL,
                            &myContext);
    require_noerr(status, CantGetGraphicsContext);
    HIViewGetBounds ((HIViewRef) userData, &bounds);// 2
    require_noerr(status, CantGetBoundingRectangle);
 
    // ********** Your drawing code here ********** // 3
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);// 4
    CGContextFillRect (myContext, CGRectMake(0, 0, 200, 100 ));// 5
    CGContextSetRGBFillColor (myContext, 0, 0, 1, .5);// 6
    CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200 ));// 7
CantGetGraphicsContext:
CantGetBoundingRectangle:
    return status;
 
}

Here’s what the code does:

  1. Calls the Carbon Event Manager function to obtain the graphics context from the event. You must pass:

    • The event to get the parameter from. The system passes this event to your handler. Recall that you registered for the draw event.

    • The symbolic name of the parameter you want to obtain. In this case, pass the system-defined constant kEventParamCGContextRef.

    • The desired type of the parameter, which you specify as the system-defined constant typeCGContextRef.

    • The actual type of the parameter, which is NULL because it’s not necessary to get this information for this example.

    • The size of the data you are obtaining.

    • The actual size of the data, which is NULL because it’s not necessary to get this information for this example.

    • A pointer to a CGContextRef data type which, on output, is the window graphics context for the view. This is what you’ll draw to.

  2. Obtains the bounding rectangle for the HIView. Note that this code assumes the userData passed to the event handler is an HIViewRef. Another approach you can take is to obtain the view by extracting the event parameter kEventParamDirectObject from the event.

  3. This is where you insert your drawing code. The four lines of code below this are examples of using Quartz 2D functions.

  4. Sets a red fill color that’s fully opaque. For information on colors and alpha (which sets opacity), see “Color and Color Spaces.”

  5. Fills a rectangle whose origin is (0,0) and whose width is 200 and height is 100. For information on drawing rectangles, see “Paths.”

  6. Sets a blue fill color that’s partially transparent.

  7. Fills a rectangle whose origin is (0,0) and which width is 100 and height is 200.

Figure 2-3 shows the output produced by the drawing code in Listing 2-3. Compare Figure 2-3 with the output produced by the same Quartz 2D drawing calls from a Cocoa application, and shown in Figure 2-1. Notice that one drawing is flipped with respect to the other.


Figure 2-3  An HIView uses HIView coordinates to display drawing

An HIView uses HIView coordinates to display drawing

The Quartz coordinate system, as mentioned in “Overview of Quartz 2D,” places the origin at the lower-left corner of the view. But HIView returns a context that places the origin a the upper-left corner of the view. Each origin has its advantages. HIView uses the upper left to ensure that the coordinates of objects, such as controls, do not change as the user resizes the window. If you want to use the Quartz coordinate system, you can add the following two lines of code to your event handler, placed just before your drawing code:

    CGContextTranslateCTM (myContext, 0, bounds.size.height);
    CGContextScaleCTM (myContext, 1.0, -1.0);

The first line of code translates the coordinate system so that the y values are moved towards the bottom of the HIView by the height of the HIView bounding rectangle. If you were to draw now, your drawing would be below the HIView, not in a visible area.

The second line of code flips the y-coordinates by a factor of –1.0. Because you just translated the coordinates below the HIView, the scaling effectively flips them into the HIView. After this operation, the origin is at the lower left of the HIView, with the y values increasing from bottom to top. The x values are unchanged; they still increase from left to right.

Figure 2-4 shows the output from Listing 2-3 after inserting the translation and scaling code before the drawing code in the handler. The sample code in the rest of this book uses the Quartz coordinate system. If you plan to use an HIView to try out the sample code from the book, you may want to transform the coordinates so that your output matches that shown in the figures.


Figure 2-4  An HIView that displays a drawing that uses transformed coordinates

An HIView that displays a drawing that uses transformed coordinates

For more information, see Carbon Event Manager Reference and HIView Programming Guide.

Windows Graphics Context: QuickDraw

If your application can’t use a compositing window, or if you are moving older QuickDraw code to Quartz, you might need an alternative to obtaining a graphics context from HIView. The QuickDraw functions QDBeginCGContext and QDEndCGContext provide such an alternative. The function QDBeginCGContext obtains a graphics context for a window port and signals the beginning of Quartz 2D drawing calls. The function QDEndCGContext signals the end of Quartz 2D drawing calls and restores the window port. These functions must be used in pairs.

The code in Listing 2-4 produces the output shown in Figure 2-5. A detailed explanation for each numbered line of code appears following Listing 2-4. Note in the figure that the graphics context provided by QDBeginCGContext uses Quartz coordinates. Unlike previous examples, the code draws to a window, not a view, which is why the drawing starts in the window corner and is not indented as it is in Figure 2-1, Figure 2-3, and Figure 2-4.


Figure 2-5  A window that contains a drawing from a graphics context obtained from QDBeginCGContext

A window that contains a drawing from a graphics context obtained from QDBeginCGContext

Listing 2-4  Code that obtains a graphics context from QDBeginCGContext

void MyDrawInWindow (WindowRef window)
{
    CGContextRef myContext;
 
    SetPortWindowPort (window);// 1
    QDBeginCGContext (GetWindowPort (window), &myContext);// 2
 
        // ********** Your drawing code here ********** // 3
        CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
        CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100));
        CGContextSetRGBFillColor (myContext, 0, 0, 1, .5);
        CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200));
        CGContextFlush(myContext);// 4
     QDEndCGContext (GetWindowPort(window), &myContext);// 5
 
}

Here’s what the code does:

  1. Sets the current graphics port to the window port.

  2. Returns a Quartz graphics context for the supplied color graphics port.

  3. Performs Quartz 2D drawing. You would replace this and the following four lines of code with the Quartz drawing routines appropriate for your application.

    Note: You can’t mix Quartz 2D routines with QuickDraw calls. You need to replace QuickDraw with Quartz 2D routines that provide similar functionality. For more information, see Quartz Programming Guide for QuickDraw Developers.

  4. Forces all pending drawing operations in a window graphics context to be rendered immediately to the destination device. Normally you don’t need to call this function. But you must call this function when you obtain a graphics context using the function QDBeginCGContext. As an alternative, you can call the function CGContextSynchronize, which marks a window graphics context for updating.

  5. Ends the Quartz 2D drawing session.

Creating a PDF Graphics Context

When you create a PDF graphics context and draw to that context, Quartz records your drawing as a series of PDF drawing commands written to a file. You supply a location for the PDF output and a default media box—a rectangle that specifies bounds of the page. Figure 2-6 shows the result of drawing to a PDF graphics context and then opening the resulting PDF in Preview.


Figure 2-6  A PDF created by using CGPDFContextCreateWithURL

A PDF created by using CGPDFContextCreateWithURL

The Quartz 2D API provides two functions that create a PDF graphics context:

A detailed explanation for each numbered line of code follows each listing.

Listing 2-5  A routine that calls CGPDFContextCreateWithURL to create a PDF graphics context

CGContextRef MyCreatePDFContext (const CGRect *inMediaBox,
                                    CFStringRef path)
{
    CGContextRef myOutContext = NULL;
    CFURLRef url;
 
    url = CFURLCreateWithFileSystemPath (NULL, // 1
                                path,
                                kCFURLPOSIXPathStyle,
                                false);
    if (url != NULL) {
        myOutContext = CGPDFContextCreateWithURL (url,// 2
                                        inMediaBox,
                                        NULL);
        CFRelease(url);// 3
    }
    return myOutContext;// 4
}

Here’s what the code does:

  1. Calls the Core Foundation function to create a CFURL object from the CFString object supplied to the MyPDFContextCreate function. You pass NULL as the first parameter to use the default allocator. You also need to specify a path style, which for this example is a POSIX-style pathname.

  2. Calls the Quartz 2D function to create a PDF graphics context using the PDF location just created (as a CFURL object) and a rectangle that specifies the bounds of the PDF. The rectangle (a CGRect) was passed to the MyPDFContextCreate function and is the default page media bounding box for the PDF.

  3. Releases the CFURL object.

  4. Returns the PDF graphics context. The caller must release the graphics context when it is no longer needed.

Listing 2-6  A routine that calls CGPDFContextCreate to create a PDF graphics context

CGContextRef MyCreatePDFContext (const CGRect *inMediaBox,
                                    CFStringRef path)
{
    CGContextRef        myOutContext = NULL;
    CFURLRef            url;
    CGDataConsumerRef   dataConsumer;
 
    url = CFURLCreateWithFileSystemPath (NULL, // 1
                                        path,
                                        kCFURLPOSIXPathStyle,
                                        false);
 
    if (url != NULL)
    {
        dataConsumer = CGDataConsumerCreateWithURL (url);// 2
        if (dataConsumer != NULL)
        {
            myOutContext = CGPDFContextCreate (dataConsumer, // 3
                                        inMediaBox,
                                        NULL);
            CGDataConsumerRelease (dataConsumer);// 4
        }
        CFRelease(url);// 5
    }
    return myOutContext;// 6
}

Here’s what the code does:

  1. Calls the Core Foundation function to create a CFURL object from the CFString object supplied to the MyPDFContextCreate function. You pass NULL as the first parameter to use the default allocator. You also need to specify a path style, which for this example is a POSIX-style path name.

  2. Creates a Quartz data consumer object using the CFURL object. If you don’t want to use a CFURL object (for example, you want to place the PDF data in a location that can’t be specified by a CFURL object), you can instead create a data consumer from a set of callback functions that you implement in your application. For more information, see “Data Management.”

  3. Calls the Quartz 2D function to create a PDF graphics context passing as parameters the data consumer and the rectangle (of type CGRect) that was passed to the MyPDFContextCreate function. This rectangle is the default page media bounding box for the PDF.

  4. Releases the data consumer.

  5. Releases the CFURL object.

  6. Returns the PDF graphics context. The caller must release the graphics context when it is no longer needed.

Listing 2-7 shows how to call the MyCreatePDFContext routine and draw to it. A detailed explanation for each numbered line of code appears following the listing.

Listing 2-7  Code that draws to a PDF graphics context

    CGRect mediaBox;// 1
 
    mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);// 2
    myPDFContext = MyCreatePDFContext (&mediaBox, CFSTR("test.pdf"));// 3
    CGContextBeginPage(myPDFContext, &mediaBox);// 4
        // ********** Your drawing code here **********// 5
        CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
        CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
        CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
        CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
    CGContextEndPage(myPDFContext);// 6
    CGContextRelease(myPDFContext);// 7

Here’s what the code does:

  1. Declares a variable for the rectangle that you use to define the PDF media box.

  2. Sets the origin of the media box to (0,0) and the width and height to variables supplied by the application.

  3. Calls the function MyCreatePDFContext (See Listing 2-6) to obtain a PDF graphics context, supplying a media box and a path name. The macro CFSTR converts a string to a CFStringRef data type.

  4. Signals the start of a page. This function is used for page-paged graphics, which is what PDF drawing is. This example passes the media box to define the page boundary. You don’t have to pass the same rectangle you used to set up the PDF graphics context. The rectangle you pass to CGContextBeginPage supersedes the rectangle you pass to set up the PDF graphics context.

    Note:  Starting in Mac OS X v10.4, you can call the function CGPDFContextBeginPage and its companion CGPDFContextEndPage to delineate PDF pages. You can provide a CFDictionary to the function CGPDFContextBeginPage to specify page properties.

  5. Calls Quartz 2D drawing functions. You replace this and the following four lines of code with the drawing code appropriate for your application.

  6. Signals the end of the PDF page.

  7. Releases the PDF graphics context when it is no longer needed.

You can write any content to a PDF that’s appropriate for your application—images, text, path drawing—and you can add links and encryption. For more information see “PDF Document Creation, Viewing, and Transforming.”

Creating a Bitmap Graphics Context

A bitmap graphics context accepts a pointer to a memory buffer that contains storage space for the bitmap. When you paint into the bitmap graphics context, the buffer is updated. After you release the graphics context, you have a fully updated bitmap in the pixel format you specify.

Note: Bitmap graphics contexts are sometimes used for drawing offscreen. Before you decide to use a bitmap graphics context for this purpose, see “CGLayer Drawing.” CGLayer objects (CGLayerRef), available in Mac OS X v10.4 and later, are optimized for offscreen drawing because, whenever possible, Quartz caches layers on the video card.

You use the function CGBitmapContextCreate to create a bitmap graphics context. This function takes the following parameters:

Listing 2-8 shows how to create a bitmap graphics context. When you draw into the resulting bitmap graphics context, Quartz records your drawing as bitmap data in the specified block of memory. A detailed explanation for each numbered line of code follows the listing.

Listing 2-8  A routine that creates a bitmap graphics context

CGContextRef MyCreateBitmapContext (int pixelsWide,
                            int pixelsHigh)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;
 
    bitmapBytesPerRow   = (pixelsWide * 4);// 1
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
 
    colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);// 2
    bitmapData = malloc( bitmapByteCount );// 3
    if (bitmapData == NULL)
    {
        fprintf (stderr, "Memory not allocated!");
        return NULL;
    }
    context = CGBitmapContextCreate (bitmapData,// 4
                                    pixelsWide,
                                    pixelsHigh,
                                    8,      // bits per component
                                    bitmapBytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaPremultipliedLast);
    if (context== NULL)
    {
        free (bitmapData);// 5
        fprintf (stderr, "Context not created!");
        return NULL;
    }
    CGColorSpaceRelease( colorSpace );// 6
 
    return context;// 7
}

Here’s what the code does:

  1. Declares a variable to represent the number of bytes per row. Each pixel in the bitmap in this example is represented by 4 bytes; 8 bits each of red, green, blue, and alpha.

  2. Creates a generic RGB color space. You can also create a CMYK color space. See “Color and Color Spaces” for more information and for a discussion of generic color spaces versus device dependent ones.

  3. Calls the malloc function to create a block of memory in which to store the bitmap data. This example creates a 32-bit RGBA bitmap (that is, an array with 32 bits per pixel, each pixel containing 8 bits each of red, green, blue, and alpha information). Each pixel in the bitmap occupies 4 bytes of memory.

  4. Creates a bitmap graphics context, supplying the bitmap data, the width and height of the bitmap, the number of bits per component, the bytes per row, the color space, and a constant that specifies whether the bitmap should contain an alpha channel and its relative location in a pixel. The constant kCGImageAlphaPremultipliedLast indicates that the alpha component is stored in the last byte of each pixel and that the color components have already been multiplied by this alpha value. See “The Alpha Value” for more information on premultiplied alpha.

  5. If the context isn’t created for some reason, frees the memory allocated for the bitmap data.

  6. Releases the color space.

  7. Returns the bitmap graphics context. The caller must release the graphics context when it is no longer needed.

Listing 2-9 shows code that calls MyCreateBitmapContext to create a bitmap graphics context, uses the bitmap graphics context to create a CGImage, then draws the resulting image to a window graphics context. Figure 2-7 shows the image drawn to the window. A detailed explanation for each numbered line of code follows the listing.

Listing 2-9  Code that draws to a bitmap graphics context

    CGRect myBoundingBox;// 1
 
    myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);// 2
    myBitmapContext = MyCreateBitmapContext (400, 300);// 3
    // ********** Your drawing code here ********** // 4
    CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
    CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
    CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
    CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
    myImage = CGBitmapContextCreateImage (myBitmapContext);// 5
    CGContextDrawImage(myContext, myBoundingBox, myImage);// 6
    char *bitmapData = CGBitmapContextGetData(myBitmapContext); // 7
    CGContextRelease (myBitmapContext);// 8
    if (bitmapData) free(bitmapData); // 9
    CGImageRelease(myImage);// 10

Here’s what the code does:

  1. Declares a variable to store the origin and dimensions of the bounding box into which Quartz will draw an image created from the bitmap graphics context.

  2. Sets the origin of the bounding box to (0,0) and the width and height to variables previously declared, but whose declaration are not shown in this code.

  3. Calls the application supplied function MyCreateBimapContext (see Listing 2-8) to create a bitmap context that is 400 pixels wide and 300 pixels high. You can create a bitmap graphics context using any dimensions that are appropriate for your application.

  4. Calls Quartz 2D functions to draw into the bitmap graphics context. You would replace this and the next four lines of code with drawing code appropriate for your application.

  5. Creates a Quartz 2D image (CGImageRef) from the bitmap graphics context. The function CGBitmapContextCreateImage is available in Mac OS X v10.4 and later. For information on creating Quartz 2D images from a bitmap graphics context in earlier versions of Mac OS X, see “Bitmap Images and Image Masks.”

  6. Draws the image into the location in the window graphics context that is specified by the bounding box. The bounding box specifies the location and dimensions in user space in which to draw the image.

    This example does not show the creation of the window graphics context. See “Creating a Window Graphics Context” for information on how to create one.

  7. Gets the bitmap data associated with the bitmap graphics context.

  8. Releases the bitmap graphics context when it is no longer needed.

  9. Free the bitmap data if it exists.

  10. Releases the image when it is no longer needed.


Figure 2-7  An image created from a bitmap graphics context and drawn to a window graphics context

An image created from a bitmap graphics context and drawn to a window graphics context

Supported Pixel Formats

Table 2-1 summarizes the pixel formats that are supported for bitmap graphics context, the associated color space (cs), and the format availability in versions of Mac OS X. The pixel format is specified as bits per pixel (bpp) and bits per component (bpc). The table also includes the bitmap information constant associated with that pixel format. See CGImage Reference for details on what each of the bitmap information format constants represent.

Table 2-1  Color space (CS), pixel formats, and availability information

CS

Pixel Format and Bitmap Information Constant

Availability

Gray

8 bpp, 8 bpc,kCGImageAlphaNone

10.0

Null

8 bpp, 8 bpc, kCGImageAlphaOnly

10.3

RGB

16 bpp, 5 bpc, kCGImageAlphaNoneSkipFirst

10.0

RGB

32 bpp, 8 bpc, kCGImageAlphaNoneSkipFirst

10.0

RGB

32 bpp, 8 bpc, kCGImageAlphaNoneSkipLast

10.0

RGB

32 bpp, 8 bpc, kCGImageAlphaPremultipliedFirst

10.0

RGB

32 bpp, 8 bpc, kCGImageAlphaPremultipliedLast

10.0

CMYK

32 bpp, 8 bpc, kCGImageAlphaNone

10.3

Gray

32 bpp, 32 bpc, kCGImageAlphaNone|kCGBitmapFloatComponents

10.4

RGB

128 bpp, 32 bpc, kCGImageAlphaNoneSkipLast |kCGBitmapFloatComponents

10.4

RGB

128 bpp, 32 bpc, kCGImageAlphaPremultipliedLast |kCGBitmapFloatComponents

10.4

CMYK

128 bpp, 32 bpc, kCGImageAlphaNone |kCGBitmapFloatComponents

10.4

Gray

16 bpp, 16 bpc, kCGImageAlphaNone

10.5

RGB

64 bpp, 16 bpc, kCGImageAlphaPremultipliedLast

10.5

RGB

64 bpp, 16 bpc, kCGImageAlphaNoneSkipLast

10.5

CMYK

64 bpp, 16 bpc, kCGImageAlphaNone

10.5

Anti-Aliasing

Bitmap graphics contexts support anti-aliasing, which is the process of artificially correcting the jagged (or aliased) edges you sometimes see in bitmap images when text or shapes are drawn. These jagged edges occur when the resolution of the bitmap is significantly lower than the resolution of your eyes. To make objects appear smooth in the bitmap, Quartz uses different colors for the pixels that surround the outline of the shape. By blending the colors in this way, the shape appears smooth. You can see the effect of using anti-aliasing in Figure 2-8. You can turn anti-aliasing off for a particular bitmap graphics context by calling the function CGContextSetShouldAntialias. The anti-aliasing setting is part of the graphics state.

As of Mac OS X v10.4 you can control whether or not to allow anti-aliasing for a particular graphics context by using the function CGContextSetAllowsAntialiasing. Pass true to this function to allow anti-aliasing; false not to allow it. This setting is not part of the graphics state. Quartz performs anti-aliasing for a graphics context if you allow anti-aliasing by passing true to CGContextSetAllowsAntialiasing and you set the anti-aliasing graphics state parameter to true by calling CGContextSetShouldAntialias.


Figure 2-8  A comparison of aliased and anti-aliasing drawing

A comparison of aliased and anti-aliasing drawing

Obtaining a Graphics Context for Printing

When you use the Carbon Printing Manager to print from an application in Mac OS X, you call the function PMSessionGetGraphicsContext to obtain a graphics context for each page you print. The Carbon Printing Manager manages the graphics context for your application so that your documents are output appropriately, whether to a raster printer or as PostScript data.

The code in Listing 2-10 is an excerpt from a more complex printing routine. The purpose is to point out the basic calls you need to make to print to a Quartz graphics context, and not to show you how to use the Carbon Printing Manager. You can find detailed information on how to print in Supporting Printing in Your Carbon Application and Carbon Printing Manager Reference.

A detailed explanation for each numbered line of code appears following the listing. Within the code, embedded comments provide you with additional guidance.

Listing 2-10  Code that prints to a Quartz graphics context

    CFStringRef         strings[1];
    CFArrayRef          myGraphicsContextsArray;
    CGContextRef        printingContext;
    PMPrintSession      printSession;
 
    strings[0] = kPMGraphicsContextCoreGraphics; // 1
    myGraphicsContextsArray = CFArrayCreate (kCFAllocatorDefault,// 2
                        (const void **) strings,
                        1,
                        &kCFTypeArrayCallBacks);
    if (myGraphicsContextsArray != NULL)
    {
        PMSessionSetDocumentFormatGeneration (printSession,// 3
                            kPMDocumentFormatPDF,
                            myGraphicsContextsArray,
                            NULL);
    CFRelease (myGraphicsContextsArray);
    }
 
    // More of your print loop code here
    // Your call to PMSessionBeginDocument
    // Your call to PMSessionBeginPage here
 
    PMSessionGetGraphicsContext (printSession,// 4
                                kPMGraphicsContextCoreGraphics,
                                (void **) &printingContext);
    // Use Quartz 2D routines to draw content to the context
    // Then, continue your print loop

Here’s what the code does:

  1. Assigns the Carbon Printing Manager constant kPMGraphicsContextCoreGraphics to the first element of an array of strings. This constant specifies a Quartz 2D context.

  2. Calls the Core Foundation array creation function to create an array that you later pass as a parameter to the function PMSessionSetDocumentFormatGeneration.

  3. Calls the Carbon Printing Manager function PMSessionSetDocumentFormatGeneration to request a spool file format and supply the graphics context types to use for drawing pages within the print loop. You pass four parameters:

    • A print session—the code here assumes the print session was already created. For more information see Carbon Printing Manager Reference.

    • A spool file format. Pass kPMDocumentFormatPDF.

    • A graphics context array. This array has one item in it (kPMGraphicsContextCoreGraphics) to specify a Quartz graphics context, and not a QuickDraw context. (QuickDraw is what older Mac OS 9 applications use because Quartz is not available prior to Mac OS X.)

    • NULL. This parameter is reserved for future use.

  4. Calls the Carbon Printing Manager function PMSessionGetGraphicsContext to obtain a Quartz graphics context for drawing. After you obtain the context, you can use Quartz 2D routines to draw content to the context. Then, continue with the appropriate print loop code. (Note that you cannot use QuickDraw routines when you print to a Quartz graphics context.)

    You call the function PMSessionGetGraphicsContext for each page you print.



< Previous PageNext Page > Hide TOC


© 2001, 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-12-11)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.