TweetFollow Us on Twitter

September 94 - Making the Most of QuickDraw GX Bitmaps

Making the Most of QuickDraw GX Bitmaps

DAVID SUROVELL

[IMAGE 048-064_Surovell_final_h1.GIF]

Besides letting you do a lot of cool things with geometric shapes and typography, QuickDraw GX has useful tools for manipulating bitmaps. For example, bitmap shapes (the QuickDraw GX counterpart to pixMaps) can be skewed, rotated, and scaled, and transforms allow these operations to be performed repeatedly without data loss. Bitmap shapes can share image data, can be used to clip other shapes, and can reside on disk instead of in memory. This article tells how you can use QuickDraw GX to improve the way you handle bitmapped graphics.

New users of QuickDraw GX will probably start by going throughInside Macintosh: QuickDraw GX Objects or the article "Getting Started With QuickDraw GX" indevelop Issue 15. If you're mainly a QuickDraw programmer, however, you may have a lot of questions about how QuickDraw GX applies specifically to bitmaps -- probably the most commonly used graphic objects. As it turns out, it can do most anything QuickDraw can do, and quite a few useful and exotic new things besides.

If you have at least a nodding familiarity with QuickDraw GX, this article will give useful tips on how to apply your knowledge to bitmap shapes. If you're a QuickDraw GX neophyte, this article will confuse you from time to time, but you may learn enough to decide to make the leap to QuickDraw GX.

CREATING BITMAP SHAPES

It takes about the same information to create a bitmap shape in QuickDraw GX as it does to make a pixMap in QuickDraw. The biggest difference is that while QuickDraw insists that you calculate the size of the image buffer and allocate it explicitly, QuickDraw GX can optionally allocate it for you when the shape is created. This is illustrated in the code in Listing 1, which creates an indexed bitmap shape.

For indexed pixelSize values (1, 2, 4, or 8), you set the gxBitmap's space field to gxIndexedSpace and its set field to a color set (the QuickDraw GX equivalent of a QuickDraw color table) with an appropriate number of entries. Direct pixelSize values (16 or 32) require that the set field be nil. Forexample, to make the routine in Listing 1 create a 16-bit bitmap shape, you would set the gxBitmap's space field to gxRGB16Space and its set field to nil.



Listing 1. Creating an indexed bitmap shape

gxShape CreateIndexedBitmapShape(long horiz, long vert,
        long targetDepth)
{
    gxBitmap        bitShapeInfo;
    gxColorSet  targetSet;
    gxShape     resultShape;

    if ((horiz <= 0) || (vert <= 0))
        return nil;
    if (targetDepth > 8)
        return nil;

    // Create a familiar "color" gxColorSet.
    // (The default gxColorSet is a gray ramp.) 
    targetSet = GetStandardColorSet(targetDepth);
    if (targetSet == nil)
        return nil;

    // Let QDGX calculate the image buffer block size and
    // allocate it.
    bitShapeInfo.image = nil;
    bitShapeInfo.rowBytes = 0;
    bitShapeInfo.width = horiz;
    bitShapeInfo.height = vert;
    bitShapeInfo.pixelSize = targetDepth;
    bitShapeInfo.space = gxIndexedSpace;
    bitShapeInfo.set = targetSet;

    // Use the default color profile. 
    bitShapeInfo.profile = nil;
    resultShape = GXNewBitmap(&bitShapeInfo, nil);

    return resultShape;
}

Note that the gxBitmap's rowBytes is a long, not a short as in QuickDraw. This means no more convoluted rowByte hacks, no more magic bits needed for flags, and no more unreasonable limits on image width.

Note also that the gxBitmap contains a profile field, a reference to a gxColorProfile (essentially an object with ColorSync data wrapped inside). If this field is nil, QuickDraw GX uses its default profile. Color matching occurs only when the target view port has the gxEnableMatchPort attribute set -- by default, it's off.

MANIPULATING BITMAP SHAPES

Once a bitmap shape is created, you can access and change its characteristics with GXGetBitmap and GXSetBitmap.

GXGetBitmap(targetShape, &bitmapInfo, &origin);
// Alter the necessary gxBitmap fields here.
. . .
GXSetBitmap(targetShape, &bitmapInfo, &origin);
GXSetBitmap is similar to QuickDraw's UpdateGWorld; it lets you change bitmap depth, color specification, and size. To change specific attributes, you may need to modify a combination of fields.

To change a bitmap's width or height, set the width or height field. If QuickDraw GX originally allocated the image buffer, you can set rowBytes to 0 and the image field to nil, and QuickDraw GX will reallocate the buffer. If you allocated the buffer yourself, you'll have to maintain it yourself.

An image isn't scaled when you change size this way. If you increase the width or height, the new areas contain undefined values; if you decrease them, the image is truncated. Bitmap scaling is discussed later in this article.*

To change a bitmap's pixel depth, set the pixelSize field to the desired depth. If the bitmap needs a new color set (which it will, unless the new depth is greater than 8 bits), create it and assign it to the set field. An example that changes the depth to 4-bit is shown in Listing 2.

To change a bitmap's color characteristics, just change the set, space, and profile fields. No changes to pixel data will occur -- all pixel values will be interpreted in the new color set. To transform pixel values, you'd need to set up a new bitmap shape and draw the existing bitmap into it. (The offscreen library routine CopyToBitmaps is ideal for this.)



Listing 2. Changing the depth of a bitmap shape

void ChangeDepthToFour(gxShape bitmapShape)
{
    gxBitmap    imageInfo;

    if ((bitmapShape != nil) && 
            (GXGetShapeType(bitmapShape) == gxBitmapType))
    {
        GXGetBitmap(bitmapShape, &imageInfo, nil);
        if (imageInfo.pixelSize != 4)
        {
            imageInfo.pixelSize = 4;
            imageInfo.space = gxIndexedSpace;
            imageInfo.set = GetStandardColorSet(4);
            GXSetBitmap(bitmapShape, &imageInfo, nil);
        }
    }
}

USING DISK-BASED PIXEL IMAGES
QuickDraw GX provides support for disk-based bitmap shapes. They're structurally the same as regular bitmaps, except that their image data is contained in a file, so they're always drawn from disk. Ten calls to GXDrawShape(diskBitmap) means QuickDraw GX reads the entire file from disk ten times. (QuickDraw GX can't assume that you didn't write into the file between accesses.) The idea is that the file system's disk caches will do the work; if the file wasn't changed, subsequent reads should be cached.

Make sure the file size is at least as large as the bitmap, or you'll get an "unexpected end of file" error. *

Disk-based bitmaps have limitations. For one thing, certain routines can't be performed on them -- GXSetShapePixel, for example. (SeeInside Macintosh: QuickDraw GX Graphics for the complete list.) You can't use disk-based bitmap shapes as drawing destinations. If you draw into the data you trigger an error. So how do you create a disk-based bitmap? As shown in Listing 3, you first set the gxBitmap's image field to gxBitmapFileAliasImageValue. After creating the bitmap shape, create a tag of type gxBitmapFileAliasTagType containing an alias record that references the file containing the target raster data and attach it to the shape.

ACCESSING IMAGE DATA
You can manipulate the image data of bitmap shapes directly. If the image data is maintained by your application, all you have to do is call GXChangedShape afterward. If the image data was allocated by QuickDraw GX, it's more complicated:

  1. Force the shape to be heap-resident with GXSetShapeAttributes.
  2. Lock the shape with GXLockShape and check for an error.
  3. Call GXGetShapeStructure to obtain a reference to the image data.
  4. Read from or write to the image data as desired.
  5. If the image data was changed, call GXChangedShape.
  6. Unlock the shape with GXUnlockShape.
  7. Call GXSetShapeAttributes to allow the shape to be cached again.


Listing 3. Creating a disk-based bitmap

gxShape CreateDiskBitmap(FSSpec *fsData, gxBitmap *targetBM)
{
    gxBitmap        localBM;
    gxShape         targetShape;
    gxTag           targetTag;

    if ((fsData == nil) || (targetBM == nil))
        return nil;
    targetShape = nil;
    targetTag = CreateBitmapAliasTag(fsData, 0L);
    if (targetTag != nil)
    {
        localBM = *targetBM;
        localBM.image = gxBitmapFileAliasImageValue;
        targetShape = GXNewBitmap(&localBM, nil);
        if (targetShape != nil)
            GXSetShapeTags(targetShape, gxBitmapFileAliasTagType,
                           1L, -1L, 1L, &targetTag);
        GXDisposeTag(targetTag);
    }
    return targetShape;
}

gxTag CreateBitmapAliasTag(FSSpec *bitmapFS,
         unsigned long fileOffset)
{
    struct gxBitmapDataSourceAlias  *aliasRecordPtr;
    gxTag           targetTag;
    FSSpec          targetFS;
    AliasHandle     aliasHdl;
    OSErr           iErr;
    long            aliasSize, aliasRecordSize;
    Boolean         wasChanged;

    targetTag = nil;
    aliasHdl = nil;
    aliasRecordPtr = nil;

    // Create an alias and resolve it.
    iErr = NewAlias(nil, bitmapFS, &aliasHdl);
    if (iErr == noErr)
        iErr = ResolveAlias(nil, aliasHdl, &targetFS, &wasChanged);

    // Build up a compact representation for inclusion into a gxTag.
    if (iErr == noErr)
    {
        aliasSize = GetHandleSize((Handle)aliasHdl);
        aliasRecordSize = aliasSize + 2 * sizeof(long);
        aliasRecordPtr = (struct gxBitmapDataSourceAlias*)
                                NewPtr(aliasRecordSize);
        iErr = MemError();
    }
    // Create the gxTag.
    if (iErr == noErr)
    {
        // Create a gxBitmapDataSourceAlias with specified fileOffset
        // and appropriate aliasRecordSize and aliasRecord.
        aliasRecordPtr->fileOffset = fileOffset;
        aliasRecordPtr->aliasRecordSize = aliasSize;
        BlockMove(*aliasHdl, &aliasRecordPtr->aliasRecord[0],
            aliasSize);
        targetTag = GXNewTag(gxBitmapFileAliasTagType,
                       aliasRecordSize, aliasRecordPtr);
    }
    // Clean up.
    if (aliasHdl != nil)
        DisposeHandle((Handle)aliasHdl);
    if (aliasRecordPtr != nil)
        DisposePtr((Ptr)aliasRecordPtr);

    return targetTag;
}

GXLockShape loads an image into memory, so it might not succeed if there isn't enough memory. And don't forget to check a bitmap shape's space field before processing the shape -- don't assume that bitmap images are always in RGB space.

See Listing 4 for an example of changing a bitmap shape's data directly.

MEMORY ISSUES
Raster surfers and Photoshop junkies know that raster images can be memory hogs; it's easy to run out of application heap when you allocate them. So what happens when QuickDraw GX runs out of memory? It doesn't. Well, almost never. Here are the steps it will go through, in order, to deliver the memory you need:

  1. Flush out-of-date caches.
  2. Flush up-to-date caches.
  3. If allowed, grow the current gxHeap.
  4. Unload shapes and other objects to disk.
  5. Give up, and return an error.

Most QuickDraw developers resort to some sort of GrowZoneProc to handle a tight application heap. QuickDraw GX provides a tiered response to abnormal occurrences. Items 1 through 4 above return notices (in the debugging version of QuickDraw GX); item 5 returns an error. All you have to do is implement a routine to handle the notices and errors.

Listing 4. Directly changing an indexed bitmap shape

void InvertBitmapShape(gxShape sourceBits)
{
    gxBitmap            sourceInfo, *sourceInfoRef;
    gxShapeAttribute    curAttributes;
    unsigned char       *sourcePtr, *rowPtr;
    long                sourceRowSize, structLen, i, j;
    Boolean             isQDGXImage;

    // Make sure that this is an indexed bitmap shape. 
    if (sourceBits == nil)
        return;
    if (GXGetShapeType(sourceBits) != gxBitmapType)
        return;
    GXGetBitmap(sourceBits, &sourceInfo, nil);
    if (sourceInfo.pixelSize > 8)
        return;
    if (sourceInfo.image == gxBitmapFileAliasImageValue)
        return;
    // If the image data was allocated by QuickDraw GX... 
    isQDGXImage = (sourceInfo.image == nil);
    if (isQDGXImage)
    {
        // Load and lock the image data. 
        curAttributes = GXGetShapeAttributes(sourceBits);
        if (!(curAttributes & gxDirectShape))
            GXSetShapeAttributes(sourceBits,
                curAttributes | gxDirectShape);
        GXLockShape(sourceBits);
        if (GXGraphicsError(nil) != 0)
            return;

        // Get a reference to the image data. 
        sourceInfoRef =
            (gxBitmap*)GXGetShapeStructure(sourceBits, &structLen);
        if ((sourceInfoRef == nil) || (structLen < sizeof(gxBitmap)))
            return;

        sourceInfo = *sourceInfoRef;
    }

    // Invert index values, one row at a time. 
    sourcePtr = (unsigned char*)(sourceInfo.image);
    for (i = sourceInfo.height; i > 0; i--)
    {
        rowPtr = sourcePtr;
        sourceRowSize = sourceInfo.rowBytes;
        while (sourceRowSize-- > 0)
        {
            *rowPtr = ~*rowPtr;
            rowPtr++;
        }
        // Skip to the next row.
        sourcePtr = (unsigned char*)sourcePtr + sourceInfo.rowBytes;
    }

    GXChangedShape(sourceBits);
    if (isQDGXImage)
    {
        GXUnlockShape(sourceBits);
        GXSetShapeAttributes(sourceBits, curAttributes);
    }
}

GEOMETRIC OPERATIONS

One of the niftiest features of QuickDraw GX is the ability to perform geometric operations on bitmap shapes. Most of the operators that apply to geometric shapes also apply to bitmaps: rotate, scale, skew, perspective, and clip. In comparison, QuickDraw provides only three geometric operators: scale, clip, and mask.

ALTERING THE TRANSFORM VERSUS THE GEOMETRY
When you change a bitmap shape's geometry (that is, its actual pixel data), whether by rotating, skewing, applying perspective, or scaling, you normally lose image data -- it's often impossible to return the image to its pristine state.

You can eliminate this data loss by instead applying geometric operators to a shape'stransform. A shape can make use of a 3 x 3 matrix to mathematically change its appearance when rendered without changing the underlying data. This is especially important for bitmaps. Figure 2 shows both possibilities of multiple rotations of a bitmap.

[IMAGE 048-064_Surovell_final_h2.GIF]

Figure 2. Successive rotations of a bitmap

Rotation, translation (change in origin), skew, perspective, and scale operations can all be performed on transforms directly, by GXRotateTransform, GXSkewTransform, and so forth, or indirectly, using the gxMapTransformShape attribute.

When a shape's gxMapTransformShape attribute is set, geometric operations automatically apply to its transform rather than its geometry. Bitmap and picture shapes default to having this attribute set; other shapes begin with it off. This means that if you convert a polygon shape (for example) to a bitmap shape, the gxMapTransformShape attribute won't automatically be set.

[IMAGE 048-064_Surovell_final_h3.GIF]

Figure 3. Effect of GXRotateShape on bitmap geometry

When a QuickDraw GX routine modifies a bitmap shape's geometry, a clip shape is often attached to define the geometric extent of the modified bitmap. More often than not, the bitmap's image buffer is expanded, as shown in Figure 3. Rotating a bitmap's geometry can increase its memory requirements by over 40%.

ROTATION
There aren't many QuickDraw programmers who haven't wished for a simple way to rotate bitmaps. GXRotateShape takes parameters for the target shape, degrees clockwise to rotate, and center point of rotation, as shown in Listing 5.


Listing 5. Rotating a bitmap shape

void RotateBitmap(gxShape targetShape, Fixed theta)
{
    gxBitmap    targetBM;
    gxPoint     origin, shCenter;

    // Determine the bitmap shape's current center point.
    GXGetBitmap(targetShape, &targetBM, &origin);
    shCenter.x = ff(targetBM.width) / 2 + origin.x;
    shCenter.y = ff(targetBM.height) / 2 + origin.y;

    // Rotate it around its center point.
    GXRotateShape(targetShape, theta, shCenter.x, shCenter.y);
}

SKEWING AND PERSPECTIVE
Skewing and perspective are just as much fun as rotation, and even more useful as general-purpose graphic effects. The code in Listing 6 illustrates a simple type of perspective; Figure 4 shows the results of this perspective mapping.

SCALING
You can expand or shrink bitmap shapes, like other shape types, with GXScaleShape. QuickDraw pixMaps are scaled by setting the destination rectangle passed to CopyBits, whereas GXScaleShape uses a scaling factor. To convert your QuickDraw bitmap scaling code into the equivalent QuickDraw GX code, you have to calculate this scaling factor. Listing 7 shows how.

You can flip a bitmap horizontally or vertically by using negative scaling values. *



Listing 6. Applying perspective to a bitmap shape

void TrapezoidalWarp(void)
{
    gxShape bitsShape, warpShape;
    long        trapezoidData[] =
    {
        1L, 4L,
        ff(130), ff(100), ff(170), ff(100),
        ff(200), ff(200), ff(100), ff(200)
    };

    bitsShape = CreateBasicBitmapShape();
    warpShape = GXNewShapeVector(gxPolygonType, trapezoidData);
    if (warpShape != nil)
    {
        ShapeSetPolyMap(bitsShape, warpShape);
        GXDisposeShape(warpShape);
    }
    GXDrawShape(bitsShape);
}

void ShapeSetPolyMap(gxShape targetShape, gxShape mappingShape)
{
    gxRectangle     boundsRect;
    gxPolygon       *mapPoly, *targetPoly;
    gxMapping       theMapping;
    gxShape         targetBounds;
    long            ignored;

    if (targetShape == nil)
        return;
    if ((mappingShape == nil)
            || (GXGetShapeType(mappingShape) != gxPolygonType))
        return;

    // Determine the dimensions of the target shape. 
    GXGetShapeBounds(targetShape, 0L, &boundsRect);
    targetBounds = GXNewRectangle(&boundsRect);
    if (targetBounds == nil)
        return;

    // Scale the mapping shape to the dimensions of the target shape.
    GXSetShapeBounds(mappingShape, &boundsRect);
    GXSetShapeType(targetBounds, gxPolygonType);

    // Load & lock both shapes so that their structures can be
    // accessed.
    GXSetShapeAttributes(mappingShape,
        GXGetShapeAttributes(mappingShape) | gxDirectShape);
    GXLockShape(mappingShape);
    GXSetShapeAttributes(targetBounds,
        GXGetShapeAttributes(targetBounds) | gxDirectShape);
    GXLockShape(targetBounds);
    // NOTE: Structure is actually of type gxPolygon. 
    mapPoly = 
        (gxPolygon*)GXGetShapeStructure(mappingShape, &ignored);
    targetPoly =
        (gxPolygon*)GXGetShapeStructure(targetBounds, &ignored);

    if ((mapPoly != nil) && (targetPoly != nil))
    {
        // Skip past the gxPolygons contour count to the first
        // contour.
        mapPoly = (gxPolygon*)((Ptr)mapPoly + sizeof(long));
        targetPoly = (gxPolygon*)((Ptr)targetPoly + sizeof(long));

        // Calculate the desired shape mapping. 
        // PolyToPolyMap() is in "mapping library.c." 
        PolyToPolyMap(targetPoly, mapPoly, &theMapping);
    }

    // Release both shapes from bondage. 
    GXUnlockShape(mappingShape);
    GXSetShapeAttributes(mappingShape,
        GXGetShapeAttributes(mappingShape) & ~gxDirectShape);
    GXUnlockShape(targetBounds);
    GXSetShapeAttributes(targetBounds,
        GXGetShapeAttributes(targetBounds) & ~gxDirectShape);

    // Set the target shape's mapping as desired. 
    GXSetShapeMapping(targetShape, &theMapping);

    GXDisposeShape(targetBounds);
}

[IMAGE 048-064_Surovell_final_h5.GIF]

Figure 4. Applying perspective to a bitmap shape Listing 7. Calculating a scaling factor

void BitmapShapeScaleQDStyle(gxShape targetShape, Rect *qdSourceR,
            Rect *qdDestR)
{
    gxPoint     centerPt;
    fixed       scaleFactorH, scaleFactorV;

    scaleFactorH = FixRatio(qdSourceR.right - qdSourceR.left,
                            qdDestR.right - qdDestR.left);
    scaleFactorV = FixRatio(qdSourceR.bottom - qdSourceR.top,
                            qdDestR.bottom - qdDestR.top);
    centerPt.x = ff((qdDestR.right + qdDestR.left) / 2);
    centerPt.y = ff((qdDestR.bottom + qdDestR.top) / 2);
    GXScaleShape(targetShape, scaleFactorH, scaleFactorV, centerPt.x,
                 centerPt.y);
    GXMoveShapeTo(targetShape, ff(qdDestR.left), ff(qdDestR.top));
    GXDrawShape(targetShape);
}

CLIPPING AND MASKING
QuickDraw GX can do some neat tricks with clipping. These tricks work with bitmap shapes, too. For example, to create a gradient-filled polygon, you can make a rectangular bitmap shape with a gradient and then set the polygon shape as the bitmap's clip shape. (For another example, see Graphical Truffles in this issue.)

You can use 1-bit bitmap shapes as clip shapes, too. The effect is just like that of CopyMask; pixels in the source shape are drawn only where the clipping bitmap pixel value is nonzero. (On this issue's CD, you'll also find example code that does image processing similar to CopyDeepMask using the new transfer modes.)

Clipping occurs in geometry space, before transform mapping, so a bitmap's clip shape should be based on its bounds rectangle, not its rendered location. *

To convert geometric shapes into masking bitmap shapes, you can call the GXSetShapeType routine to convert the shape to a 1-bit mask bitmap.

With GXCheckBitmapColor, you can generate a masking bitmap from an existing bitmap shape. If you pass GXCheckBitmapColor a color set, it puts 0 in the result bitmap for source pixel values that are in the color set. If you pass it a color profile, it puts 0 in the result bitmap for source pixel values that are within the color profile's gamut. The result bitmap can be useful for color correction.

QUICKDRAW GX TRICKS FOR QUICKDRAW DOGS

QuickDraw GX has ways to do almost anything you can do with QuickDraw. All you need to know is how their environments and feature sets compare, and you'll understand how to convert from one to the other.

THE VIEW PORT LIST VERSUS THE GRAPHICS PORT
Most of the time you won't have to concern yourself with view ports at rendering time, because there's no sense of the "current port" as there is in QuickDraw. Here's the recommended method for drawing an existing shape into a new view port:

  1. Copy the shape's transform and install the desired destination view port into the copy.
  2. Call GXDrawShape.
  3. Restore the original transform.
  4. Dispose of the copied transform.

Examples of preserving view port lists can be found in the library routine CopyToBitmaps and in the DrawShapeOffscreen example later in this article (Listing 9).

BITMAPS AND TRANSFER MODES
QuickDraw GX has a lot of transfer modes. This is a good thing, really. Not alltransfer modes are functionally equivalent to those in QuickDraw, but the transferModelibrary is fairly complete. Many of the capabilities of QuickDraw search procedures can be implemented using transfer modes. (The first page ofInside Macintosh: QuickDraw GX Graphics has color pictures of the new transfer modes in action.)

The transfer mode is contained in a shape's ink. Since transfer modes are applied on a per- component basis, you can easily get some groovy effects. For example, you can add the hue of one image to the brightness of another. Usually, though, you'll want all components to use the same mode. The transferMode library routine SetCommonTransfer will do this for you.

There are some differences between QuickDraw GX transfer modes and those found in QuickDraw:

  • Dithering is a view port feature, not a transfer mode. Halftoning is also available on a per-gxViewPort basis. These two features are mutually exclusive; you can't dither and halftone at the same time.
  • Transparency is not a single mode. It's a whole family of modes based on alpha component values.
  • All QuickDraw GX transfer modes occur in color space, while some QuickDraw transfer modes are bitwise.

ONSCREEN BITMAPS
QuickDraw GX maintains a view device list that mirrors the QuickDraw GDevice list. (Utility routines are provided for getting one if you have the other.) The Window Manager is patched in a couple of places so that a window's view port transforms and image memory are maintained when it enters and leaves GDevice real estate.

Drawing a bitmap onscreen obeys the screen GDevice's index entry protections -- QuickDraw GX doesn't use indexes reserved by the Palette Manager for other applications. If you want to draw an image that uses animated palette entries, you'll need to clone references to the destination viewDevice color set and profile, and then insert those references into the bitmap shape before drawing. Example code that does this is on this issue's CD.

COPYBITS IN QUICKDRAW GX
Let's see what it takes to make GXDrawShape do what CopyBits does. CopyBits has several explicit parameters: the source, destination, clipping region, and transfer mode. In QuickDraw GX, the source is the bitmap shape. The destination is defined by the shape's view port list. The clipping region is any shape that you attach to the bitmap shape with GXSetShapeClip. As mentioned before, the transfer mode is contained in the shape's ink.

So, to do a CopyBits-style blit in QuickDraw GX:

  1. Set up the shape's view port list.
  2. Determine the transfer mode (usually just "copy," but it's your choice).
  3. Adjust the shape clip. Don't change the device clip or view port clip.
  4. Adjust the transform if you want to reposition, scale, skew, rotate, or apply perspective to the shape.
  5. Call GXDrawShape.
  6. Clean up as needed.

QuickDraw GX doesn't implement all of the color capabilities of CopyBits. There's no colorizing and no color interpolation for indexed values beyond the end of a bitmap's color set. *

DRAWING OFFSCREEN WITH QUICKDRAW GX
Successive QuickDraw implementations have presented newer and better ways to draw into a offscreen image buffer. The QuickDraw GX offscreen library contains routines to help maintain the data structures necessary to implement the equivalent of a GWorld.

The example in Listing 8 uses the CreateIndexedBitmapShape routine from Listing 1 and the library routine CreateOffscreen to create a fully functional offscreen bitmap.

You might think drawing into a QuickDraw GX offscreen bitmap would be difficult, but it's not. To draw a shape into the offscreen bitmap, set its view port list to the offscreen bitmap's view port and call GXDrawShape (see Listing 9).

Listing 8. Creating an offscreen bitmap

OSErr MakeIndexedOffscreen(offscreen *targetOffWorld, long horiz,
                           long vert, long targetDepth)
{
    gxShape bitsShape;

    if (!CheckArguments(...))
        return paramErr;
    bitsShape = CreateIndexedBitmapShape(horiz, vert, targetDepth);
    if (bitsShape == nil)
        return paramErr;
    CreateOffscreen(targetOffWorld, bitsShape);
    return noErr;
}


Listing 9. Drawing into an offscreen bitmap

void DrawShapeOffscreen(offscreen *offGXWorld, gxShape targetShape)
{
    gxTransform newXform, savedXform;

    if ((offGXWorld == nil) || (targetShape == nil))
        return;
    if (offGXWorld->port == nil)
        return;

    savedXform = GXGetShapeTransform(targetShape);
    newXform = GXCopyToTransform(nil, savedXform);
    GXSetTransformViewPorts(newXform, 1L, &(offGXWorld->port));
    GXSetShapeTransform(targetShape, newXform);

    GXDrawShape(targetShape);

    GXSetShapeTransform(targetShape, savedXform);
    GXDisposeTransform(newXform);
}

BITMAP SHAPES VERSUS PIXMAPS
Sometimes, converting existing QuickDraw code to QuickDraw GX is impractical. If your application needs to use the same data in both offscreen pixMaps and bitmap shapes, it can, provided that the bitmap shape is packed the same as the pixMap -- that is, of identical width, height, pixel depth, and color space.

To use bitmap shape data in a QuickDraw pixMap, build the pixMap with the baseAddr the same as the gxBitmap.image. (Make sure that the bitmap shape is locked down.) To use pixMap data in QuickDraw GX, create a gxBitmap with the image field set to the base address of the source pixMap.

THE QUICKDRAW GX LIBRARIES

Several libraries are included with the QuickDraw GX Software Developer's Kit. They contain, among other things, routines for offscreen rendering and converting image data between QuickDraw and QuickDraw GX. The library code instructs by example and is a good starting point for your own library.

The library code is not completely tested. You should treat it as template code, not a final solution. *

The offscreen library. This library contains support for offscreen bitmaps, copyingbetween bitmap shapes, and simple gradient fills. The offscreen image implementationis basic but solid (it lacks some of the features found in QuickDraw GWorlds, such as automatic longword realignment of images). The utility routine CopyToBitmaps is also useful; it shows a good example of saving a view port list.

The math library. This library contains a number of useful routines for manipulatingmappings. The routine PolyToPolyMap is used in the trapezoidal warp example (Listing 6). The header file math routine.h contains essential macros for conversion between fixed-point, floating-point, and integral values.

The ramp library. Get your gradient fills here. Pleasing to the eye, easy on the code. A gradient-filled bitmap can be rotated and clipped, and voilà! Gradient-filled shapes.

The qd and oval libraries. The qd library has facilities for conversion of bitmap and color data between QuickDraw and QuickDraw GX formats. The oval library has real ovals, not those phony squished QuickDraw things.

The transferMode library. This library facilitates access to a shape's transfer mode information and contains routines for emulating most of the QuickDraw transfer modes. It also contains a bonus -- one of my favorite routines. If you've ever wanted to get the results of a QuickDraw transfer mode on color values without having to use CopyBits, TransmogrifyColor is for you. Check it out.

The storage library. This library implements spooling routines for use with GXFlattenShape and GXUnflattenShape, which you'll need for reading and writing shapes to and from files. These routines detect errors but don't report them, so they're only useful as templates.

The camera library. Perspective is cool, but hard to use unless your math skills are well developed. This library provides nifty 3-D techniques.

AND A FEW MORE THINGS . . .

Here I'll point out some caveats and additional interesting features of QuickDraw GX, just so you know what to look for (and look out for).

EXECUTION OVERHEAD
How fast are QuickDraw GX blits? How slow does an offscreen, 256 x 256, 45º-rotated, 32-bit, YXY, gradient-filled bitmap draw into a window on a 4-bit monitor? How much for all of these shiny pebbles? It depends. Let's look at the issues involved. QuickDraw GX and QuickDraw have much in common here:

  • They're fastest when there's no conversion of value or image location.
  • Common code paths are optimized inside the API: 8-bit to 8-bit, 1-bit to 1-bit, 24-bit to 8-bit, no clipping, rectangle clipped.
  • Blits involving complex transformations are usually orders of magnitude slower.

Some transformations require more processing. QuickDraw GX does only as much work as the transformation matrix mandates. From fastest to slowest, the order is: no transformation (or translation only); scaling; skewing or rotation; perspective.*

The basic performance guidelines are similar to automotive fuel efficiency ratings -- though we have no hard estimates, mileage is better on a smooth highway (no color mapping, skewing, or scaling) than on surface streets.

A transform mutation can require a 3 x 3 matrix operation for each pixel value when rendered. That's a lot of fixed-point multiplications. If execution speed is critical and the mutated version will be used a lot, copy the bitmap shape, mutate the geometry, and draw like crazy. Otherwise, mutate the transform and draw as needed.

SHARED IMAGE BUFFERS
A bitmap shape's raster image buffer can be shared by other bitmap shapes. Just make the source bitmap shape's image field the same as that of another bitmap shape. GXCopyToShape uses this sharing of image buffers. If you need a copy of a bitmap shape (or a picture that contains bitmap shapes) to have its own image buffer, use GXCopyDeepToShape.

USING BITMAPS AS PATTERNS
Bitmap shapes can be used as patterns. Unlike QuickDraw, QuickDraw GX has no limitation on area dimension or size of raster data in a pattern. To do simple tiling, you can just set the bitmap pattern on the shape.

You can align the pattern to all destination view ports simply by setting the gxPortAlignPattern attribute. This forces all shapes drawn with that pattern in a given view port to visually line up with each other. Another pattern attribute, gxPortMapPattern, keeps a pattern from being affected by a shape's transform; this is useful, for example, when you want a shape rotated and its pattern unrotated.

BITMAP SHAPE EQUIVALENCE
You can test QuickDraw GX shapes for equivalence by calling GXEqualShape. However, this routine doesn't account for mapping effects. For example, a bitmap gradient from black to white would be considered not equal to a white-to-black gradient bitmap whose transform is rotated 180º, even though the two shapes would produce identical results when drawn.

SIMPLIFICATION
GXSimplifyShape reduces an indexed bitmap to its simplest representation, even reducing the pixel depth when possible. For example, if an 8-bit-deep bitmap shape contains only 15 colors, GXSimplifyShape will convert it to a 4-bit-deep bitmap. If a bitmap is all one color, it will be converted into a rectangle shape -- it won't be a bitmap shape any more.

SUBSET EDITING
QuickDraw GX provides tools for working with area subsets of bitmaps. A piece can be copied from a source bitmap via GXGetBitmapParts, edited, and then blasted back into the source image with GXSetBitmapParts.

Individual pixel values can be accessed with the GXGetShapePixel and GXSetShapePixel routines. Unlike in QuickDraw, these routines don't need to reference a gxViewDevice to determine the color.

SO GET GOING

As you can see, QuickDraw GX does some really cool things with bitmaps. The transforms alone make it worthwhile -- it's easy to get addicted to rotating and skewing your bitmaps without having to do a lot of work. The new transfer modes are great. All the rest is a bonus. In the future, when memory is cheap and every machine is fast, you'll see more and more Macintosh systems and applications become dependent on QuickDraw GX.

PIXEL VALUE REPRESENTATION

A raster image is, naturally enough, an array of pixel values. For indexed color, each pixel value is an index into an associated color set.

For direct color (16 or 32 bits per pixel), a pixel value is converted directly into a color value by expanding bit fields of the 16- or 32-bit value into three or four 16-bit unsigned integer values.

The expansion of direct pixel values depends on the color space of the raster image and the "packing" of the color components. QuickDraw supports only RGB and a handful of packing schemes, but QuickDraw GX supports a whole family of color spaces and packing formats, some of which are shown in Figure 1. The packing types are defined in the gxColorSpaces enum in the header file graphics types.h. You'll also find definitions for extended color space specifications, such as gxRGB16Space (gxRGBSpace + gxWord5ColorPacking) and gxARGB32Space (gxLong8ColorPacking + gxRGBASpace + gxAlphaFirstPacking). Only explicitly defined permutations are valid -- you can't just make up your own.

[IMAGE 048-064_Surovell_final_h4.GIF]

DAVID SUROVELLWhere there was once one, there now are three: after approximately 1500 years of bachelorhood, David recently married (Jane) and achieved fatherhood (Elliot Ivan). He once wrote a book on QuickDraw, but that was long ago. When he's not sleeping under his desk at Apple, David's passionate avocations include auditioning as a guitarist for bands that fail to play in public, committing brutal fouls in otherwise friendly soccer matches and basketball games, and playing paintball with other rush- hour commuters.*

Thanks to our technical reviewers Pete ("Luke") Alexander, Josh Horwich, and Chris Yerga. *

 
AAPL
$102.50
Apple Inc.
+0.25
MSFT
$45.43
Microsoft Corpora
+0.55
GOOG
$571.60
Google Inc.
+2.40

MacTech Search:
Community Search:

Software Updates via MacUpdate

VueScan 9.4.41 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Cloud 3.0.0 - File sharing from your men...
Cloud is simple file sharing for the Mac. Drag a file from your Mac to the CloudApp icon in the menubar and we take care of the rest. A link to the file will automatically be copied to your clipboard... Read more
LibreOffice 4.3.1.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
SlingPlayer Plugin 3.3.20.505 - Browser...
SlingPlayer is the screen interface software that works hand-in-hand with the hardware inside the Slingbox to make your TV viewing experience just like that at home. It features an array of... Read more
Get Lyrical 3.8 - Auto-magically adds ly...
Get Lyrical auto-magically add lyrics to songs in iTunes. You can choose either a selection of tracks, or the current track. Or turn on "Active Tagging" to get lyrics for songs as you play them.... Read more
Viber 4.2.2 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Cocktail 7.6 - General maintenance and o...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
LaunchBar 6.1 - Powerful file/URL/email...
LaunchBar is an award-winning productivity utility that offers an amazingly intuitive and efficient way to search and access any kind of information stored on your computer or on the Web. It provides... Read more
Maya 2015 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
BBEdit 10.5.12 - Powerful text and HTML...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more

Latest Forum Discussions

See All

Qube Kingdom – Tips, Tricks, Strategies,...
Qube Kingdom is a tower defense game from DeNA. You rally your troops – magicians, archers, knights, barbarians, and others – and fight against an evil menace looking to dominate your kingdom of tiny squares. Planning a war isn’t easy, so here are a... | Read more »
Qube Kingdom Review
Qube Kingdom Review By Nadia Oxford on August 29th, 2014 Our Rating: :: KIND OF A SQUARE KINGDOMUniversal App - Designed for iPhone and iPad Qube Kingdom has cute visuals, but it’s a pretty basic tower defense game at heart.   | Read more »
Fire in the Hole Review
Fire in the Hole Review By Rob Thomas on August 29th, 2014 Our Rating: :: WALK THE PLANKUniversal App - Designed for iPhone and iPad Seafoam’s Fire in the Hole looks like a bright, 8-bit throwback, but there’s not enough booty to... | Read more »
Alien Creeps TD is Now Available Worldwi...
Alien Creeps TD is Now Available Worldwide Posted by Ellis Spice on August 29th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Dodo Master Review
Dodo Master Review By Jordan Minor on August 29th, 2014 Our Rating: :: NEST EGGiPad Only App - Designed for the iPad Dodo Master is tough but fair, and that’s what makes it a joy to play.   | Read more »
Motorsport Manager Review
Motorsport Manager Review By Lee Hamlet on August 29th, 2014 Our Rating: :: MARVELOUS MANAGEMENTUniversal App - Designed for iPhone and iPad Despite its depth and sense of tactical freedom, Motorsport Manager is one of the most... | Read more »
Motorsport Manager – Beginner Tips, Tric...
The world of Motorsport management can be an unforgiving and merciless one, so to help with some of the stress that comes with running a successful race team, here are a few hints and tips to leave your opponents in the dust. | Read more »
CalPal Update Brings the App to 2.0, Add...
CalPal Update Brings the App to 2.0, Adds Lots of New Stuff Posted by Ellis Spice on August 29th, 2014 [ permalink ] | Read more »
Baseball Battle Review
Baseball Battle Review By Jennifer Allen on August 29th, 2014 Our Rating: :: SIMPLE HITTINGUniversal App - Designed for iPhone and iPad Simple and cute, Baseball Battle is a fairly fun baseball game for those looking for something... | Read more »
Checkmark 2.1 Update Released, and it’s...
Checkmark 2.1 Update Released, and it’s on Sale for a Limited Time Posted by Jessica Fisher on August 29th, 2014 [ permalink ] | Read more »

Price Scanner via MacPrices.net

Labor Day Weekend MacBook Pro sale; 15-inch m...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for up to $125 off MSRP. Shipping is free, and B&H charges NY sales tax only. They’ll also include free copies of Parallels Desktop... Read more
Labor Day Weekend iPad mini sale; $50 to $100...
Best Buy has the iPad mini with Retina Display (WiFi models) on sale for $50 off MSRP on their online store for Labor Day Weekend. Choose free shipping or free local store pick up. Price is for... Read more
13-inch 1.4GHz MacBook Air on sale for $899,...
Adorama has the new 2014 13″ 1.4GHz/128GB MacBook Air on sale for $899.99 including free shipping plus NY & NJ tax only. Their price is $100 off MSRP. Read more
It’s Official: Apple Issues Invitations To Se...
Apple has issued one of its characteristically cryptic press invitations for a special event to be held at the Flint Center for the Performing Arts in hometown Cupertino on Sept. 9, 2014 at 10:00 am... Read more
Tablet Shipments To See First On-year Decline...
TrendForce analyst Caroline Chen notes that when the iPad launched in 2010, it was an instant hit and spurred a tablet PC revolution, with tablets so popular that that notebook PC sales stagnated and... Read more
SOBERLINK Releases Apple iOS Compatible Handh...
Cypress, California based SOBERLINK, Inc., creator of the first handheld Breathalyzer designed to improve recovery outcomes, continues to show prominence in the mobile alcohol monitoring space with... Read more
New 21″ 1.4GHz iMac on sale again for $999, s...
Best Buy has the new 21″ 1.4GHz iMac on sale for $999.99 on their online store. Their price is $100 off MSRP. Choose free shipping or free local store pick up. Price is for online orders only, in-... Read more
Smartphone Outlook Remains Strong for 2014, U...
According to a new mobile phone forecast from the International Data Corporation (IDC) Worldwide Quarterly Mobile Phone Tracker, more than 1.25 billion smartphones will be shipped worldwide in 2014,... Read more
Save up to $60 with Apple refurbished iPod to...
The Apple Store has Apple Certified Refurbished 5th generation iPod touches available starting at $149. Apple’s one-year warranty is included with each model, and shipping is free. Many, but not all... Read more
12-Inch MacBook Air Coming in 4Q14 or 2015 –...
Digitimes’ Aaron Lee and Joseph Tsai report that according to Taiwan-based upstream supply chain insiders, Apple plans to launch a thinner MacBook model either at year end 2014 or in 2015, and that... Read more

Jobs Board

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