Important: The information in this document is obsolete and should not be used for new development.
Using Graphics Devices
To use graphics devices, your application generally uses the QuickDraw routines described elsewhere in this book to draw images into a window; Color QuickDraw automatically displays your images in a manner appropriate for each graphics device that contains a portion of that window.
Instead of drawing directly into an onscreen graphics port, your application can use an offscreen graphics world (described in the chapter "Offscreen Graphics Worlds") to create images with the ideal pixel depth and color table required by your application. Then your application can use the
- Note
- The pixel map for a window's color graphics port always consists of the pixel depth, color table, and boundary rectangle of the main screen, even if the window is created on or moved to an entirely different screen.
![]()
CopyBitsprocedure to copy the images to the screen. Color QuickDraw converts the colors of the images for appropriate display on grayscale graphics devices and on direct and indirect color graphics devices. The manner in which Color QuickDraw translates the colors specified by your application to different graphics devices is described in the chapter "Color QuickDraw." However, if Color QuickDraw were to translate the colors of a color wheel (such as that used by the Color Picker, described in Advanced Color Imaging on the Mac OS), the image would appear as solid black on a black-and-white screen.Many applications can let Color QuickDraw manage multiple video devices of differing dimensions and pixel depths. If your application needs more control over video device management--if it needs certain pixel depths or sets of colors to function effectively, for example--you can take several steps.
To use the routines described in this chapter, your application must check for the existence of Color QuickDraw by using the
- If you need to know about the characteristics of available video devices, your application can use the
GetDeviceListfunction to obtain a handle to the firstGDevicerecord in the device list, theGetGDevicefunction to obtain a handle to theGDevicerecord for the current device, theGetMainDevicefunction to obtain a handle to theGDevicerecord for the main screen, or theGetMaxDevicefunction to obtain a handle to theGDevicerecord for the graphics device with the greatest pixel depth. Your application can then pass this handle to a routine like theTestDeviceAttributefunction or theHasDepthfunction to determine various characteristics of a video device, or your application can examine thegdRectfield of theGDevicerecord to determine the dimensions of the screen it represents.- If you want to optimize your application's drawing for the best possible display on whatever type of screen is the current device, your application can use the
DeviceLoopprocedure, described on page 5-28, to determine the capabilities of the current device before drawing into a window on that device.- If the current device is not suitable for the proper display of an image--for example, if the user has moved the window for your multicolored display of national flags to a black-and-white screen--your application can display the best image possible and display a message explaining that a more capable screen is required for better presentation of the image. Your application can use the
DeviceLoopprocedure to determine the capabilities of the current device.- If your application uses the
HasDepthfunction to determine that the current device can support the pixel depth required for the proper display of your image, but theDeviceLoopprocedure indicates that the user has changed the screen's display, your application can use theSetDepthfunction to change the pixel depth of the screen. Note that theSetDepthfunction is provided for applications that are able to run
only on graphics devices of a particular depth. Your application should use it only after soliciting the user's permission with a dialog box.- If your application needs more control over colors on different indexed devices, your application can use the Palette Manager to arrange different sets of colors for particular images. Because the CLUT is variable on most video devices, your application can display up to 16 million colors, although on an 8-bit indexed device, for example, only 256 different colors can appear at once. See the chapter "Palette Manager" in Advanced Color Imaging on the Mac OS for more information.
- If your application needs to work with offscreen images that have characteristics different from those on the available graphics devices, your application can create offscreen graphics worlds, which contain their own
GDevicerecords. See the chapter "Offscreen Graphics Worlds" in this book for more information.
Gestaltfunction with thegestaltQuickDrawVersionselector. TheGestaltfunction returns a 4-byte value in itsresponseparameter; the low-order word contains QuickDraw version data. In that low-order word, the high-order byte gives the major revision number and the low-order byte gives the minor revision number. If the value returned in theresponseparameter is greater than or equal to the value of the constantgestalt32BitQD, then the system supports Color QuickDraw and all of the routines described in this chapter.Optimizing Your Images for Different Graphics Devices
TheDeviceLoopprocedure searches for graphics devices that intersect your window's drawing region, and it informs your application of each different graphics device it finds. TheDeviceLoopprocedure provides your application with information about the current device's pixel depth and other attributes. Your application can then choose what drawing technique to use for the current device. For example, your application might use inversion to achieve a highlighting effect on a 1-bit graphics device, and, by using theHiliteColorprocedure described in the chapter "Color QuickDraw," it might specify a color like magenta as the highlight color on a color graphics device.For example, you can call
DeviceLoopafter calling the Event Manager procedureBeginUpdatewhenever your application needs to draw into a window, as shown in Listing 5-1.Listing 5-1 Using the
DeviceLoopprocedure
PROCEDURE DoUpdate (window: WindowPtr); VAR windowType := Integer; myWindow: LongInt; BEGIN windowType := MyGetWindowType(window); CASE windowType OF kSimpleRectanglesWindow: {simple case: window with 2 color rectangles} BEGIN BeginUpdate(window); myWindow := LongInt(window); {coerce window ptr for MyDrawingProc} DeviceLoop(window^.visRgn, @MyTrivialDrawingProc, myWindow, []); EndUpdate; END; {handle other window types--documents, dialog boxes, etc.--here} END;When you use theDeviceLoopprocedure, you must supply a handle to a drawing region and a pointer to your own application-defined drawing procedure. In Listing 5-1, a handle to the window's visible region and a pointer to an application-defined drawing procedure calledMyTrivialDrawingProcare passed toDeviceLoop. For each graphics device it finds as the application updates its window,DeviceLoopcallsMyTrivialDrawingProc.Because
DeviceLoopprovides your drawing procedure with the pixel depth of the current device (along with other attributes passed to your drawing procedure in thedeviceFlagsparameter), your drawing procedure can optimize its drawing for whatever type of video device is the current device, as illustrated in Listing 5-2.Listing 5-2 Drawing into different screens
PROCEDURE MyTrivialDrawingProc (depth: Integer; deviceFlags: Integer; targetDevice: GDHandle; userData: LongInt); VAR window: WindowPtr; BEGIN window:= WindowPtr(userData); EraseRect(window^.portRect); CASE depth OF 1: {black-and-white screen} MyDraw1BitRects(window); {draw with ltGray, dkGray pats} 2: MyDraw2BitRects(window); {draw with 2 of 4 available colors} {handle other screen depths here} END;Zooming Windows on Multiscreen Systems
The zoom box in the upper-right corner of the standard document window allows the user to alternate quickly between two window positions and sizes: the user state and the standard state.The user state is the window size and location established by the user. If your application does not supply an initial user state, the user state is simply the size and location of the window when it was created, until the user resizes it.
The standard state is the window size and location that your application considers most convenient, considering the function of the document and the screen space available. In a word-processing application, for example, a standard-state window might show a
full page, if possible, or a page of full width and as much length as fits on the screen.
If the user changes the page size with the Page Setup command, the application might adjust the standard state to reflect the new page size. If your application does not define a standard state, the Window Manager automatically sets the standard state to the entire gray region on the main screen, minus a three-pixel border on all sides. (See Macintosh Human Interface Guidelines for a detailed description of how your application determines where to open and zoom windows.) The user cannot change a window's standard state. (The user and standard states are stored in a data structure of typeWStateDatawhose handle appears in thedataHandlefield of the window record.)Listing 5-3 illustrates an application-defined procedure,
DoZoomWindow, which an application might call when the user clicks the zoom box. Because the user might have moved the window to a different screen since it was last zoomed, the procedure first determines which screen contains the largest area of the window and then calculates the ideal window size for that screen before zooming the window.The screen calculations in the
DoZoomWindowprocedure compareGDevicerecords stored in the device list. (If Color QuickDraw is not available,DoZoomWindowassumes that it's running on a computer with a single screen.)
PROCEDURE DoZoomWindow (thisWindow: windowPtr; zoomInOrOut: Integer); VAR gdNthDevice, gdZoomOnThisDevice: GDHandle; savePort: GrafPtr; windRect, zoomRect, theSect: Rect; sectArea, greatestArea: LongInt; wTitleHeight: Integer; sectFlag: Boolean; BEGIN GetPort(savePort); SetPort(thisWindow); EraseRect(thisWindow^.portRect); {erase to avoid flicker} IF zoomInOrOut = inZoomOut THEN {zooming to standard state} BEGIN IF NOT gColorQDAvailable THEN {assume a single screen and } BEGIN { set standard state to full screen} zoomRect := screenBits.bounds; InsetRect(zoomRect, 4, 4); WStateDataHandle(WindowPeek(thisWindow)^.dataHandle)^^.stdState := zoomRect; END ELSE {locate window on available screens} BEGIN windRect := thisWindow^.portRect; LocalToGlobal(windRect.topLeft); {convert to global coordinates} LocalToGlobal(windRect.botRight); {calculate height of window's title bar} wTitleHeight := windRect.top - 1 - WindowPeek(thisWindow)^.strucRgn^^.rgnBBox.top; windRect.top := windRect.top - wTitleHeight; gdNthDevice := GetDeviceList; {get the first screen} greatestArea := 0; {initialize area to 0} {check window against all gdRects in gDevice list and remember } { which gdRect contains largest area of window} WHILE gdNthDevice <> NIL DO IF TestDeviceAttribute(gdNthDevice, screenDevice) THEN IF TestDeviceAttribute(gdNthDevice, screenActive) THEN BEGIN {The SectRect function calculates the intersection } { of the window rectangle and this GDevice's boundary } { rectangle and returns TRUE if the rectangles intersect, } { FALSE if they don't.} sectFlag := SectRect(windRect, gdNthDevice^^.gdRect, theSect); {determine which screen holds greatest window area} {first, calculate area of rectangle on current screen} WITH theSect DO sectArea := LongInt(right - left) * (bottom - top); IF sectArea > greatestArea THEN BEGIN greatestArea := sectArea; {set greatest area so far} gdZoomOnThisDevice := gdNthDevice; {set zoom device} END; gdNthDevice := GetNextDevice(gdNthDevice); {get next } END; {of WHILE} { GDevice record} {if gdZoomOnThisDevice is on main device, allow for menu bar height} IF gdZoomOnThisDevice = GetMainDevice THEN wTitleHeight := wTitleHeight + GetMBarHeight; WITH gdZoomOnThisDevice^^.gdRect DO {create the zoom rectangle} BEGIN {set the zoom rectangle to the full screen, minus window title } { height (and menu bar height if necessary), inset by 3 pixels} SetRect(zoomRect, left + 3, top + wTitleHeight + 3, right - 3, bottom - 3); {If your application has a different "most useful" standard } { state, then size the zoom window accordingly.} {set up the WStateData record for this window} WStateDataHandle(WindowPeek(thisWindow)^.dataHandle)^^.stdState := zoomRect; END; END; END; {of inZoomOut} {if zoomInOrOut = inZoomIn, just let ZoomWindow zoom to user state} {zoom the window frame} ZoomWindow(thisWindow, zoomInOrOut, (thisWindow = FrontWindow)); MyResizeWindow(thisWindow); {application-defined window-sizing routine} SetPort(savePort); END; (of DoZoomWindow)If the user is zooming the window to the standard state,DoZoomWindowcalculates a new standard size and location based on the application's own considerations, the current location of the window, and the available screens. TheDoZoomWindowprocedure always places the standard state on the screen where the window is currently displayed or, if the window spans screens, on the screen containing the largest area
of the window.Listing 5-3 uses the QuickDraw routines
GetDeviceList,TestDeviceAttribute,GetNextDevice,SectRect, andGetMainDeviceto examine characteristics of the available screens as stored inGDevicerecords. Most of the code in Listing 5-3 is devoted to determining which screen should display the window in the standard state.
After calculating the standard state, if necessary,
- IMPORTANT
- Never use the
boundsfield of aPixMaprecord to determine the size of the screen; instead use the value of thegdRectfield of theGDevicerecord for the screen, as shown in Listing 5-3.![]()
DoZoomWindowcalls theZoomWindowprocedure to redraw the window frame in the new size and location and then calls the application-defined procedureMyResizeWindowto redraw the window's content region. For more information on zooming and resizing windows, see the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials.Setting a Device's Pixel Depth
The Monitors control panel is the user interface for changing the pixel depth, color capabilities, and positions of video devices. Since the user can control the capabilities of the video device, your application should be flexible: although it may have a preferred pixel depth, your application should do its best to accommodate less than ideal conditions.Your application can use the
SetDepthfunction to change the pixel depth of a video device, but your application should do so only with the consent of the user. If your application must have a specific pixel depth, it can display a dialog box that offers the user a choice between changing to that depth or canceling display of the image. This dialog box saves the user the trouble of going to the Monitors control panel before returning to your application. (See the chapter "Dialog Manager" in Inside Macintosh: Macintosh Toolbox Essentials for more information about creating and using dialog boxes.)Before calling
SetDepth, use theHasDepthfunction to determine whether the available hardware can support the pixel depth you require. TheSetDepthfunction is described on page 5-33, and theHasDepthfunction is described on page 5-32.Exceptional Cases When Working With Color Devices
If your application always specifies colors inRGBColorrecords, Color QuickDraw automatically handles the colors on both indexed and direct devices. However, if your application does not specify colors inRGBColorrecords, your application may need to create and use special-purposeCGrafPort,PixMap, andGDevicerecords with the routines described in the chapter "Offscreen Graphics Worlds."If your application must work with
CGrafPort,PixMap, andGDevicerecords in ways beyond the scope of the routines described elsewhere in this book, the following guidelines may aid you in adapting Color QuickDraw to color graphics devices.
- Don't draw directly to the screen. Create your own offscreen graphics world (as described in the chapter "Offscreen Graphics Worlds") and use the
CopyBits,CopyMask, orCopyDeepMaskroutine (described in the chapter "Color QuickDraw") to transfer the image to the screen.- Don't directly change the
fgColororbkColorfields of aGrafPortrecord and expect them to be used as the pixel values. Color QuickDraw recalculates these values for each graphics device. If you want to draw with a color with a particular index value, use a palette with explicit colors, as described in Advanced Color Imaging on the Mac OS. For device-independent colors, use theRGBForeColorandRGBBackColorprocedures, described in the chapter "Color QuickDraw" in this book.- Don't copy a
GDevicerecord'sPixMaprecord. Instead, use theNewPixMapfunction or theCopyPixMapprocedure, and fill all the fields. (These routines are described in the chapter "Color QuickDraw.") TheNewPixMapfunction returns aPixMaprecord that is cloned from thePixMaprecord pointed to by the global variableTheGDevice. If you don't want a copy of the main screen'sPixMaprecord--for example, you want one that is a different pixel depth--then you must fill out more fields than justpixelSize: you must fill out thepixelType,cmpCount, andcmpSizefields. Set thepmVersionfield to 0 when initializing your ownPixMaprecord. For future compatibility you should also set thepackType,packSize,planeBytes, andpmReservedfields to 0. Don't assume aPixMaprecord has a color table--a pixel map for a direct device doesn't need one. For compatibility, aPixMaprecord for a direct device should have a dummy handle in thepmTablefield that points to aColorTablerecord with a seed value equal tocmpSizecmpCountand actSizefield set to 0.- Fill out all the fields of a new
GDevicerecord. When creating an offscreenGDevicerecord by callingNewGDevicewith themodeparameter set to -1, you must fill out the fields of theGDevicerecord (for instance, thegdTypefield) yourself. If you want a copy of an existingGDevicerecord, copy thegdTypefield from it. If you explicitly want an indexed device, assign theclutTypeconstant to thegdTypefield.