2 * Copyright © 2008 Red Hat, Inc.
3 * Partly based on code Copyright © 2000 SuSE, Inc.
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Red Hat not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. Red Hat makes no representations about the
12 * suitability of this software for any purpose. It is provided "as is"
13 * without express or implied warranty.
15 * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat
17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Permission to use, copy, modify, distribute, and sell this software and its
23 * documentation for any purpose is hereby granted without fee, provided that
24 * the above copyright notice appear in all copies and that both that
25 * copyright notice and this permission notice appear in supporting
26 * documentation, and that the name of SuSE not be used in advertising or
27 * publicity pertaining to distribution of the software without specific,
28 * written prior permission. SuSE makes no representations about the
29 * suitability of this software for any purpose. It is provided "as is"
30 * without express or implied warranty.
32 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
34 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
35 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
36 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
37 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
39 * Author: Owen Taylor <otaylor@fishsoup.net>
40 * Based on code by: Keith Packard
43 #ifdef HAVE_DIX_CONFIG_H
44 #include <dix-config.h>
54 #define DBG_GLYPH_CACHE(a) ErrorF a
56 #define DBG_GLYPH_CACHE(a)
59 /* Width of the pixmaps we use for the caches; this should be less than
60 * max texture size of the driver; this may need to actually come from
63 #define CACHE_PICTURE_WIDTH 1024
65 /* Maximum number of glyphs we buffer on the stack before flushing
66 * rendering to the mask or destination surface.
68 #define GLYPH_BUFFER_SIZE 256
72 ExaCompositeRectRec rects
[GLYPH_BUFFER_SIZE
];
74 } ExaGlyphBuffer
, *ExaGlyphBufferPtr
;
77 ExaGlyphSuccess
, /* Glyph added to render buffer */
78 ExaGlyphFail
, /* out of memory, etc */
79 ExaGlyphNeedFlush
, /* would evict a glyph already in the buffer */
80 } ExaGlyphCacheResult
;
83 exaGlyphsInit(ScreenPtr pScreen
)
85 ExaScreenPriv(pScreen
);
88 memset(pExaScr
->glyphCaches
, 0, sizeof(pExaScr
->glyphCaches
));
90 pExaScr
->glyphCaches
[i
].format
= PICT_a8
;
91 pExaScr
->glyphCaches
[i
].glyphWidth
= pExaScr
->glyphCaches
[i
].glyphHeight
=
94 pExaScr
->glyphCaches
[i
].format
= PICT_a8
;
95 pExaScr
->glyphCaches
[i
].glyphWidth
= pExaScr
->glyphCaches
[i
].glyphHeight
=
98 pExaScr
->glyphCaches
[i
].format
= PICT_a8r8g8b8
;
99 pExaScr
->glyphCaches
[i
].glyphWidth
= pExaScr
->glyphCaches
[i
].glyphHeight
=
102 pExaScr
->glyphCaches
[i
].format
= PICT_a8r8g8b8
;
103 pExaScr
->glyphCaches
[i
].glyphWidth
= pExaScr
->glyphCaches
[i
].glyphHeight
=
107 assert(i
== EXA_NUM_GLYPH_CACHES
);
109 for (i
= 0; i
< EXA_NUM_GLYPH_CACHES
; i
++) {
110 pExaScr
->glyphCaches
[i
].columns
=
111 CACHE_PICTURE_WIDTH
/ pExaScr
->glyphCaches
[i
].glyphWidth
;
112 pExaScr
->glyphCaches
[i
].size
= 256;
113 pExaScr
->glyphCaches
[i
].hashSize
= 557;
118 exaUnrealizeGlyphCaches(ScreenPtr pScreen
, unsigned int format
)
120 ExaScreenPriv(pScreen
);
123 for (i
= 0; i
< EXA_NUM_GLYPH_CACHES
; i
++) {
124 ExaGlyphCachePtr cache
= &pExaScr
->glyphCaches
[i
];
126 if (cache
->format
!= format
)
129 if (cache
->picture
) {
130 FreePicture((pointer
) cache
->picture
, (XID
) 0);
131 cache
->picture
= NULL
;
134 free(cache
->hashEntries
);
135 cache
->hashEntries
= NULL
;
138 cache
->glyphs
= NULL
;
139 cache
->glyphCount
= 0;
143 #define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0)
145 /* All caches for a single format share a single pixmap for glyph storage,
146 * allowing mixing glyphs of different sizes without paying a penalty
147 * for switching between mask pixmaps. (Note that for a size of font
148 * right at the border between two sizes, we might be switching for almost
151 * This function allocates the storage pixmap, and then fills in the
152 * rest of the allocated structures for all caches with the given format.
155 exaRealizeGlyphCaches(ScreenPtr pScreen
, unsigned int format
)
157 ExaScreenPriv(pScreen
);
159 int depth
= PIXMAN_FORMAT_DEPTH(format
);
160 PictFormatPtr pPictFormat
;
163 CARD32 component_alpha
;
168 pPictFormat
= PictureMatchFormat(pScreen
, depth
, format
);
172 /* Compute the total vertical size needed for the format */
175 for (i
= 0; i
< EXA_NUM_GLYPH_CACHES
; i
++) {
176 ExaGlyphCachePtr cache
= &pExaScr
->glyphCaches
[i
];
179 if (cache
->format
!= format
)
182 cache
->yOffset
= height
;
184 rows
= (cache
->size
+ cache
->columns
- 1) / cache
->columns
;
185 height
+= rows
* cache
->glyphHeight
;
188 /* Now allocate the pixmap and picture */
189 pPixmap
= (*pScreen
->CreatePixmap
) (pScreen
,
190 CACHE_PICTURE_WIDTH
, height
, depth
, 0);
194 component_alpha
= NeedsComponent(pPictFormat
->format
);
195 pPicture
= CreatePicture(0, &pPixmap
->drawable
, pPictFormat
,
196 CPComponentAlpha
, &component_alpha
, serverClient
,
199 (*pScreen
->DestroyPixmap
) (pPixmap
); /* picture holds a refcount */
204 /* And store the picture in all the caches for the format */
205 for (i
= 0; i
< EXA_NUM_GLYPH_CACHES
; i
++) {
206 ExaGlyphCachePtr cache
= &pExaScr
->glyphCaches
[i
];
209 if (cache
->format
!= format
)
212 cache
->picture
= pPicture
;
213 cache
->picture
->refcnt
++;
214 cache
->hashEntries
= malloc(sizeof(int) * cache
->hashSize
);
215 cache
->glyphs
= malloc(sizeof(ExaCachedGlyphRec
) * cache
->size
);
216 cache
->glyphCount
= 0;
218 if (!cache
->hashEntries
|| !cache
->glyphs
)
221 for (j
= 0; j
< cache
->hashSize
; j
++)
222 cache
->hashEntries
[j
] = -1;
224 cache
->evictionPosition
= rand() % cache
->size
;
227 /* Each cache references the picture individually */
228 FreePicture((pointer
) pPicture
, (XID
) 0);
232 exaUnrealizeGlyphCaches(pScreen
, format
);
237 exaGlyphsFini(ScreenPtr pScreen
)
239 ExaScreenPriv(pScreen
);
242 for (i
= 0; i
< EXA_NUM_GLYPH_CACHES
; i
++) {
243 ExaGlyphCachePtr cache
= &pExaScr
->glyphCaches
[i
];
246 exaUnrealizeGlyphCaches(pScreen
, cache
->format
);
251 exaGlyphCacheHashLookup(ExaGlyphCachePtr cache
, GlyphPtr pGlyph
)
255 slot
= (*(CARD32
*) pGlyph
->sha1
) % cache
->hashSize
;
257 while (TRUE
) { /* hash table can never be full */
258 int entryPos
= cache
->hashEntries
[slot
];
264 (pGlyph
->sha1
, cache
->glyphs
[entryPos
].sha1
,
265 sizeof(pGlyph
->sha1
)) == 0) {
271 slot
= cache
->hashSize
- 1;
276 exaGlyphCacheHashInsert(ExaGlyphCachePtr cache
, GlyphPtr pGlyph
, int pos
)
280 memcpy(cache
->glyphs
[pos
].sha1
, pGlyph
->sha1
, sizeof(pGlyph
->sha1
));
282 slot
= (*(CARD32
*) pGlyph
->sha1
) % cache
->hashSize
;
284 while (TRUE
) { /* hash table can never be full */
285 if (cache
->hashEntries
[slot
] == -1) {
286 cache
->hashEntries
[slot
] = pos
;
292 slot
= cache
->hashSize
- 1;
297 exaGlyphCacheHashRemove(ExaGlyphCachePtr cache
, int pos
)
300 int emptiedSlot
= -1;
302 slot
= (*(CARD32
*) cache
->glyphs
[pos
].sha1
) % cache
->hashSize
;
304 while (TRUE
) { /* hash table can never be full */
305 int entryPos
= cache
->hashEntries
[slot
];
310 if (entryPos
== pos
) {
311 cache
->hashEntries
[slot
] = -1;
314 else if (emptiedSlot
!= -1) {
315 /* See if we can move this entry into the emptied slot, we can't
316 * do that if if entry would have hashed between the current position
317 * and the emptied slot. (taking wrapping into account). Bad positions
326 * i - slot, j - emptiedSlot
332 (*(CARD32
*) cache
->glyphs
[entryPos
].sha1
) % cache
->hashSize
;
334 if (!((entrySlot
>= slot
&& entrySlot
< emptiedSlot
) ||
335 (emptiedSlot
< slot
&&
336 (entrySlot
< emptiedSlot
|| entrySlot
>= slot
)))) {
337 cache
->hashEntries
[emptiedSlot
] = entryPos
;
338 cache
->hashEntries
[slot
] = -1;
345 slot
= cache
->hashSize
- 1;
349 #define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth)
350 #define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight)
352 /* The most efficient thing to way to upload the glyph to the screen
353 * is to use the UploadToScreen() driver hook; this allows us to
354 * pipeline glyph uploads and to avoid creating gpu backed pixmaps for
355 * glyphs that we'll never use again.
357 * If we can't do it with UploadToScreen (because the glyph has a gpu copy,
358 * etc), we fall back to CompositePicture.
360 * We need to damage the cache pixmap manually in either case because the damage
361 * layer unwrapped the picture screen before calling exaGlyphs.
364 exaGlyphCacheUploadGlyph(ScreenPtr pScreen
,
365 ExaGlyphCachePtr cache
, int x
, int y
, GlyphPtr pGlyph
)
367 ExaScreenPriv(pScreen
);
368 PicturePtr pGlyphPicture
= GetGlyphPicture(pGlyph
, pScreen
);
369 PixmapPtr pGlyphPixmap
= (PixmapPtr
) pGlyphPicture
->pDrawable
;
371 ExaPixmapPriv(pGlyphPixmap
);
372 PixmapPtr pCachePixmap
= (PixmapPtr
) cache
->picture
->pDrawable
;
374 if (!pExaScr
->info
->UploadToScreen
|| pExaScr
->swappedOut
||
375 pExaPixmap
->accel_blocked
)
378 /* If the glyph pixmap is already uploaded, no point in doing
380 if (exaPixmapHasGpuCopy(pGlyphPixmap
))
383 /* UploadToScreen only works if bpp match */
384 if (pGlyphPixmap
->drawable
.bitsPerPixel
!=
385 pCachePixmap
->drawable
.bitsPerPixel
)
388 if (pExaScr
->do_migration
) {
389 ExaMigrationRec pixmaps
[1];
391 /* cache pixmap must have a gpu copy. */
392 pixmaps
[0].as_dst
= TRUE
;
393 pixmaps
[0].as_src
= FALSE
;
394 pixmaps
[0].pPix
= pCachePixmap
;
395 pixmaps
[0].pReg
= NULL
;
396 exaDoMigration(pixmaps
, 1, TRUE
);
399 if (!exaPixmapHasGpuCopy(pCachePixmap
))
402 /* x,y are in pixmap coordinates, no need for cache{X,Y}off */
403 if (pExaScr
->info
->UploadToScreen(pCachePixmap
,
408 (char *) pExaPixmap
->sys_ptr
,
409 pExaPixmap
->sys_pitch
))
413 CompositePicture(PictOpSrc
,
417 0, 0, 0, 0, x
, y
, pGlyph
->info
.width
, pGlyph
->info
.height
);
420 /* The cache pixmap isn't a window, so no need to offset coordinates. */
421 exaPixmapDirty(pCachePixmap
,
422 x
, y
, x
+ cache
->glyphWidth
, y
+ cache
->glyphHeight
);
425 static ExaGlyphCacheResult
426 exaGlyphCacheBufferGlyph(ScreenPtr pScreen
,
427 ExaGlyphCachePtr cache
,
428 ExaGlyphBufferPtr buffer
,
434 INT16 xMask
, INT16 yMask
, INT16 xDst
, INT16 yDst
)
436 ExaCompositeRectPtr rect
;
440 if (buffer
->mask
&& buffer
->mask
!= cache
->picture
)
441 return ExaGlyphNeedFlush
;
443 if (!cache
->picture
) {
444 if (!exaRealizeGlyphCaches(pScreen
, cache
->format
))
448 DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n",
449 cache
->glyphWidth
, cache
->glyphHeight
,
450 cache
->format
== PICT_a8
? "A" : "ARGB",
451 (long) *(CARD32
*) pGlyph
->sha1
));
453 pos
= exaGlyphCacheHashLookup(cache
, pGlyph
);
455 DBG_GLYPH_CACHE((" found existing glyph at %d\n", pos
));
460 if (cache
->glyphCount
< cache
->size
) {
461 /* Space remaining; we fill from the start */
462 pos
= cache
->glyphCount
;
466 DBG_GLYPH_CACHE((" storing glyph in free space at %d\n", pos
));
468 exaGlyphCacheHashInsert(cache
, pGlyph
, pos
);
472 /* Need to evict an entry. We have to see if any glyphs
473 * already in the output buffer were at this position in
476 pos
= cache
->evictionPosition
;
479 DBG_GLYPH_CACHE((" evicting glyph at %d\n", pos
));
483 for (i
= 0; i
< buffer
->count
; i
++) {
485 (buffer
->rects
[i
].xMask
== x
&&
486 buffer
->rects
[i
].yMask
==
487 y
) : (buffer
->rects
[i
].xSrc
== x
&&
488 buffer
->rects
[i
].ySrc
== y
)) {
489 DBG_GLYPH_CACHE((" must flush buffer\n"));
490 return ExaGlyphNeedFlush
;
495 /* OK, we're all set, swap in the new glyph */
496 exaGlyphCacheHashRemove(cache
, pos
);
497 exaGlyphCacheHashInsert(cache
, pGlyph
, pos
);
499 /* And pick a new eviction position */
500 cache
->evictionPosition
= rand() % cache
->size
;
503 exaGlyphCacheUploadGlyph(pScreen
, cache
, x
, y
, pGlyph
);
506 buffer
->mask
= cache
->picture
;
508 rect
= &buffer
->rects
[buffer
->count
];
526 rect
->width
= pGlyph
->info
.width
;
527 rect
->height
= pGlyph
->info
.height
;
531 return ExaGlyphSuccess
;
537 static ExaGlyphCacheResult
538 exaBufferGlyph(ScreenPtr pScreen
,
539 ExaGlyphBufferPtr buffer
,
544 INT16 ySrc
, INT16 xMask
, INT16 yMask
, INT16 xDst
, INT16 yDst
)
546 ExaScreenPriv(pScreen
);
547 unsigned int format
= (GetGlyphPicture(pGlyph
, pScreen
))->format
;
548 int width
= pGlyph
->info
.width
;
549 int height
= pGlyph
->info
.height
;
550 ExaCompositeRectPtr rect
;
554 if (buffer
->count
== GLYPH_BUFFER_SIZE
)
555 return ExaGlyphNeedFlush
;
557 if (PICT_FORMAT_BPP(format
) == 1)
560 for (i
= 0; i
< EXA_NUM_GLYPH_CACHES
; i
++) {
561 ExaGlyphCachePtr cache
= &pExaScr
->glyphCaches
[i
];
563 if (format
== cache
->format
&&
564 width
<= cache
->glyphWidth
&& height
<= cache
->glyphHeight
) {
565 ExaGlyphCacheResult result
= exaGlyphCacheBufferGlyph(pScreen
,
580 case ExaGlyphSuccess
:
581 case ExaGlyphNeedFlush
:
587 /* Couldn't find the glyph in the cache, use the glyph picture directly */
589 mask
= GetGlyphPicture(pGlyph
, pScreen
);
590 if (buffer
->mask
&& buffer
->mask
!= mask
)
591 return ExaGlyphNeedFlush
;
595 rect
= &buffer
->rects
[buffer
->count
];
603 rect
->height
= height
;
607 return ExaGlyphSuccess
;
611 exaGlyphsToMask(PicturePtr pMask
, ExaGlyphBufferPtr buffer
)
613 exaCompositeRects(PictOpAdd
, buffer
->mask
, NULL
, pMask
,
614 buffer
->count
, buffer
->rects
);
621 exaGlyphsToDst(PicturePtr pSrc
, PicturePtr pDst
, ExaGlyphBufferPtr buffer
)
623 exaCompositeRects(PictOpOver
, pSrc
, buffer
->mask
, pDst
, buffer
->count
,
630 /* Cut and paste from render/glyph.c - probably should export it instead */
632 GlyphExtents(int nlist
, GlyphListPtr list
, GlyphPtr
* glyphs
, BoxPtr extents
)
641 extents
->x1
= MAXSHORT
;
642 extents
->x2
= MINSHORT
;
643 extents
->y1
= MAXSHORT
;
644 extents
->y2
= MINSHORT
;
652 x1
= x
- glyph
->info
.x
;
655 y1
= y
- glyph
->info
.y
;
658 x2
= x1
+ glyph
->info
.width
;
661 y2
= y1
+ glyph
->info
.height
;
664 if (x1
< extents
->x1
)
666 if (x2
> extents
->x2
)
668 if (y1
< extents
->y1
)
670 if (y2
> extents
->y2
)
672 x
+= glyph
->info
.xOff
;
673 y
+= glyph
->info
.yOff
;
682 PictFormatPtr maskFormat
,
684 INT16 ySrc
, int nlist
, GlyphListPtr list
, GlyphPtr
* glyphs
)
686 PixmapPtr pMaskPixmap
= 0;
687 PicturePtr pMask
= NULL
;
688 ScreenPtr pScreen
= pDst
->pDrawable
->pScreen
;
689 int width
= 0, height
= 0;
691 int first_xOff
= list
->xOff
, first_yOff
= list
->yOff
;
695 BoxRec extents
= { 0, 0, 0, 0 };
696 CARD32 component_alpha
;
697 ExaGlyphBuffer buffer
;
700 ExaScreenPriv(pScreen
);
704 GlyphExtents(nlist
, list
, glyphs
, &extents
);
706 if (extents
.x2
<= extents
.x1
|| extents
.y2
<= extents
.y1
)
708 width
= extents
.x2
- extents
.x1
;
709 height
= extents
.y2
- extents
.y1
;
711 if (maskFormat
->depth
== 1) {
712 PictFormatPtr a8Format
= PictureMatchFormat(pScreen
, 8, PICT_a8
);
715 maskFormat
= a8Format
;
718 pMaskPixmap
= (*pScreen
->CreatePixmap
) (pScreen
, width
, height
,
720 CREATE_PIXMAP_USAGE_SCRATCH
);
723 component_alpha
= NeedsComponent(maskFormat
->format
);
724 pMask
= CreatePicture(0, &pMaskPixmap
->drawable
,
725 maskFormat
, CPComponentAlpha
, &component_alpha
,
726 serverClient
, &error
);
728 (!component_alpha
&& pExaScr
->info
->CheckComposite
&&
729 !(*pExaScr
->info
->CheckComposite
) (PictOpAdd
, pSrc
, NULL
, pMask
)))
731 PictFormatPtr argbFormat
;
733 (*pScreen
->DestroyPixmap
) (pMaskPixmap
);
738 /* The driver can't seem to composite to a8, let's try argb (but
739 * without component-alpha) */
740 FreePicture((pointer
) pMask
, (XID
) 0);
742 argbFormat
= PictureMatchFormat(pScreen
, 32, PICT_a8r8g8b8
);
745 maskFormat
= argbFormat
;
747 pMaskPixmap
= (*pScreen
->CreatePixmap
) (pScreen
, width
, height
,
749 CREATE_PIXMAP_USAGE_SCRATCH
);
753 pMask
= CreatePicture(0, &pMaskPixmap
->drawable
, maskFormat
, 0, 0,
754 serverClient
, &error
);
756 (*pScreen
->DestroyPixmap
) (pMaskPixmap
);
760 pGC
= GetScratchGC(pMaskPixmap
->drawable
.depth
, pScreen
);
761 ValidateGC(&pMaskPixmap
->drawable
, pGC
);
765 rect
.height
= height
;
766 (*pGC
->ops
->PolyFillRect
) (&pMaskPixmap
->drawable
, pGC
, 1, &rect
);
784 if (glyph
->info
.width
> 0 && glyph
->info
.height
> 0) {
785 /* pGlyph->info.{x,y} compensate for empty space in the glyph. */
787 if (exaBufferGlyph(pScreen
, &buffer
, glyph
, NULL
, pMask
,
788 0, 0, 0, 0, x
- glyph
->info
.x
,
789 y
- glyph
->info
.y
) ==
791 exaGlyphsToMask(pMask
, &buffer
);
792 exaBufferGlyph(pScreen
, &buffer
, glyph
, NULL
, pMask
,
793 0, 0, 0, 0, x
- glyph
->info
.x
,
798 if (exaBufferGlyph(pScreen
, &buffer
, glyph
, pSrc
, pDst
,
799 xSrc
+ (x
- glyph
->info
.x
) - first_xOff
,
800 ySrc
+ (y
- glyph
->info
.y
) - first_yOff
,
801 0, 0, x
- glyph
->info
.x
,
803 == ExaGlyphNeedFlush
) {
804 exaGlyphsToDst(pSrc
, pDst
, &buffer
);
805 exaBufferGlyph(pScreen
, &buffer
, glyph
, pSrc
, pDst
,
806 xSrc
+ (x
- glyph
->info
.x
) - first_xOff
,
807 ySrc
+ (y
- glyph
->info
.y
) - first_yOff
,
808 0, 0, x
- glyph
->info
.x
,
814 x
+= glyph
->info
.xOff
;
815 y
+= glyph
->info
.yOff
;
822 exaGlyphsToMask(pMask
, &buffer
);
824 exaGlyphsToDst(pSrc
, pDst
, &buffer
);
834 xSrc
+ x
- first_xOff
,
835 ySrc
+ y
- first_yOff
, 0, 0, x
, y
, width
, height
);
836 FreePicture((pointer
) pMask
, (XID
) 0);
837 (*pScreen
->DestroyPixmap
) (pMaskPixmap
);