PDD file (126K) | ClarisWorks 4 file (42K) | not available yet |
Technote 1038 | MARCH 1996 |
This Note is intended for Macintosh QuickDraw GX developers who implement flickerless drawing or double buffering using OffscreenLibrary.c or who are considering using it for their QuickDraw GX graphics applications.
Contents
The GX Libraries fill this gap by providing services built on top of the rest of GX in source form. This Technote and others document these services. Since GX libraries are provided as source, it is reasonable for developers to modify them to meet their specific needs. Care was taken for the libraries not to depend on the implementation details of GX, so that future versions of GX should not invalidate them, in original or modified form.
The libraries are likely to evolve to take advantage of improved algorithms, new Macintosh or GX services; if you modify one for your application's specific needs, it's worth occasionally reviewing the GX library provided by Apple to stay synchronized with any improvements.
OffscreenLibrary.c has two distinct groups of functions. The simpler revolves around the offscreen struct, and provides a single bitmap for back buffering. The other set uses a viewPortBuffer to support multiple offscreens that correspond to the portions of a window that spans multiple monitors of different depths.
struct offscreen { gxShape draw; /* a bitmap which, when drawn, transfers the offscreen to the display */ gxTransform xform; /* this causes shapes owning it to draw offscreen. */ gxViewDevice device; /* the offscreen device whose colorSpace, etc. you may change */ gxViewPort port; /* the offscreen port which may be put in any transform's viewPort list */ gxViewGroup group; /* the global space in which the viewPort and viewDevice exist */ };and these functions:
void CreateOffscreen(offscreen *target, gxShape bitmapShape);Use CreateOffscreen to fill in the offscreen struct, given the bitmap shape to back up.
void DisposeOffscreen(offscreen *target);When you're through with the offscreen, use DisposeOffscreen to get rid of the pieces.
void CopyToBitmaps(gxShape target, gxShape source);To copy one bitmap to another, use CopyToBitmaps.
GX makes it pretty darn easy to create an offscreen. For instance, you can create a bitmap that contains a diagonal line with these calls:
gxLine aLine = {ff(20), ff(40), ff(60), ff(80)}; gxShape lineBits = GXNewLine(&aLine); GXSetShapeType(lineBits, gxBitmapType);Then, to create an offscreen from the line bitmap:
offscreen offLine; CreateOffscreen(offLine, lineBits);You can draw the line bitmap with:
GXDrawShape(offLine.draw);To add a rectangle to the line bitmap, first create a rectangle:
gxRectangle aRect = {ff(50), ff(50), ff(60), ff(60)}; gxShape rectToAdd = GXNewRectangle(&aRect);Then change the rectangle to the transform in the offscreen:
GXSetShapeTransform(rectToAdd, offLine.xform); GXDrawShape(rectToAdd);Now, drawing the line bitmap will draw both the line and the rectangle:
GXDrawShape(lineBits);Once you're done, you can use DisposeOffscreen to get rid of it:
DisposeOffscreen(&offLine);The function CopyToBitmaps uses the offscreen structure internally to copy one bitmap onto another. The name is somewhat misleading, since the shape to be copied can be any shape type, not necessarily a bitmap. For instance, you can use it to create a bitmap that has a specific bit depth from a picture:
static gxShape Create8BitPicture(gxShape myPicture) { gxRectangle bounds; // get the bounding box of the picture GXGetShapeBounds(myPicture, 0, &bounds); // move the picture so that it�s upper left corner is at (0, 0) GXMoveShape(myPicture, -bounds.left, -bounds.top); // create a bitmap big enough to hold the picture gxShape bitmap = {nil, FixRound(bounds.right - bounds.left), FixRound(bounds.bottom - bounds.top), 0, 8, nil, nil, nil}; // copy the picture to the bitmap CopyToBItmaps(bitmap, myPicture); // move the bitmap to the picture�s original position GXMoveShape(myPicture, bounds.left, bounds.top); // restore the picture�s original position GXMoveShape(bitmap, bounds.left, bounds.top); return bitmap; }
This makes allocating an offscreen bitmap a challenge, since there may be no single best depth that allows drawing to contain the correct amount of color and draw as quickly as possible. The solution provided by a viewPortBuffer creates a picture containing an array of offscreens that match the desired multiple viewDevices.
Here's the interface to viewPortBuffer.
typedef struct viewPortBufferRecord **viewPortBuffer;The viewPortBuffer is a blind handle that points to the internals kept by these routines. It is never necessary to directly access the fields pointed to by this handle.
viewPortBuffer NewViewPortBuffer(gxViewPort originalPort);To create an offscreen for a window that may cross multiple monitors, call NewViewPortBuffer. It takes the window's viewPort, returns a reference to the internal structure. The window's viewPort can be retrieved from GXGetWindowViewPort.
void DisposeViewPortBuffer(viewPortBuffer target);When the window is closed, call DisposeViewPortBuffer to get deallocate the internal objects allocated by the viewPortBuffer.
gxViewPort GetViewPortBufferViewPort(viewPortBuffer source);To draw shapes into the offscreen, first call GetViewPortBufferViewPort. GetViewPortBufferViewPort returns a viewPort that references the multiple offscreens. Drawing into this viewPort draws into as many offscreen bitmaps as is appropriate. To attach this viewPort to a single shape, use the library routine SetShapeViewPort. To change all shapes of a given type, try
SetTransformViewPort( GXGetDefaultTransform( theType ));
gxShape GetViewPortBufferShape(viewPortBuffer source);To draw the offscreens, call GetViewPortBufferShape to get the shape to draw. Drawing the returned shape transfers the offscreen bitmaps to the viewDevices pointed to by the original viewPort, typically the window's viewPort.
Boolean ValidViewPortBuffer(viewPortBuffer target);The user may foul things up by changing the monitors depth or the window's position. After a window-altering event, call ValidViewPortBuffer to see if the viewPortBuffer needs to be recomputed.
Boolean UpdateViewPortBuffer(viewPortBuffer target);If the viewPortBuffer is out of date, UpdateViewPortBuffer will put things right again. It returns true if the viewPortBuffer was already valid.
Here's a convoluted example that builds the offscreens and draws a shape.
static void BufferDraw(gxShape shape, WindowPtr window) { // create the viewPortBuffer from the viewPort associated with the window viewPortBuffer buffer = NewViewPortBuffer(GXGetWindowViewPort(window)); // retrieve the viewPort created that allows drawing into the offscreen viewPort offscreenPort = GetViewPortBufferViewPort(buffer); // point the shape to that offscreen SetShapeViewPort(shape, offscreenPort); // draw the shape into the offscreen GXDrawShape(shape); // draw the offscreen into the window GXDrawShape(GetViewPortBufferShape(buffer)); // throw the offscreen away DisposeViewPortBuffer(buffer); }
The implementation is split into a few steps: