iand@mullian.ee.mu.OZ.AU (Ian Robert DOBSON) (01/03/91)
SUMMARY: > I am programming a window at present which will use colour if > the machine is equipped with Color QuickDraw. However, to > optimise the display for users with a b/w monitor, I want > to handle redraw specially when the monitor is in 1 or 2 > bit mode. > > What I need is a mechanism to detect the screen depth of my > window. I thought of examining the window's pixmap, but > it raises the following questions: ---------------------------------------------------------------------- > 1. What happens to a window's pixmap if, in a multi-monitor > environment, the window is moved from a >= 8 bit monitor > to a b/w display? Color Quickdraw (CQD) translates the color pixels of the window (or your color QD calls on an update) into B&W pixels. The window's pixmap still exists at its original depth, so it will retain colour information even though it is being displayed on a black & white screen. When the window is moved back to the colour display, any colour QD drawing performed when the window was on the B&W screen will be properly displayed in colour. ---------------------------------------------------------------------- > 2. How is a window straddling a screen boundary (and thus > displayed partially on both screens) stored in memory? Once again CQD does the necessary work for you. The pixmap still exists at its original depth, but CQD does the final mapping to B&W on the one monitor, and color display on the color monitor. ---------------------------------------------------------------------- > 3. What determines a pixmap's original depth? > Are all windows created at maximum depth for the system > (say 24 bits), in which case a window on the mono display > would still use 24 bits per pixel and be grossly memory > inefficient. Close! All color windows and color grafports are created at the depth of the "current" graphics device. This is normally the main monitor - the one with the menu bar on it. If the current device is a 1-bit B&W monitor, then your color windows will have two colors only - black and white. The Mac OS always sets the current device to the main monitor, but the programmer can change the current device with a call to SetGDevice - be sure to save a reference to the original device using GetGDevice and to restore the current device to that reference when you're done. ---------------------------------------------------------------------- > 4. How do I ensure my window update method is consistant > in a multi-monitor environment? I.E. I don't want > half my window to be updated one way and half the > other. If the window appears on more than one monitor, > should I draw the window contents with a method suited > to the monitor with the lowest screen depth? If so, how > do I find this depth? Most people just let CQD do the work for them. There are times though were the B&W results are not good enough. In this case, you need to write code that identifies the environment(s) that the window exists in, and special case the drawing to each of those environments. This is not a trivial piece of work. It means finding out the bit depth of the various monitors your window overlaps. Then you must find the regions of your grafport that intersect the each monitor. Once you have this information, you can then draw into each of these regions in a manner best suited for the bit depth of that region. The routine you want is: FUNCTION GetMaxDevice(globalRect: Rect) : GDHandle which is described in IM V-125. Basically, you pass it a rectangle (your portRect) converted to global coordinates (SetPort, LocalToGlobal twice) and it finds the deepest GDevice that intersects. Look in the GDevice for the depth (maxgdh^^.gdPMap^^.pixelSize). One thing you end up doing a lot on color QD systems is walking the GDevice list, which is a linked list of handles. The rectangle in global coordinates that the GDevice occupies is in gdRect. You also need to check if it's a screenDevice. Look at the GDevice chapter. If you're using offscreen pixmaps, you'd do best to use the GWorld stuff (documented in very few places, part of 32-Bit QD) or perhaps the DTS sample code which does similar things. ----------- SAMPLE CODE ----------- ---------------------------------------- From: daven@sv.portal.com (David Newman) ---------------------------------------- { Does the window have a pixmap, and what is the pixmap's depth } PROCEDURE WindowInfo (theWindow: WindowPtr; VAR hasColor: Boolean; VAR bitDepth: integer); BEGIN hasColor := BTst(theWindow^.portBits.rowBytes, 15); {IF bit 15=1 THEN pixMap} IF hasColor THEN bitDepth := CGrafPtr(theWindow)^.portPixMap^^.pixelSize ELSE bitDepth := 1; END; { Returns the bitDepth of the monitor on which theWindow is located. If theWindow spans multiple monitors, then the largest depth is returned} PROCEDURE MonitorInfo (theWindow: WindowPtr; VAR bitDepth: integer); VAR tempRect: Rect; oldPort: GrafPtr; theDevice: GDHandle; BEGIN GetPort(oldPort); SetPort(theWindow); tempRect := theWindow^.portRect; LocalToGlobal(tempRect.topLeft); LocalToGlobal(tempRect.botRight); IF theWorld.hasColorQD THEN BEGIN theDevice := GetMaxDevice(tempRect); IF BTst(theDevice^^.gdPMap^^.rowBytes, 15) THEN {IF bit 15=1 THEN pixMap} bitDepth := theDevice^^.gdPMap^^.pixelSize ELSE bitDepth := 1; END ELSE bitDepth:=1; SetPort(oldPort); END; { Returns max bit depth of deepest monitor in the VAR parameter, and TRUE cpu has ColorQD } FUNCTION WorldInfo (VAR bitDepth: integer): Boolean; VAR tempRect: Rect; theDevice: GDHandle; BEGIN WorldInfo := FALSE; SetRect(tempRect, -maxint-1,-maxint-1,maxint,maxint); IF theWorld.hasColorQD THEN BEGIN WorldInfo := TRUE; theDevice := GetMaxDevice(tempRect); IF BTst(theDevice^^.gdPMap^^.rowBytes, 15) THEN {IF bit 15=1 THEN pixMap} bitDepth := theDevice^^.gdPMap^^.pixelSize ELSE bitDepth := 1; END ELSE bitDepth := 1; END; ------------------------------------------------ From: well!oster@apple.com (David Phillip Oster) ------------------------------------------------ Use a loop like the following: if (world.hasColorQD) { for (gdh = GetDeviceList();gdh != NIL;gdh = GetNextDevice(gdh)) { /* loop through devices */ if ( TestDeviceAttribute(gdh, screenActive) && /* is active? */ TestDeviceAttribute(gdh, screenDevice) && /* and a CRT */ (rg = (**gdh).gdRect, PtInRect(where, &rg))) { /* and contains "where" */ *r = (**gdh).gdRect; if (gdh == GetMainDevice()) { r->top += MenuBarSize(); /* trim off menu bar */ } return; /* <- found it, exit */ } } } to scan the device list, looking for the devices that intersect your window (copy the portRect to a temp, then use LocalToGlobal on the topLeft() and botRight() of the temp.) I call: /* SetUseColor - use color style hiliting only on machines that handle it correctly (i.e, have Color QuickDraw and more than one bit-per pixel CRT attached.) */ SetUseColor() { GDHandle gd; Rect desktop; useColor = FALSE; if (world.hasColorQD) { desktop = (**GrayRgn).rgnBBox; gd = GetMaxDevice(&desktop); useColor = (NIL != gd && (**(**gd).gdPMap).pixelSize > 1); } } from each activate/update, sicne if the user changes the window depth, the window will get an update. This almost works. If a window straddles more than one screen, and both parts need updating, you see 1 update update for the whole thing,so you have to split it into per-device rectangles yourself. ------------------------------------------------------------------------- Thanks to all who responded to my request. The help was most appreciated. ' ) ' ) ) ' /~\ / | Dept. of Electrical Engineering / __ _ /--' / / ____/____ _____ | University of Melbourne, Australia / (_(_/ ) / \ o /__( (_) /_) _)_(_) / ) | UUCP: iand@mullian.ee.mu.oz.AU