1 /***********************************************************
3 Copyright (c) 1987 X Consortium
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of the X Consortium shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from the X Consortium.
27 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
31 Permission to use, copy, modify, and distribute this software and its
32 documentation for any purpose and without fee is hereby granted,
33 provided that the above copyright notice appear in all copies and that
34 both that copyright notice and this permission notice appear in
35 supporting documentation, and that the name of Digital not be
36 used in advertising or publicity pertaining to distribution of the
37 software without specific, written prior permission.
39 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
47 ******************************************************************/
49 /* $XConsortium: miexpose.c /main/43 1996/08/01 19:25:26 dpw $ */
50 /* $XFree86: xc/programs/Xserver/mi/miexpose.c,v 3.1 1996/12/23 07:09:44 dawes Exp $ */
55 #include "Xprotostr.h"
58 #include "regionstr.h"
59 #include "scrnintstr.h"
61 #include "windowstr.h"
65 #include "dixstruct.h"
69 extern WindowPtr *WindowTable;
72 machine-independent graphics exposure code. any device that uses
73 the region package can call this.
77 #define RECTLIMIT 25 /* pick a number, any number > 8 */
81 generate a region for exposures for areas that were copied from obscured or
82 non-existent areas to non-obscured areas of the destination. Paint the
83 background for the region, if the destination is a window.
86 this should generally be called, even if graphicsExposures is false,
87 because this is where bits get recovered from backing store.
90 added argument 'plane' is used to indicate how exposures from backing
91 store should be accomplished. If plane is 0 (i.e. no bit plane), CopyArea
92 should be used, else a CopyPlane of the indicated plane will be used. The
93 exposing is done by the backing store's GraphicsExpose function, of course.
98 miHandleExposures(pSrcDrawable, pDstDrawable,
99 pGC, srcx, srcy, width, height, dstx, dsty, plane)
100 register DrawablePtr pSrcDrawable;
101 register DrawablePtr pDstDrawable;
108 register ScreenPtr pscr = pGC->pScreen;
109 RegionPtr prgnSrcClip; /* drawable-relative source clip */
111 RegionPtr prgnDstClip; /* drawable-relative dest clip */
113 BoxRec srcBox; /* unclipped source */
114 RegionRec rgnExposed; /* exposed region, calculated source-
115 relative, made dst relative to
116 intersect with visible parts of
117 dest and send events to client,
118 and then screen relative to paint
119 the window background
125 /* avoid work if we can */
126 if (!pGC->graphicsExposures &&
127 (pDstDrawable->type == DRAWABLE_PIXMAP) &&
128 ((pSrcDrawable->type == DRAWABLE_PIXMAP) ||
129 (((WindowPtr)pSrcDrawable)->backStorage == NULL)))
134 srcBox.x2 = srcx+width;
135 srcBox.y2 = srcy+height;
137 if (pSrcDrawable->type != DRAWABLE_PIXMAP)
141 TsrcBox.x1 = srcx + pSrcDrawable->x;
142 TsrcBox.y1 = srcy + pSrcDrawable->y;
143 TsrcBox.x2 = TsrcBox.x1 + width;
144 TsrcBox.y2 = TsrcBox.y1 + height;
145 pSrcWin = (WindowPtr) pSrcDrawable;
146 if (pGC->subWindowMode == IncludeInferiors)
148 prgnSrcClip = NotClippedByChildren (pSrcWin);
149 if ((RECT_IN_REGION(pscr, prgnSrcClip, &TsrcBox)) == rgnIN)
151 REGION_DESTROY(pscr, prgnSrcClip);
157 if ((RECT_IN_REGION(pscr, &pSrcWin->clipList, &TsrcBox)) == rgnIN)
159 prgnSrcClip = &rgnSrcRec;
160 REGION_INIT(pscr, prgnSrcClip, NullBox, 0);
161 REGION_COPY(pscr, prgnSrcClip, &pSrcWin->clipList);
163 REGION_TRANSLATE(pscr, prgnSrcClip,
164 -pSrcDrawable->x, -pSrcDrawable->y);
170 if ((srcBox.x1 >= 0) && (srcBox.y1 >= 0) &&
171 (srcBox.x2 <= pSrcDrawable->width) &&
172 (srcBox.y2 <= pSrcDrawable->height))
177 box.x2 = pSrcDrawable->width;
178 box.y2 = pSrcDrawable->height;
179 prgnSrcClip = &rgnSrcRec;
180 REGION_INIT(pscr, prgnSrcClip, &box, 1);
181 pSrcWin = (WindowPtr)NULL;
184 if (pDstDrawable == pSrcDrawable)
186 prgnDstClip = prgnSrcClip;
188 else if (pDstDrawable->type != DRAWABLE_PIXMAP)
190 if (pGC->subWindowMode == IncludeInferiors)
192 prgnDstClip = NotClippedByChildren((WindowPtr)pDstDrawable);
196 prgnDstClip = &rgnDstRec;
197 REGION_INIT(pscr, prgnDstClip, NullBox, 0);
198 REGION_COPY(pscr, prgnDstClip,
199 &((WindowPtr)pDstDrawable)->clipList);
201 REGION_TRANSLATE(pscr, prgnDstClip,
202 -pDstDrawable->x, -pDstDrawable->y);
210 box.x2 = pDstDrawable->width;
211 box.y2 = pDstDrawable->height;
212 prgnDstClip = &rgnDstRec;
213 REGION_INIT(pscr, prgnDstClip, &box, 1);
216 /* drawable-relative source region */
217 REGION_INIT(pscr, &rgnExposed, &srcBox, 1);
219 /* now get the hidden parts of the source box*/
220 REGION_SUBTRACT(pscr, &rgnExposed, &rgnExposed, prgnSrcClip);
222 if (pSrcWin && pSrcWin->backStorage)
225 * Copy any areas from the source backing store. Modifies
228 (* pSrcWin->drawable.pScreen->ExposeCopy) ((WindowPtr)pSrcDrawable,
237 /* move them over the destination */
238 REGION_TRANSLATE(pscr, &rgnExposed, dstx-srcx, dsty-srcy);
240 /* intersect with visible areas of dest */
241 REGION_INTERSECT(pscr, &rgnExposed, &rgnExposed, prgnDstClip);
244 * If we have LOTS of rectangles, we decide to take the extents
245 * and force an exposure on that. This should require much less
246 * work overall, on both client and server. This is cheating, but
247 * isn't prohibited by the protocol ("spontaneous combustion" :-)
250 extents = pGC->graphicsExposures &&
251 (REGION_NUM_RECTS(&rgnExposed) > RECTLIMIT) &&
252 (pDstDrawable->type != DRAWABLE_PIXMAP);
257 if (!(region = wClipShape (pSrcWin)))
258 region = wBoundingShape (pSrcWin);
260 * If you try to CopyArea the extents of a shaped window, compacting the
261 * exposed region will undo all our work!
263 if (extents && pSrcWin && region &&
264 (RECT_IN_REGION(pscr, region, &srcBox) != rgnIN))
270 WindowPtr pWin = (WindowPtr)pDstDrawable;
272 expBox = *REGION_EXTENTS(pscr, &rgnExposed);
273 REGION_RESET(pscr, &rgnExposed, &expBox);
274 /* need to clear out new areas of backing store */
275 if (pWin->backStorage)
276 (void) (* pWin->drawable.pScreen->ClearBackingStore)(
280 expBox.x2 - expBox.x1,
281 expBox.y2 - expBox.y1,
284 if ((pDstDrawable->type != DRAWABLE_PIXMAP) &&
285 (((WindowPtr)pDstDrawable)->backgroundState != None))
287 WindowPtr pWin = (WindowPtr)pDstDrawable;
289 /* make the exposed area screen-relative */
290 REGION_TRANSLATE(pscr, &rgnExposed,
291 pDstDrawable->x, pDstDrawable->y);
295 /* PaintWindowBackground doesn't clip, so we have to */
296 REGION_INTERSECT(pscr, &rgnExposed, &rgnExposed, &pWin->clipList);
298 (*pWin->drawable.pScreen->PaintWindowBackground)(
299 (WindowPtr)pDstDrawable, &rgnExposed, PW_BACKGROUND);
303 REGION_RESET(pscr, &rgnExposed, &expBox);
306 REGION_TRANSLATE(pscr, &rgnExposed,
307 -pDstDrawable->x, -pDstDrawable->y);
309 if (prgnDstClip == &rgnDstRec)
311 REGION_UNINIT(pscr, prgnDstClip);
313 else if (prgnDstClip != prgnSrcClip)
315 REGION_DESTROY(pscr, prgnDstClip);
318 if (prgnSrcClip == &rgnSrcRec)
320 REGION_UNINIT(pscr, prgnSrcClip);
324 REGION_DESTROY(pscr, prgnSrcClip);
327 if (pGC->graphicsExposures)
330 RegionPtr exposed = REGION_CREATE(pscr, NullBox, 0);
331 *exposed = rgnExposed;
336 REGION_UNINIT(pscr, &rgnExposed);
341 /* send GraphicsExpose events, or a NoExpose event, based on the region */
344 miSendGraphicsExpose (client, pRgn, drawable, major, minor)
351 if (pRgn && !REGION_NIL(pRgn))
355 register BoxPtr pBox;
359 numRects = REGION_NUM_RECTS(pRgn);
360 pBox = REGION_RECTS(pRgn);
361 if(!(pEvent = (xEvent *)ALLOCATE_LOCAL(numRects * sizeof(xEvent))))
365 for (i=1; i<=numRects; i++, pe++, pBox++)
367 pe->u.u.type = GraphicsExpose;
368 pe->u.graphicsExposure.drawable = drawable;
369 pe->u.graphicsExposure.x = pBox->x1;
370 pe->u.graphicsExposure.y = pBox->y1;
371 pe->u.graphicsExposure.width = pBox->x2 - pBox->x1;
372 pe->u.graphicsExposure.height = pBox->y2 - pBox->y1;
373 pe->u.graphicsExposure.count = numRects - i;
374 pe->u.graphicsExposure.majorEvent = major;
375 pe->u.graphicsExposure.minorEvent = minor;
377 TryClientEvents(client, pEvent, numRects,
378 (Mask)0, NoEventMask, NullGrab);
379 DEALLOCATE_LOCAL(pEvent);
384 event.u.u.type = NoExpose;
385 event.u.noExposure.drawable = drawable;
386 event.u.noExposure.majorEvent = major;
387 event.u.noExposure.minorEvent = minor;
388 TryClientEvents(client, &event, 1,
389 (Mask)0, NoEventMask, NullGrab);
394 miSendExposures(pWin, pRgn, dx, dy)
399 register BoxPtr pBox;
401 register xEvent *pEvent, *pe;
404 pBox = REGION_RECTS(pRgn);
405 numRects = REGION_NUM_RECTS(pRgn);
406 if(!(pEvent = (xEvent *) ALLOCATE_LOCAL(numRects * sizeof(xEvent))))
409 for (i=numRects, pe = pEvent; --i >= 0; pe++, pBox++)
411 pe->u.u.type = Expose;
412 pe->u.expose.window = pWin->drawable.id;
413 pe->u.expose.x = pBox->x1 - dx;
414 pe->u.expose.y = pBox->y1 - dy;
415 pe->u.expose.width = pBox->x2 - pBox->x1;
416 pe->u.expose.height = pBox->y2 - pBox->y1;
417 pe->u.expose.count = i;
419 DeliverEvents(pWin, pEvent, numRects, NullWindow);
420 DEALLOCATE_LOCAL(pEvent);
424 miWindowExposures(pWin, prgn, other_exposed)
426 register RegionPtr prgn, other_exposed;
428 RegionPtr exposures = prgn;
429 if (pWin->backStorage && prgn)
431 * in some cases, backing store will cause a different
432 * region to be exposed than needs to be repainted
433 * (like when a window is mapped). RestoreAreas is
434 * allowed to return a region other than prgn,
435 * in which case this routine will free the resultant
436 * region. If exposures is null, then no events will
437 * be sent to the client; if prgn is empty
438 * no areas will be repainted.
440 exposures = (*pWin->drawable.pScreen->RestoreAreas)(pWin, prgn);
441 if ((prgn && !REGION_NIL(prgn)) ||
442 (exposures && !REGION_NIL(exposures)) || other_exposed)
445 int clientInterested;
448 * Restore from backing-store FIRST.
450 clientInterested = (pWin->eventMask|wOtherEventMasks(pWin)) & ExposureMask;
455 REGION_UNION(pWin->drawable.pScreen, other_exposed,
458 if (exposures != prgn)
459 REGION_DESTROY(pWin->drawable.pScreen, exposures);
461 exposures = other_exposed;
463 if (clientInterested && exposures && (REGION_NUM_RECTS(exposures) > RECTLIMIT))
466 * If we have LOTS of rectangles, we decide to take the extents
467 * and force an exposure on that. This should require much less
468 * work overall, on both client and server. This is cheating, but
469 * isn't prohibited by the protocol ("spontaneous combustion" :-).
473 box = *REGION_EXTENTS( pWin->drawable.pScreen, exposures);
474 if (exposures == prgn) {
476 REGION_INIT( pWin->drawable.pScreen, exposures, &box, 1);
477 REGION_RESET( pWin->drawable.pScreen, prgn, &box);
479 REGION_RESET( pWin->drawable.pScreen, exposures, &box);
480 REGION_UNION( pWin->drawable.pScreen, prgn, prgn, exposures);
482 /* PaintWindowBackground doesn't clip, so we have to */
483 REGION_INTERSECT( pWin->drawable.pScreen, prgn, prgn, &pWin->clipList);
484 /* need to clear out new areas of backing store, too */
485 if (pWin->backStorage)
486 (void) (* pWin->drawable.pScreen->ClearBackingStore)(
488 box.x1 - pWin->drawable.x,
489 box.y1 - pWin->drawable.y,
494 if (prgn && !REGION_NIL(prgn))
495 (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, prgn, PW_BACKGROUND);
496 if (clientInterested && exposures && !REGION_NIL(exposures))
497 miSendExposures(pWin, exposures,
498 pWin->drawable.x, pWin->drawable.y);
499 if (exposures == &expRec)
501 REGION_UNINIT( pWin->drawable.pScreen, exposures);
503 else if (exposures && exposures != prgn && exposures != other_exposed)
504 REGION_DESTROY( pWin->drawable.pScreen, exposures);
506 REGION_EMPTY( pWin->drawable.pScreen, prgn);
508 else if (exposures && exposures != prgn)
509 REGION_DESTROY( pWin->drawable.pScreen, exposures);
514 this code is highly unlikely. it is not haile selassie.
516 there is some hair here. we can't just use the window's
517 clip region as it is, because if we are painting the border,
518 the border is not in the client area and so we will be excluded
519 when we validate the GC, and if we are painting a parent-relative
520 background, the area we want to paint is in some other window.
521 since we trust the code calling us to tell us to paint only areas
522 that are really ours, we will temporarily give the window a
523 clipList the size of the whole screen and an origin at (0,0).
524 this more or less assumes that ddX code will do translation
525 based on the window's absolute position, and that ValidateGC will
526 look at clipList, and that no other fields from the
527 window will be used. it's not possible to just draw
528 in the root because it may be a different depth.
530 to get the tile to align correctly we set the GC's tile origin to
531 be the (x,y) of the window's upper left corner, after which we
532 get the right bits when drawing into the root.
534 because the clip_mask is being set to None, we may call DoChangeGC with
535 fPointer set true, thus we no longer need to install the background or
536 border tile in the resource table.
539 static RESTYPE ResType = 0;
540 static int numGCs = 0;
541 static GCPtr screenContext[MAXSCREENS];
549 GCPtr pGC = (GCPtr)value;
550 screenContext[pGC->pScreen->myNum] = (GCPtr)NULL;
559 miPaintWindow(pWin, prgn, what)
560 register WindowPtr pWin;
566 Bool usingScratchGC = FALSE;
579 ChangeGCVal gcval[7];
580 ChangeGCVal newValues [COUNT_BITS];
582 BITS32 gcmask, index, mask;
584 DDXPointRec oldCorner;
589 register BoxPtr pbox;
590 register ScreenPtr pScreen = pWin->drawable.pScreen;
591 register xRectangle *prect;
596 if (what == PW_BACKGROUND)
598 switch (pWin->backgroundState) {
602 (*pWin->parent->drawable.pScreen->PaintWindowBackground)(pWin->parent, prgn, what);
604 case BackgroundPixel:
605 newValues[FOREGROUND].val = pWin->background.pixel;
606 newValues[FILLSTYLE].val = FillSolid;
607 gcmask |= GCForeground | GCFillStyle;
609 case BackgroundPixmap:
610 newValues[TILE].ptr = (pointer)pWin->background.pixmap;
611 newValues[FILLSTYLE].val = FillTiled;
612 gcmask |= GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin;
618 if (pWin->borderIsPixel)
620 newValues[FOREGROUND].val = pWin->border.pixel;
621 newValues[FILLSTYLE].val = FillSolid;
622 gcmask |= GCForeground | GCFillStyle;
626 newValues[TILE].ptr = (pointer)pWin->border.pixmap;
627 newValues[FILLSTYLE].val = FillTiled;
628 gcmask |= GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin;
632 prect = (xRectangle *)ALLOCATE_LOCAL(REGION_NUM_RECTS(prgn) *
637 newValues[FUNCTION].val = GXcopy;
638 gcmask |= GCFunction | GCClipMask;
641 pRoot = WindowTable[i];
644 if (what == PW_BORDER)
646 while (pBgWin->backgroundState == ParentRelative)
647 pBgWin = pBgWin->parent;
650 if ((pWin->drawable.depth != pRoot->drawable.depth) ||
651 (pWin->drawable.bitsPerPixel != pRoot->drawable.bitsPerPixel))
653 usingScratchGC = TRUE;
654 pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen);
657 DEALLOCATE_LOCAL(prect);
661 * mash the clip list so we can paint the border by
662 * mangling the window in place, pretending it
663 * spans the entire screen
665 if (what == PW_BORDER)
667 prgnWin = pWin->clipList;
668 oldCorner.x = pWin->drawable.x;
669 oldCorner.y = pWin->drawable.y;
670 pWin->drawable.x = pWin->drawable.y = 0;
673 box.x2 = pScreen->width;
674 box.y2 = pScreen->height;
675 REGION_INIT(pScreen, &pWin->clipList, &box, 1);
676 pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
677 newValues[ABSX].val = pBgWin->drawable.x;
678 newValues[ABSY].val = pBgWin->drawable.y;
682 newValues[ABSX].val = 0;
683 newValues[ABSY].val = 0;
687 * draw the background to the root window
689 if (screenContext[i] == (GCPtr)NULL)
691 if (!ResType && !(ResType = CreateNewResourceType(tossGC)))
693 screenContext[i] = CreateGC((DrawablePtr)pWin, (BITS32) 0,
694 (XID *)NULL, &status);
695 if (!screenContext[i])
698 if (!AddResource(FakeClientID(0), ResType,
699 (pointer)screenContext[i]))
702 pGC = screenContext[i];
703 newValues[SUBWINDOW].val = IncludeInferiors;
704 newValues[ABSX].val = pBgWin->drawable.x;
705 newValues[ABSY].val = pBgWin->drawable.y;
706 gcmask |= GCSubwindowMode;
710 if (pWin->backStorage)
711 (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeVisBack);
717 index = lowbit (mask);
721 if (pGC->alu != newValues[FUNCTION].val) {
723 gcval[i++].val = newValues[FUNCTION].val;
726 case GCTileStipXOrigin:
727 if ( pGC->patOrg.x != newValues[ABSX].val) {
729 gcval[i++].val = newValues[ABSX].val;
732 case GCTileStipYOrigin:
733 if ( pGC->patOrg.y != newValues[ABSY].val) {
735 gcval[i++].val = newValues[ABSY].val;
739 if ( pGC->clientClipType != CT_NONE) {
741 gcval[i++].val = CT_NONE;
744 case GCSubwindowMode:
745 if ( pGC->subWindowMode != newValues[SUBWINDOW].val) {
747 gcval[i++].val = newValues[SUBWINDOW].val;
751 if (pGC->tileIsPixel || pGC->tile.pixmap != newValues[TILE].ptr)
754 gcval[i++].ptr = newValues[TILE].ptr;
758 if ( pGC->fillStyle != newValues[FILLSTYLE].val) {
760 gcval[i++].val = newValues[FILLSTYLE].val;
764 if ( pGC->fgPixel != newValues[FOREGROUND].val) {
766 gcval[i++].val = newValues[FOREGROUND].val;
773 dixChangeGC(NullClient, pGC, gcmask, NULL, gcval);
775 if (pWin->drawable.serialNumber != pGC->serialNumber)
776 ValidateGC((DrawablePtr)pWin, pGC);
778 numRects = REGION_NUM_RECTS(prgn);
779 pbox = REGION_RECTS(prgn);
780 for (i= numRects; --i >= 0; pbox++, prect++)
782 prect->x = pbox->x1 - pWin->drawable.x;
783 prect->y = pbox->y1 - pWin->drawable.y;
784 prect->width = pbox->x2 - pbox->x1;
785 prect->height = pbox->y2 - pbox->y1;
788 (*pGC->ops->PolyFillRect)((DrawablePtr)pWin, pGC, numRects, prect);
789 DEALLOCATE_LOCAL(prect);
791 if (pWin->backStorage)
792 (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeNothing);
796 if (what == PW_BORDER)
798 REGION_UNINIT(pScreen, &pWin->clipList);
799 pWin->clipList = prgnWin;
800 pWin->drawable.x = oldCorner.x;
801 pWin->drawable.y = oldCorner.y;
802 pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
809 /* MICLEARDRAWABLE -- sets the entire drawable to the background color of
810 * the GC. Useful when we have a scratch drawable and need to initialize
812 miClearDrawable(pDraw, pGC)
816 XID fg = pGC->fgPixel;
817 XID bg = pGC->bgPixel;
822 rect.width = pDraw->width;
823 rect.height = pDraw->height;
824 DoChangeGC(pGC, GCForeground, &bg, 0);
825 ValidateGC(pDraw, pGC);
826 (*pGC->ops->PolyFillRect)(pDraw, pGC, 1, &rect);
827 DoChangeGC(pGC, GCForeground, &fg, 0);
828 ValidateGC(pDraw, pGC);