iOS Reference Library Apple Developer
Search

Graphics Contexts

A graphics context represents a drawing destination. It contains drawing parameters and all device-specific information that the drawing system needs to perform any subsequent drawing commands. A graphics context defines basic drawing attributes such as the colors to use when drawing, the clipping area, line width and style information, font information, compositing options, and several others.

You can obtain a graphics context by using Quartz context creation functions or by using higher-level functions provided by one of the Mac OS X frameworks or the UI Kit framework in iOS. Quartz provides functions for various flavors of Quartz graphics contexts including bitmap and PDF, which you can use to create custom content.

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.

Drawing to a Graphics Context in iOS

In an iOS application, you set up a UIView object to draw to and implement the drawRect: method to perform drawing. Before calling your custom drawRect: method, the view object automatically configures its drawing environment so that your code can start drawing immediately. As part of this configuration, the UIView object creates a graphics context (a CGContextRef opaque type) for the current drawing environment. You obtain this graphics context by calling the UIKit function UIGraphicsGetCurrentContext. You save and restore graphics contexts using the functions UIGraphicsPushContext and UIGraphicsPopContext.

You can create custom graphics context objects in situations where you want to draw somewhere other than your view. For example, you may want to capture a series of drawing commands and use them to create an image or a PDF file. To create the context, you use the CGBitmapContextCreate or CGPDFContextCreate function. After you have the context, you can pass it to the drawing functions needed to create your content.

When creating custom contexts, the coordinate system for those contexts is different from the native coordinate system used by iOS. Instead of the origin being in the upper-left corner of the drawing surface, it is in the lower-left corner and the axes point up and to the right. The coordinates you specify in your drawing commands must take this into consideration or the resulting image or PDF file may appear wrong when rendered. See “Creating a Bitmap Graphics Context” and “Creating a PDF Graphics Context” for details on using CGBitmapContextCreate and CGPDFContextCreate.

Important: Because you use a lower-left origin when drawing into a bitmap or PDF context, you must compensate for that coordinate system when rendering the resulting content into a view. In other words, if you create an image and draw it using the CGContextDrawImage function, the image will appear upside down by default. To correct for this, you must invert the y-axis of the CTM (by multiplying it by -1) and shift the origin from the lower-left corner to the top-right corner of the view.

If you use a UIImage object to wrap a CGImageRef you create, you do not need to modify the CTM. The UIImage object automatically compensates for the inverted coordinate system of the CGImageRef type.

Creating a Window Graphics Context

When drawing in Mac OS X, you need to create a window graphics context that’s appropriate for the framework you are using. The Quartz 2D API itself 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.

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 Class Reference.

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 whose 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 drawing event. Draw events for windows that are not composited do not contain this event parameter, which means you must use a composited window.

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 of type 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 at 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.

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-5 shows the result of drawing to a PDF graphics context and then opening the resulting PDF in Preview.

Figure 2-5  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.

iOS Note: If you want to create a PDF graphics context in an iOS application, make sure you also read ‚ÄúDrawing to a Graphics Context in iOS.‚Äù

Listing 2-4  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 path name.

  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-5  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 pathname.

  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 in Mac OS X.”

  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-6 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-6  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-5) to obtain a PDF graphics context, supplying a media box and a pathname. The macro CFSTR converts a string to a CFStringRef data type.

  4. Signals the start of a page. This function is used for page-oriented 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: In Mac OS X v10.4 and later, 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-7 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.

iOS Note: If you want to create a bitmap graphics context in an iOS application, make sure you also read ‚ÄúDrawing to a Graphics Context in iOS.‚Äù

Listing 2-7  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-8 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-6 shows the image drawn to the window. A detailed explanation for each numbered line of code follows the listing.

Listing 2-8  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-7) 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-6  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 version of Mac OS X in which the format was first available. 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-7. 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.

In Mac OS X v10.4 and later 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-7  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-9 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-9  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.

    • 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.

    You call the function PMSessionGetGraphicsContext for each page you print.




Last updated: 2010-06-25

Did this document help you? Yes It's good, but... Not helpful...