Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Imaging With QuickDraw /
Chapter 5 - Graphics Devices


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.

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.
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 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 Gestalt function with the gestaltQuickDrawVersion selector. The Gestalt function returns a 4-byte value in its response 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 the response parameter is greater than or equal to the value of the constant gestalt32BitQD, then the system supports Color QuickDraw and all of the routines described in this chapter.

Optimizing Your Images for Different Graphics Devices

The DeviceLoop procedure searches for graphics devices that intersect your window's drawing region, and it informs your application of each different graphics device it finds. The DeviceLoop 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 the HiliteColor 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 procedure BeginUpdate 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 the DeviceLoop 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 called MyTrivialDrawingProc are passed to DeviceLoop. For each graphics device it finds as the application updates its window, DeviceLoop calls MyTrivialDrawingProc.

Because DeviceLoop provides your drawing procedure with the pixel depth of the current device (along with other attributes passed to your drawing procedure in the deviceFlags 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 type WStateData whose handle appears in the dataHandle 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 compare GDevice 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.)

Listing 5-3 Zooming a window

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. The DoZoomWindow 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, and GetMainDevice to examine characteristics of the available screens as stored in GDevice records. Most of the code in Listing 5-3 is devoted to determining which screen should display the window in the standard state.

IMPORTANT
Never use the bounds field of a PixMap record to determine the size of the screen; instead use the value of the gdRect field of the GDevice record for the screen, as shown in Listing 5-3.
After calculating the standard state, if necessary, DoZoomWindow calls the ZoomWindow procedure to redraw the window frame in the new size and location and then calls the application-defined procedure MyResizeWindow 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 the HasDepth function to determine whether the available hardware can support the pixel depth you require. The SetDepth function is described on page 5-33, and the HasDepth function is described on page 5-32.

Exceptional Cases When Working With Color Devices

If your application always specifies colors in RGBColor records, Color QuickDraw automatically handles the colors on both indexed and direct devices. However, if your application does not specify colors in RGBColor records, your application may need to create and use special-purpose CGrafPort, PixMap, and GDevice records with the routines described in the chapter "Offscreen Graphics Worlds."

If your application must work with CGrafPort, PixMap, and GDevice 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.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996