1/*
2 * Copyright © 2008 Red Hat, Inc.
3 * Partly based on code Copyright © 2000 SuSE, Inc.
4 *
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.
14 *
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.
21 *
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.
31 *
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.
38 *
39 * Author: Owen Taylor <otaylor@fishsoup.net>
40 * Based on code by: Keith Packard
41 */
42
43#ifdef HAVE_DIX_CONFIG_H
44#include <dix-config.h>
45#endif
46
47#include <stdlib.h>
48
49#include "exa_priv.h"
50
51#include "mipict.h"
52
53#if DEBUG_GLYPH_CACHE
54#define DBG_GLYPH_CACHE(a) ErrorF a
55#else
56#define DBG_GLYPH_CACHE(a)
57#endif
58
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
61 * the driver.
62 */
63#define CACHE_PICTURE_WIDTH 1024
64
65/* Maximum number of glyphs we buffer on the stack before flushing
66 * rendering to the mask or destination surface.
67 */
68#define GLYPH_BUFFER_SIZE 256
69
70typedef struct {
71    PicturePtr mask;
72    ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE];
73    int count;
74} ExaGlyphBuffer, *ExaGlyphBufferPtr;
75
76typedef enum {
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;
81
82void
83exaGlyphsInit(ScreenPtr pScreen)
84{
85    ExaScreenPriv(pScreen);
86    int i = 0;
87
88    memset(pExaScr->glyphCaches, 0, sizeof(pExaScr->glyphCaches));
89
90    pExaScr->glyphCaches[i].format = PICT_a8;
91    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight =
92        16;
93    i++;
94    pExaScr->glyphCaches[i].format = PICT_a8;
95    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight =
96        32;
97    i++;
98    pExaScr->glyphCaches[i].format = PICT_a8r8g8b8;
99    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight =
100        16;
101    i++;
102    pExaScr->glyphCaches[i].format = PICT_a8r8g8b8;
103    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight =
104        32;
105    i++;
106
107    assert(i == EXA_NUM_GLYPH_CACHES);
108
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;
114    }
115}
116
117static void
118exaUnrealizeGlyphCaches(ScreenPtr pScreen, unsigned int format)
119{
120    ExaScreenPriv(pScreen);
121    int i;
122
123    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
124        ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
125
126        if (cache->format != format)
127            continue;
128
129        if (cache->picture) {
130            FreePicture((void *) cache->picture, (XID) 0);
131            cache->picture = NULL;
132        }
133
134        free(cache->hashEntries);
135        cache->hashEntries = NULL;
136
137        free(cache->glyphs);
138        cache->glyphs = NULL;
139        cache->glyphCount = 0;
140    }
141}
142
143#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0)
144
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
149 * every glyph.)
150 *
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.
153 */
154static Bool
155exaRealizeGlyphCaches(ScreenPtr pScreen, unsigned int format)
156{
157    ExaScreenPriv(pScreen);
158
159    int depth = PIXMAN_FORMAT_DEPTH(format);
160    PictFormatPtr pPictFormat;
161    PixmapPtr pPixmap;
162    PicturePtr pPicture;
163    CARD32 component_alpha;
164    int height;
165    int i;
166    int error;
167
168    pPictFormat = PictureMatchFormat(pScreen, depth, format);
169    if (!pPictFormat)
170        return FALSE;
171
172    /* Compute the total vertical size needed for the format */
173
174    height = 0;
175    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
176        ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
177        int rows;
178
179        if (cache->format != format)
180            continue;
181
182        cache->yOffset = height;
183
184        rows = (cache->size + cache->columns - 1) / cache->columns;
185        height += rows * cache->glyphHeight;
186    }
187
188    /* Now allocate the pixmap and picture */
189    pPixmap = (*pScreen->CreatePixmap) (pScreen,
190                                        CACHE_PICTURE_WIDTH, height, depth, 0);
191    if (!pPixmap)
192        return FALSE;
193
194    component_alpha = NeedsComponent(pPictFormat->format);
195    pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat,
196                             CPComponentAlpha, &component_alpha, serverClient,
197                             &error);
198
199    (*pScreen->DestroyPixmap) (pPixmap);        /* picture holds a refcount */
200
201    if (!pPicture)
202        return FALSE;
203
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];
207        int j;
208
209        if (cache->format != format)
210            continue;
211
212        cache->picture = pPicture;
213        cache->picture->refcnt++;
214        cache->hashEntries = xallocarray(cache->hashSize, sizeof(int));
215        cache->glyphs = xallocarray(cache->size, sizeof(ExaCachedGlyphRec));
216        cache->glyphCount = 0;
217
218        if (!cache->hashEntries || !cache->glyphs)
219            goto bail;
220
221        for (j = 0; j < cache->hashSize; j++)
222            cache->hashEntries[j] = -1;
223
224        cache->evictionPosition = rand() % cache->size;
225    }
226
227    /* Each cache references the picture individually */
228    FreePicture((void *) pPicture, (XID) 0);
229    return TRUE;
230
231 bail:
232    exaUnrealizeGlyphCaches(pScreen, format);
233    return FALSE;
234}
235
236void
237exaGlyphsFini(ScreenPtr pScreen)
238{
239    ExaScreenPriv(pScreen);
240    int i;
241
242    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
243        ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
244
245        if (cache->picture)
246            exaUnrealizeGlyphCaches(pScreen, cache->format);
247    }
248}
249
250static int
251exaGlyphCacheHashLookup(ExaGlyphCachePtr cache, GlyphPtr pGlyph)
252{
253    int slot;
254
255    slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize;
256
257    while (TRUE) {              /* hash table can never be full */
258        int entryPos = cache->hashEntries[slot];
259
260        if (entryPos == -1)
261            return -1;
262
263        if (memcmp
264            (pGlyph->sha1, cache->glyphs[entryPos].sha1,
265             sizeof(pGlyph->sha1)) == 0) {
266            return entryPos;
267        }
268
269        slot--;
270        if (slot < 0)
271            slot = cache->hashSize - 1;
272    }
273}
274
275static void
276exaGlyphCacheHashInsert(ExaGlyphCachePtr cache, GlyphPtr pGlyph, int pos)
277{
278    int slot;
279
280    memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1));
281
282    slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize;
283
284    while (TRUE) {              /* hash table can never be full */
285        if (cache->hashEntries[slot] == -1) {
286            cache->hashEntries[slot] = pos;
287            return;
288        }
289
290        slot--;
291        if (slot < 0)
292            slot = cache->hashSize - 1;
293    }
294}
295
296static void
297exaGlyphCacheHashRemove(ExaGlyphCachePtr cache, int pos)
298{
299    int slot;
300    int emptiedSlot = -1;
301
302    slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize;
303
304    while (TRUE) {              /* hash table can never be full */
305        int entryPos = cache->hashEntries[slot];
306
307        if (entryPos == -1)
308            return;
309
310        if (entryPos == pos) {
311            cache->hashEntries[slot] = -1;
312            emptiedSlot = slot;
313        }
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
318             * are:
319             *
320             * |   XXXXXXXXXX             |
321             *     i         j
322             *
323             * |XXX                   XXXX|
324             *     j                  i
325             *
326             * i - slot, j - emptiedSlot
327             *
328             * (Knuth 6.4R)
329             */
330
331            int entrySlot =
332                (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize;
333
334            if (!((entrySlot >= slot && entrySlot < emptiedSlot) ||
335                  (emptiedSlot < slot &&
336                   (entrySlot < emptiedSlot || entrySlot >= slot)))) {
337                cache->hashEntries[emptiedSlot] = entryPos;
338                cache->hashEntries[slot] = -1;
339                emptiedSlot = slot;
340            }
341        }
342
343        slot--;
344        if (slot < 0)
345            slot = cache->hashSize - 1;
346    }
347}
348
349#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth)
350#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight)
351
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.
356 *
357 * If we can't do it with UploadToScreen (because the glyph has a gpu copy,
358 * etc), we fall back to CompositePicture.
359 *
360 * We need to damage the cache pixmap manually in either case because the damage
361 * layer unwrapped the picture screen before calling exaGlyphs.
362 */
363static void
364exaGlyphCacheUploadGlyph(ScreenPtr pScreen,
365                         ExaGlyphCachePtr cache, int x, int y, GlyphPtr pGlyph)
366{
367    ExaScreenPriv(pScreen);
368    PicturePtr pGlyphPicture = GetGlyphPicture(pGlyph, pScreen);
369    PixmapPtr pGlyphPixmap = (PixmapPtr) pGlyphPicture->pDrawable;
370
371    ExaPixmapPriv(pGlyphPixmap);
372    PixmapPtr pCachePixmap = (PixmapPtr) cache->picture->pDrawable;
373
374    if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut ||
375        pExaPixmap->accel_blocked)
376        goto composite;
377
378    /* If the glyph pixmap is already uploaded, no point in doing
379     * things this way */
380    if (exaPixmapHasGpuCopy(pGlyphPixmap))
381        goto composite;
382
383    /* UploadToScreen only works if bpp match */
384    if (pGlyphPixmap->drawable.bitsPerPixel !=
385        pCachePixmap->drawable.bitsPerPixel)
386        goto composite;
387
388    if (pExaScr->do_migration) {
389        ExaMigrationRec pixmaps[1];
390
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);
397    }
398
399    if (!exaPixmapHasGpuCopy(pCachePixmap))
400        goto composite;
401
402    /* x,y are in pixmap coordinates, no need for cache{X,Y}off */
403    if (pExaScr->info->UploadToScreen(pCachePixmap,
404                                      x,
405                                      y,
406                                      pGlyph->info.width,
407                                      pGlyph->info.height,
408                                      (char *) pExaPixmap->sys_ptr,
409                                      pExaPixmap->sys_pitch))
410        goto damage;
411
412 composite:
413    CompositePicture(PictOpSrc,
414                     pGlyphPicture,
415                     None,
416                     cache->picture,
417                     0, 0, 0, 0, x, y, pGlyph->info.width, pGlyph->info.height);
418
419 damage:
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);
423}
424
425static ExaGlyphCacheResult
426exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
427                         ExaGlyphCachePtr cache,
428                         ExaGlyphBufferPtr buffer,
429                         GlyphPtr pGlyph,
430                         PicturePtr pSrc,
431                         PicturePtr pDst,
432                         INT16 xSrc,
433                         INT16 ySrc,
434                         INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst)
435{
436    ExaCompositeRectPtr rect;
437    int pos;
438    int x, y;
439
440    if (buffer->mask && buffer->mask != cache->picture)
441        return ExaGlyphNeedFlush;
442
443    if (!cache->picture) {
444        if (!exaRealizeGlyphCaches(pScreen, cache->format))
445            return ExaGlyphFail;
446    }
447
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));
452
453    pos = exaGlyphCacheHashLookup(cache, pGlyph);
454    if (pos != -1) {
455        DBG_GLYPH_CACHE(("  found existing glyph at %d\n", pos));
456        x = CACHE_X(pos);
457        y = CACHE_Y(pos);
458    }
459    else {
460        if (cache->glyphCount < cache->size) {
461            /* Space remaining; we fill from the start */
462            pos = cache->glyphCount;
463            x = CACHE_X(pos);
464            y = CACHE_Y(pos);
465            cache->glyphCount++;
466            DBG_GLYPH_CACHE(("  storing glyph in free space at %d\n", pos));
467
468            exaGlyphCacheHashInsert(cache, pGlyph, pos);
469
470        }
471        else {
472            /* Need to evict an entry. We have to see if any glyphs
473             * already in the output buffer were at this position in
474             * the cache
475             */
476            pos = cache->evictionPosition;
477            x = CACHE_X(pos);
478            y = CACHE_Y(pos);
479            DBG_GLYPH_CACHE(("  evicting glyph at %d\n", pos));
480            if (buffer->count) {
481                int i;
482
483                for (i = 0; i < buffer->count; i++) {
484                    if (pSrc ?
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;
491                    }
492                }
493            }
494
495            /* OK, we're all set, swap in the new glyph */
496            exaGlyphCacheHashRemove(cache, pos);
497            exaGlyphCacheHashInsert(cache, pGlyph, pos);
498
499            /* And pick a new eviction position */
500            cache->evictionPosition = rand() % cache->size;
501        }
502
503        exaGlyphCacheUploadGlyph(pScreen, cache, x, y, pGlyph);
504    }
505
506    buffer->mask = cache->picture;
507
508    rect = &buffer->rects[buffer->count];
509
510    if (pSrc) {
511        rect->xSrc = xSrc;
512        rect->ySrc = ySrc;
513        rect->xMask = x;
514        rect->yMask = y;
515    }
516    else {
517        rect->xSrc = x;
518        rect->ySrc = y;
519        rect->xMask = 0;
520        rect->yMask = 0;
521    }
522
523    rect->pDst = pDst;
524    rect->xDst = xDst;
525    rect->yDst = yDst;
526    rect->width = pGlyph->info.width;
527    rect->height = pGlyph->info.height;
528
529    buffer->count++;
530
531    return ExaGlyphSuccess;
532}
533
534#undef CACHE_X
535#undef CACHE_Y
536
537static ExaGlyphCacheResult
538exaBufferGlyph(ScreenPtr pScreen,
539               ExaGlyphBufferPtr buffer,
540               GlyphPtr pGlyph,
541               PicturePtr pSrc,
542               PicturePtr pDst,
543               INT16 xSrc,
544               INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst)
545{
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;
551    PicturePtr mask;
552    int i;
553
554    if (buffer->count == GLYPH_BUFFER_SIZE)
555        return ExaGlyphNeedFlush;
556
557    if (PICT_FORMAT_BPP(format) == 1)
558        format = PICT_a8;
559
560    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
561        ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
562
563        if (format == cache->format &&
564            width <= cache->glyphWidth && height <= cache->glyphHeight) {
565            ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen,
566                                                                  &pExaScr->
567                                                                  glyphCaches
568                                                                  [i],
569                                                                  buffer,
570                                                                  pGlyph,
571                                                                  pSrc,
572                                                                  pDst,
573                                                                  xSrc, ySrc,
574                                                                  xMask, yMask,
575                                                                  xDst, yDst);
576
577            switch (result) {
578            case ExaGlyphFail:
579                break;
580            case ExaGlyphSuccess:
581            case ExaGlyphNeedFlush:
582                return result;
583            }
584        }
585    }
586
587    /* Couldn't find the glyph in the cache, use the glyph picture directly */
588
589    mask = GetGlyphPicture(pGlyph, pScreen);
590    if (buffer->mask && buffer->mask != mask)
591        return ExaGlyphNeedFlush;
592
593    buffer->mask = mask;
594
595    rect = &buffer->rects[buffer->count];
596    rect->xSrc = xSrc;
597    rect->ySrc = ySrc;
598    rect->xMask = xMask;
599    rect->yMask = yMask;
600    rect->xDst = xDst;
601    rect->yDst = yDst;
602    rect->width = width;
603    rect->height = height;
604
605    buffer->count++;
606
607    return ExaGlyphSuccess;
608}
609
610static void
611exaGlyphsToMask(PicturePtr pMask, ExaGlyphBufferPtr buffer)
612{
613    exaCompositeRects(PictOpAdd, buffer->mask, NULL, pMask,
614                      buffer->count, buffer->rects);
615
616    buffer->count = 0;
617    buffer->mask = NULL;
618}
619
620static void
621exaGlyphsToDst(CARD8 op, PicturePtr pSrc, PicturePtr pDst, ExaGlyphBufferPtr buffer)
622{
623    exaCompositeRects(op, pSrc, buffer->mask, pDst, buffer->count,
624                      buffer->rects);
625
626    buffer->count = 0;
627    buffer->mask = NULL;
628}
629
630/* Cut and paste from render/glyph.c - probably should export it instead */
631static void
632GlyphExtents(int nlist, GlyphListPtr list, GlyphPtr * glyphs, BoxPtr extents)
633{
634    int x1, x2, y1, y2;
635    int n;
636    GlyphPtr glyph;
637    int x, y;
638
639    x = 0;
640    y = 0;
641    extents->x1 = MAXSHORT;
642    extents->x2 = MINSHORT;
643    extents->y1 = MAXSHORT;
644    extents->y2 = MINSHORT;
645    while (nlist--) {
646        x += list->xOff;
647        y += list->yOff;
648        n = list->len;
649        list++;
650        while (n--) {
651            glyph = *glyphs++;
652            x1 = x - glyph->info.x;
653            if (x1 < MINSHORT)
654                x1 = MINSHORT;
655            y1 = y - glyph->info.y;
656            if (y1 < MINSHORT)
657                y1 = MINSHORT;
658            x2 = x1 + glyph->info.width;
659            if (x2 > MAXSHORT)
660                x2 = MAXSHORT;
661            y2 = y1 + glyph->info.height;
662            if (y2 > MAXSHORT)
663                y2 = MAXSHORT;
664            if (x1 < extents->x1)
665                extents->x1 = x1;
666            if (x2 > extents->x2)
667                extents->x2 = x2;
668            if (y1 < extents->y1)
669                extents->y1 = y1;
670            if (y2 > extents->y2)
671                extents->y2 = y2;
672            x += glyph->info.xOff;
673            y += glyph->info.yOff;
674        }
675    }
676}
677
678void
679exaGlyphs(CARD8 op,
680          PicturePtr pSrc,
681          PicturePtr pDst,
682          PictFormatPtr maskFormat,
683          INT16 xSrc,
684          INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs)
685{
686    PixmapPtr pMaskPixmap = 0;
687    PicturePtr pMask = NULL;
688    ScreenPtr pScreen = pDst->pDrawable->pScreen;
689    int width = 0, height = 0;
690    int x, y;
691    int first_xOff = list->xOff, first_yOff = list->yOff;
692    int n;
693    GlyphPtr glyph;
694    int error;
695    BoxRec extents = { 0, 0, 0, 0 };
696    CARD32 component_alpha;
697    ExaGlyphBuffer buffer;
698
699    if (maskFormat) {
700        ExaScreenPriv(pScreen);
701        GCPtr pGC;
702        xRectangle rect;
703
704        GlyphExtents(nlist, list, glyphs, &extents);
705
706        if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1)
707            return;
708        width = extents.x2 - extents.x1;
709        height = extents.y2 - extents.y1;
710
711        if (maskFormat->depth == 1) {
712            PictFormatPtr a8Format = PictureMatchFormat(pScreen, 8, PICT_a8);
713
714            if (a8Format)
715                maskFormat = a8Format;
716        }
717
718        pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height,
719                                                maskFormat->depth,
720                                                CREATE_PIXMAP_USAGE_SCRATCH);
721        if (!pMaskPixmap)
722            return;
723        component_alpha = NeedsComponent(maskFormat->format);
724        pMask = CreatePicture(0, &pMaskPixmap->drawable,
725                              maskFormat, CPComponentAlpha, &component_alpha,
726                              serverClient, &error);
727        if (!pMask ||
728            (!component_alpha && pExaScr->info->CheckComposite &&
729             !(*pExaScr->info->CheckComposite) (PictOpAdd, pSrc, NULL, pMask)))
730        {
731            PictFormatPtr argbFormat;
732
733            (*pScreen->DestroyPixmap) (pMaskPixmap);
734
735            if (!pMask)
736                return;
737
738            /* The driver can't seem to composite to a8, let's try argb (but
739             * without component-alpha) */
740            FreePicture((void *) pMask, (XID) 0);
741
742            argbFormat = PictureMatchFormat(pScreen, 32, PICT_a8r8g8b8);
743
744            if (argbFormat)
745                maskFormat = argbFormat;
746
747            pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height,
748                                                    maskFormat->depth,
749                                                    CREATE_PIXMAP_USAGE_SCRATCH);
750            if (!pMaskPixmap)
751                return;
752
753            pMask = CreatePicture(0, &pMaskPixmap->drawable, maskFormat, 0, 0,
754                                  serverClient, &error);
755            if (!pMask) {
756                (*pScreen->DestroyPixmap) (pMaskPixmap);
757                return;
758            }
759        }
760        pGC = GetScratchGC(pMaskPixmap->drawable.depth, pScreen);
761        ValidateGC(&pMaskPixmap->drawable, pGC);
762        rect.x = 0;
763        rect.y = 0;
764        rect.width = width;
765        rect.height = height;
766        (*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect);
767        FreeScratchGC(pGC);
768        x = -extents.x1;
769        y = -extents.y1;
770    }
771    else {
772        x = 0;
773        y = 0;
774    }
775    buffer.count = 0;
776    buffer.mask = NULL;
777    while (nlist--) {
778        x += list->xOff;
779        y += list->yOff;
780        n = list->len;
781        while (n--) {
782            glyph = *glyphs++;
783
784            if (glyph->info.width > 0 && glyph->info.height > 0) {
785                /* pGlyph->info.{x,y} compensate for empty space in the glyph. */
786                if (maskFormat) {
787                    if (exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask,
788                                       0, 0, 0, 0, x - glyph->info.x,
789                                       y - glyph->info.y) ==
790                        ExaGlyphNeedFlush) {
791                        exaGlyphsToMask(pMask, &buffer);
792                        exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask,
793                                       0, 0, 0, 0, x - glyph->info.x,
794                                       y - glyph->info.y);
795                    }
796                }
797                else {
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,
802                                       y - glyph->info.y)
803                        == ExaGlyphNeedFlush) {
804                        exaGlyphsToDst(op, 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,
809                                       y - glyph->info.y);
810                    }
811                }
812            }
813
814            x += glyph->info.xOff;
815            y += glyph->info.yOff;
816        }
817        list++;
818    }
819
820    if (buffer.count) {
821        if (maskFormat)
822            exaGlyphsToMask(pMask, &buffer);
823        else
824            exaGlyphsToDst(op, pSrc, pDst, &buffer);
825    }
826
827    if (maskFormat) {
828        x = extents.x1;
829        y = extents.y1;
830        CompositePicture(op,
831                         pSrc,
832                         pMask,
833                         pDst,
834                         xSrc + x - first_xOff,
835                         ySrc + y - first_yOff, 0, 0, x, y, width, height);
836        FreePicture((void *) pMask, (XID) 0);
837        (*pScreen->DestroyPixmap) (pMaskPixmap);
838    }
839}
840