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

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

BusyContacts 1.0.2 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Capture One Pro 8.2.0.82 - RAW workflow...
Capture One Pro 8 is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 300 high-end cameras -- straight out of the box. It... Read more
Backblaze 4.0.0.872 - Online backup serv...
Backblaze is an online backup service designed from the ground-up for the Mac.With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Little Snitch 3.5.2 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more
Monolingual 1.6.4 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. If you use your computer in only one (human) language, you... Read more
CleanApp 5.0 - Application deinstaller a...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Fantastical 2.0 - Create calendar events...
Fantastical is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event details... Read more
Cocktail 8.2 - 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
Direct Mail 4.0.4 - Create and send grea...
Direct Mail is an easy-to-use, fully-featured email marketing app purpose-built for OS X. It lets you create and send great looking email campaigns. Start your newsletter by selecting from a gallery... Read more
jAlbum Pro 12.6 - Organize your digital...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code!... Read more

MLB Manager 2015 (Games)
MLB Manager 2015 5.0.14 Device: iOS Universal Category: Games Price: $4.99, Version: 5.0.14 (iTunes) Description: Guide your favorite MLB franchise to glory! MLB Manager 2015, officially licensed by MLB.com and based on the award-... | Read more »
Breath of Light (Games)
Breath of Light 1.0.1421 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.1421 (iTunes) Description: Hold a quiet moment. Breath of Light is a meditative and beautiful puzzle game with a hypnotic soundtrack by... | Read more »
WWE WrestleMania Tags into the App Store
Are You ready to rumble? The official WWE WrestleMania app, by World Wrestling Entertainment, is now available. Now you can get all your WrestleMania info in one place before anyone else. The app offers details on superstar signings, interactive... | Read more »
Bio Inc's New Expansion is Infectin...
Bio Inc., by DryGin Studios, is the real time strategy game where you infect a human body with the worst virus your evil brain can design. Recently, the game was updated to add a whole lot of new features. Now you can play the new “Lethal”... | Read more »
The Monocular Minion is Here! Despicable...
Despicable Me: Minion Rush, by Gameloft, is introducing a new runner to the mix in their latest update. Now you can play as Carl, the prankster minion. Carl has a few new abilities to play with, including running at a higher speed from the start.... | Read more »
Dungeon of Madness (Games)
Dungeon of Madness 1.0.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.0 (iTunes) Description: Dungeon of Madness is an action game where you rotate tiles to create our own route. Help the hero by connecting the... | Read more »
Filters for iPhone (Photography)
Filters for iPhone 1.0 Device: iOS iPhone Category: Photography Price: $.99, Version: 1.0 (iTunes) Description: | Read more »
Jump'N'Shoot Attack (Games)
Jump'N'Shoot Attack 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: A mobile game for gamers! Join Louise Lightfoot, the legendary "Master of Jumping and Shooting", on her mission to save... | Read more »
Space Bounties Inc. (Games)
Space Bounties Inc. 1.4 Device: iOS Universal Category: Games Price: $1.99, Version: 1.4 (iTunes) Description: SuperGameDroid: 4/5 "Satisfying futuristic RPG combat, high replay value, and a heavy dose of nostalgia make Space... | Read more »
Gamebook: Pocket RPG (Games)
Gamebook: Pocket RPG 1.0.11 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.11 (iTunes) Description: Walk into the Land of Lanthir Lamath ruled by wicked skeletons and fight for your life in a thrilling adventure.... | Read more »

Price Scanner via MacPrices.net

Logitech Says MX Master Is Its Most Advanced...
Logitech’s new MX Master Wireless Mouse incorporates the best of Logitech’s many computer mouse innovations into a striking hand-sculpted design. The company claims that the MX Master creates a new... Read more
Save up to $300 on a new Mac, $30 on an iPad,...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store lowered prices on Apple Certified Refurbished 2014 MacBook Airs recently, with models now available starting at $679. An Apple one-year warranty is included with each MacBook, and... Read more
Mac Notebook Evolution; A Desktop Replacement...
More often than not right from the beginning, Apple’s Macs have tended to skew toward small. The original Macs were called “compacts,”, and notwithstanding a few exceptions like the honking Big Mac... Read more
13-inch 1.4GHz/128GB MacBook Air (Apple refur...
The Apple Store has Apple Certified Refurbished 2014 13″ 1.4GHz/128GB MacBook Airs available for $759 including free shipping plus Apple’s standard one-year warranty. Their price is $240 off original... Read more
YEP! Alternative Browser for iOS Now Supports...
Pfaeffikon, Switzerland based Power App AG has announced the release of an update to their Yep! Web Browser (v1.3.0) for iOS8 iPhone and iPad. Yep! hit the App Store shortly after the release of iOS... Read more
15-inch Retina MacBook Pros on sale for up to...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for up to $250 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $... Read more
Clearance 13-inch Retina MacBook Pros availab...
B&H Photo has leftover 2014 13″ Retina MacBook Pros on sale for up to $250 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.6GHz/128GB Retina MacBook Pro: $1098... Read more
Clearance 2014 MacBook Airs on sale for up to...
B&H Photo has MacBook Airs on sale for up to $180 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 11″ 128GB MacBook Air: $789.99 110 off original MSRP - 11″ 256GB... Read more
Apple refurbished Time Capsules available for...
The Apple Store has certified refurbished Time Capsules available for $100 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $199, $100... 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
Sr. Technical Services Consultant, *Apple*...
**Job Summary** Apple Professional Services (APS) has an opening for a senior technical position that contributes to Apple 's efforts for strategic and transactional Read more
Lead *Apple* Solutions Consultant - Retail...
**Job Summary** Job Summary The Lead ASC is an Apple employee who serves as the Apple business manager and influencer in a hyper-business critical Reseller's store Read more
*Apple* Pay - Site Reliability Engineer - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.