Technote 1182
By Geoff Stahl and Mike Marinkovich |
CONTENTS
The New NewGWorldNewGWorld () |
This Technote describes the changes in NewGWorldNewGWorld with the release of Mac OS 9.
The This Technote describes the new selectors, covers their basic use, then goes on to illustrate some of the basic problems associated with their use. Finally, the note discusses basic performance figures from a sample implementation. |
The New NewGWorldNewGWorld ()NewGWorldNewGWorld
Use the |
QDErr NewGWorldNewGWorld (GWorldPtr * offscreenGWorld, short pixelDepth, const Rect * boundsRect, CTabHandle cTable, /* can be NULL */ GDHandle aGDevice, /* can be NULL */ GWorldFlags flags) |
offscreenGWorld offscreenGWorld is a pointer to the offscreen graphics world created by this routine. pixelDepth pixelDepth is the pixel depth of the offscreen world; possible depths are 1, 2, 4, 8, 16, and 32 bits per pixel. The NewGWorldNewGWorld function uses the pixel depth of the screen with the greatest pixel depth from among all screens whose boundary rectangles intersect the rectangle that you specify in the boundsRect parameter. If you specify zero in this parameter, NewGWorldNewGWorld also uses the GDevice record from this device instead of creating a new GDevice record for the offscreen world. If you use NewGWorldNewGWorld on a computer that supports only basic QuickDraw, you may specify only zero or one in this parameter.
boundsRect boundsRect is the boundary rectangle and port rectangle for the offscreen pixel map. This becomes the boundary rectangle for the GDevice record, if NewGWorldNewGWorld creates one. If you specify zero in the pixelDepth parameter, NewGWorldNewGWorld interprets the boundaries in global coordinates that it uses to determine which screens intersect the rectangle. (NewGWorldNewGWorld then uses the pixel depth, color table, and GDevice record from the screen with the greatest pixel depth from among all screens whose boundary rectangles intersect this rectangle.) Typically, your application supplies this parameter with the port rectangle for the onscreen window into which your application will copy the pixel image from this offscreen world.
cTable cTable is handle to a ColorTable record. If you pass NILNULL in this parameter, NewGWorldNewGWorld uses the default color table for the pixel depth that you specify in the pixelDepth parameter. If you set the pixelDepth parameter to 0, NewGWorldNewGWorld ignores the cTable parameter, and instead copies and uses the color table of the graphics device with the greatest pixel depth among all graphics devices whose boundary rectangles intersect the rectangle that you specify in the boundsRect parameter. If you use NewGWorldNewGWorld on a computer that supports only basic QuickDraw, you may specify only NILNULL in this parameter.
aGDevice aGDevice is a handle to a GDevice record that is used in only two cases. First, when you specify the noNewDevice flag in the flags parameter, in which case NewGWorldNewGWorld attaches this GDevice record to the new offscreen graphics world. Second, when you specify useDistantHdwrMem and/or useLocalHdwrMem flags in the flags parameter, in which case NewGWorld uses this GDevice ’s VRAM or AGP memory to store the GWorld . If you set the pixelDepth parameter to zero, or if you do not set the noNewDevice, noNewDevice flag,useDistantHdwrMem , and/or useLocalHdwrMem flag(s), NewGWorldNewGWorld ignores the aGDevice parameter, so you should set it to NILNULL . If you set the pixelDepth parameter to zero, NewGWorldNewGWorld uses the GDevice record for the graphics device with the greatest pixel depth among all graphics devices whose boundary rectangles intersect the rectangle that you specify in the boundsRect parameter. You should pass NILNULL in this parameter if the computer supports only basic QuickDraw. Generally, your application should never create GDevice records for offscreen graphics worlds. Lastly, if you set useDistantHdwrMem and/or useLocalHdwrMem flags you should always specify a GDevice , otherwise the behavior and device associated with the GWorld , is indeterminate.flags flags describes options available to your application. You can set almost any combination of the flags pixPurge , noNewDevice , useTempMem , keepLocal , useDistantHdwrMem , and useLocalHdwrMem . If you don’t wish to use any of these flags, pass 0 in this parameter, in which case you get the default behavior for NewGWorldNewGWorld —that is, it creates an offscreen graphics world where the base address for the offscreen pixel image is unpurgeable, it uses an existing GDevice record (if you pass 0 in the depth parameter) or creates a new GDevice record, it uses memory in your application heap, and it allows graphics accelerators to cache the offscreen pixel image. You should not use keepLocal with either useDistantHdwrMem or useLocalHdwrMem , the results are in determinate. The available flags are described here:
|
enum { pixPurge = 1L << pixPurgeBit, noNewDevice = 1L << noNewDeviceBit, useTempMem = 1L << useTempMemBit, keepLocal = 1L << keepLocalBit, useDistantHdwrMem = 1L << useDistantHdwrMemBit, useLocalHdwrMem = 1L << useLocalHdwrMemBit, }; typedef unsigned longGWorldFlags; |
pixPurge
noNewDevice
GDevice record.
useTempMem
keepLocal
useDistantHdwrMem
useLocalHdwrMem
DESCRIPTION
The
Typically, you pass 0 in the
The
Unless you specify zero in the
When creating an image, your application can use the
The
The address of the offscreen pixel image is not directly accessible from the For purposes of estimating memory use, you can compute the size of the offscreen pixel image by using this formula: |
rowBytes * (boundsRect.bottom - boundsRect.top) |
In the
If you specify the
As its function result, SPECIAL CONSIDERATIONS
If you supply a handle to a
If when using
There are two important things to note about
Secondly, an offscreen pixel image allocated in VRAM can be purged at system task time by the display driver. This means any time your application yields time such by calling
To use a custom color table in an offscreen graphics world, you need to create the associated offscreen
Currently,
The |
noErr | 0 | No error | |
paramErr | -50 | Illegal parameter | |
memFullErr | -108 | Out of memory error | |
cDepthErr | -157 | Invalid pixel depth |
See also Inside Macintosh: Imaging With QuickDraw. Listing 6-1 on page 6-5 and Listing 6-2 on page 6-10 illustrate how to use
If your application needs to change the pixel depth, boundary rectangle, or color table for an offscreen graphics world, use the Using the New
|
Boolean gNewNewGWorld = false; long versionSystem; // this will only work with Mac OS later than 8.6 Gestalt (’sysv', &versionSystem); if (0x00000860 < (versionSystem & 0x00000FFFF)) gNewNewGWorld = true; // system is greater than version 8.6 |
Next, we want to encapsulate the functions required to allocate and reallocate our |
Boolean BuildOffscreen (GWorldPtr * ppGWorld, WindowPtr pWindow, short * plocation) { GDHandle hgdWindow = NULL; Boolean fMustRebuild = false; if (NULL == *ppGWorld) // if GWorld passed in is not allocated fMustRebuild = true; else { PixMapHandle hPixmap = GetGWorldPixMap (*ppGWorld); // if pixmap handle is NULL or pixmap base address is NULL if ((NULL == hPixmap) || (!GetPixBaseAddr (hPixmap))) fMustRebuild = true; // if GWorld not on same device as window else if (GetGWorldDevice(*ppGWorld) != GetWindowDevice (pWindow)) fMustRebuild = true; } if (fMustRebuild) // must rebuild { // window pixel depth short wPixDepth = (**((CGrafPtr)pWindow)->portPixMap).pixelSize; GDHandle hgdWindow = GetWindowDevice (pWindow);// window GDevice if (NULL != *ppGWorld) // if we have an allocated GWorld { DisposeGWorld (*ppGWorld);// dump our current GWorld *ppGWorld = NULL; } switch (*plocation) // where to we want to put it { case kInVRAM: if (noErr == NewGWorld (ppGWorld, wPixDepth, &pWindow->portRect, NULL, hgdWindow, noNewDevice | useDistantHdwrMem)) break; // we failed with VRAM, signal that and drop to AGP SysBeep (30); *plocation = kInAGP; case kInAGP: if (noErr == NewGWorld (ppGWorld, wPixDepth, &pWindow->portRect, NULL, hgdWindow, noNewDevice | useLocalHdwrMem)) break; // we failed with AGP, signal that and drop to system memory SysBeep (30); *plocation = kInSystem; case kInSystem: default: if (noErr != NewGWorld (ppGWorld, wPixDepth, &pWindow->portRect, NULL, hgdWindow, keepLocal | noNewDevice)) { // we failed with system thus, we can’t allocate our GWorld, // signal that, indicate no location and drop to debugger SysBeep (30); *plocation = kNoWhere; DebugStr ("\pUnable to allocate off screen image"); return false; // nothing was allocated } *plocation = kInSystem; } return true; // we rebuilt our GWorld } return false; // everything is okay } |
This previous function uses standard Macintosh Toolbox functions except the call to |
GDHandle GetWindowDevice (WindowPtr pWindow) { Rect rectWind, rectSect; short wFrameHeight, wTitleHeight; long greatestArea, sectArea; GDHandle hgdNthDevice, hgdZoomOnThisDevice; rectWind = pWindow->portRect; LocalToGlobal ((Point*)& rectWind.top); // convert to global coordinates LocalToGlobal ((Point*)& rectWind.bottom); // calculate height of window’s title bar wFrameHeight = rectWind.left - 1 — (**(((WindowPeek)pWindow)->strucRgn)).rgnBBox.left; wTitleHeight = rectWind.top - 1 — (**(((WindowPeek)pWindow)->strucRgn)).rgnBBox.top; rectWind.top = rectWind.top - wTitleHeight; hgdNthDevice = GetDeviceList (); greatestArea = 0; // initialize to 0 // check window against all gdRects in GDevice list and remember // which gdRect contains largest area of window} while (hgdNthDevice) { if (TestDeviceAttribute (hgdNthDevice, screenDevice)) if (TestDeviceAttribute (hgdNthDevice, screenActive)) { // The SectRect routine calculates the intersection // of the window rectangle and this GDevice // rectangle and returns TRUE if the rectangles intersect, // FALSE if they don’t. SectRect(&rectWind, &(**hgdNthDevice).gdRect, &rectSect); // determine which screen holds greatest window area // first, calculate area of rectangle on current device sectArea = (long)(rectSect.right - rectSect.left) * (rectSect.bottom - rectSect.top); if ( sectArea > greatestArea ) { greatestArea = sectArea;// set greatest area so far hgdZoomOnThisDevice = hgdNthDevice;// set zoom device } hgdNthDevice = GetNextDevice(hgdNthDevice); } } // of while return hgdZoomOnThisDevice; } |
Once we have the buffer allocated, we just need to fill it and blit it to our window. The process to do this remains unchanged. The following listings demonstrate this. Note, |
// fills offscreen buffer with random bright color void FillOffscreen (GWorldPtr pGWorld) { GDHandle hGDSave; CGrafPtr pCGrafSave; Rect rectSource = (pGWorld->portRect); RGBColor rgbColor; rgbColor.red = (Random () + 32767) / 2 + 32767; rgbColor.green = (Random () + 32767) / 2 + 32767; rgbColor.blue = (Random () + 32767) / 2 + 32767; GetGWorld (&pCGrafSave, &hGDSave); SetGWorld (pGWorld, NULL); if (LockPixels (GetGWorldPixMap (pGWorld))) { // draw some background EraseRect (&rectSource); RGBForeColor (&rgbColor); PaintRect (&rectSource); UnlockPixels (GetGWorldPixMap (pGWorld)); } SetGWorld (pCGrafSave, hGDSave); } // checks offscreen and blits it to the front void BlitToWindow (GWorldPtr pGWorld, WindowPtr pWindow, short * pLocation) { Rect rectDest = ((GrafPtr)pWindow)->portRect; Rect rectSource = ((GrafPtr)pWindow)->portRect; GrafPtr pCGrafSave; // check to ensure we have a valid offscreen and rebuild if required if (BuildOffscreen (&pGWorld, pWindow, pLocation)) FillOffscreen (pGWorld); // blit GetPort (&pCGrafSave); SetPort ((GrafPtr) pWindow); if (LockPixels (GetGWorldPixMap (pGWorld))) { CopyBits (&((GrafPtr)pGWorld)->portBits, &pWindow->portBits, &rectSource, &rectDest, srcCopy, NULL); UnlockPixels (GetGWorldPixMap (pGWorld)); } SetPort (pCGrafSave); } |
Lastly, we need to ensure the memory allocated by |
// this is VERY important since the
|
Thanks to Tim Carroll, Kent Miller, and Fernando Urbina.