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.
CopyBits
procedure 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
GetDeviceList
function to obtain a handle to the firstGDevice
record in the device list, theGetGDevice
function to obtain a handle to theGDevice
record for the current device, theGetMainDevice
function to obtain a handle to theGDevice
record for the main screen, or theGetMaxDevice
function to obtain a handle to theGDevice
record for the graphics device with the greatest pixel depth. Your application can then pass this handle to a routine like theTestDeviceAttribute
function or theHasDepth
function to determine various characteristics of a video device, or your application can examine thegdRect
field of theGDevice
record 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
DeviceLoop
procedure, 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
DeviceLoop
procedure to determine the capabilities of the current device.- If your application uses the
HasDepth
function to determine that the current device can support the pixel depth required for the proper display of your image, but theDeviceLoop
procedure indicates that the user has changed the screen's display, your application can use theSetDepth
function to change the pixel depth of the screen. Note that theSetDepth
function 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
GDevice
records. See the chapter "Offscreen Graphics Worlds" in this book for more information.
Gestalt
function with thegestaltQuickDrawVersion
selector. TheGestalt
function returns a 4-byte value in itsresponse
parameter; 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 theresponse
parameter 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
TheDeviceLoop
procedure searches for graphics devices that intersect your window's drawing region, and it informs your application of each different graphics device it finds. TheDeviceLoop
procedure 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 theHiliteColor
procedure 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
DeviceLoop
after calling the Event Manager procedureBeginUpdate
whenever your application needs to draw into a window, as shown in Listing 5-1.Listing 5-1 Using the
DeviceLoop
procedure
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 theDeviceLoop
procedure, 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 calledMyTrivialDrawingProc
are passed toDeviceLoop
. For each graphics device it finds as the application updates its window,DeviceLoop
callsMyTrivialDrawingProc
.Because
DeviceLoop
provides your drawing procedure with the pixel depth of the current device (along with other attributes passed to your drawing procedure in thedeviceFlags
parameter), 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 typeWStateData
whose handle appears in thedataHandle
field 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
DoZoomWindow
procedure compareGDevice
records stored in the device list. (If Color QuickDraw is not available,DoZoomWindow
assumes 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,DoZoomWindow
calculates a new standard size and location based on the application's own considerations, the current location of the window, and the available screens. TheDoZoomWindow
procedure 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
, andGetMainDevice
to examine characteristics of the available screens as stored inGDevice
records. 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
bounds
field of aPixMap
record to determine the size of the screen; instead use the value of thegdRect
field of theGDevice
record for the screen, as shown in Listing 5-3.DoZoomWindow
calls theZoomWindow
procedure to redraw the window frame in the new size and location and then calls the application-defined procedureMyResizeWindow
to 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
SetDepth
function 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 theHasDepth
function to determine whether the available hardware can support the pixel depth you require. TheSetDepth
function is described on page 5-33, and theHasDepth
function is described on page 5-32.Exceptional Cases When Working With Color Devices
If your application always specifies colors inRGBColor
records, Color QuickDraw automatically handles the colors on both indexed and direct devices. However, if your application does not specify colors inRGBColor
records, your application may need to create and use special-purposeCGrafPort
,PixMap
, andGDevice
records with the routines described in the chapter "Offscreen Graphics Worlds."If your application must work with
CGrafPort
,PixMap
, andGDevice
records 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
, orCopyDeepMask
routine (described in the chapter "Color QuickDraw") to transfer the image to the screen.- Don't directly change the
fgColor
orbkColor
fields of aGrafPort
record 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 theRGBForeColor
andRGBBackColor
procedures, described in the chapter "Color QuickDraw" in this book.- Don't copy a
GDevice
record'sPixMap
record. Instead, use theNewPixMap
function or theCopyPixMap
procedure, and fill all the fields. (These routines are described in the chapter "Color QuickDraw.") TheNewPixMap
function returns aPixMap
record that is cloned from thePixMap
record pointed to by the global variableTheGDevice
. If you don't want a copy of the main screen'sPixMap
record--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
, andcmpSize
fields. Set thepmVersion
field to 0 when initializing your ownPixMap
record. For future compatibility you should also set thepackType
,packSize
,planeBytes
, andpmReserved
fields to 0. Don't assume aPixMap
record has a color table--a pixel map for a direct device doesn't need one. For compatibility, aPixMap
record for a direct device should have a dummy handle in thepmTable
field that points to aColorTable
record with a seed value equal tocmpSize
cmpCount
and actSize
field set to 0.- Fill out all the fields of a new
GDevice
record. When creating an offscreenGDevice
record by callingNewGDevice
with themode
parameter set to -1, you must fill out the fields of theGDevice
record (for instance, thegdType
field) yourself. If you want a copy of an existingGDevice
record, copy thegdType
field from it. If you explicitly want an indexed device, assign theclutType
constant to thegdType
field.