exa_glyphs.c revision 4642e01f
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 source;
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 = 16;
92    i++;
93    pExaScr->glyphCaches[i].format = PICT_a8;
94    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32;
95    i++;
96    pExaScr->glyphCaches[i].format = PICT_a8r8g8b8;
97    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16;
98    i++;
99    pExaScr->glyphCaches[i].format = PICT_a8r8g8b8;
100    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32;
101    i++;
102
103    assert(i == EXA_NUM_GLYPH_CACHES);
104
105    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
106	pExaScr->glyphCaches[i].columns = CACHE_PICTURE_WIDTH / pExaScr->glyphCaches[i].glyphWidth;
107	pExaScr->glyphCaches[i].size = 256;
108	pExaScr->glyphCaches[i].hashSize = 557;
109    }
110}
111
112static void
113exaUnrealizeGlyphCaches(ScreenPtr    pScreen,
114			unsigned int format)
115{
116    ExaScreenPriv(pScreen);
117    int i;
118
119    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
120	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
121
122	if (cache->format != format)
123	    continue;
124
125	if (cache->picture) {
126	    FreePicture ((pointer) cache->picture, (XID) 0);
127	    cache->picture = NULL;
128	}
129
130	if (cache->hashEntries) {
131	    xfree(cache->hashEntries);
132	    cache->hashEntries = NULL;
133	}
134
135	if (cache->glyphs) {
136	    xfree(cache->glyphs);
137	    cache->glyphs = NULL;
138	}
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 source 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,
156		      unsigned int format)
157{
158    ExaScreenPriv(pScreen);
159
160    int depth = PIXMAN_FORMAT_DEPTH(format);
161    PictFormatPtr pPictFormat;
162    PixmapPtr pPixmap;
163    PicturePtr pPicture;
164    CARD32 component_alpha;
165    int height;
166    int i;
167    int	error;
168
169    pPictFormat = PictureMatchFormat(pScreen, depth, format);
170    if (!pPictFormat)
171	return FALSE;
172
173    /* Compute the total vertical size needed for the format */
174
175    height = 0;
176    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
177	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
178	int rows;
179
180	if (cache->format != format)
181	    continue;
182
183	cache->yOffset = height;
184
185	rows = (cache->size + cache->columns - 1) / cache->columns;
186	height += rows * cache->glyphHeight;
187    }
188
189    /* Now allocate the pixmap and picture */
190
191    pPixmap = (*pScreen->CreatePixmap) (pScreen,
192					CACHE_PICTURE_WIDTH,
193					height, depth, 0);
194    if (!pPixmap)
195	return FALSE;
196
197    component_alpha = NeedsComponent(pPictFormat->format);
198    pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat,
199			     CPComponentAlpha, &component_alpha, serverClient,
200			     &error);
201
202    (*pScreen->DestroyPixmap) (pPixmap); /* picture holds a refcount */
203
204    if (!pPicture)
205	return FALSE;
206
207    /* And store the picture in all the caches for the format */
208
209    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
210	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
211	int j;
212
213	if (cache->format != format)
214	    continue;
215
216	cache->picture = pPicture;
217	cache->picture->refcnt++;
218	cache->hashEntries = xalloc(sizeof(int) * cache->hashSize);
219	cache->glyphs = xalloc(sizeof(ExaCachedGlyphRec) * cache->size);
220	cache->glyphCount = 0;
221
222	if (!cache->hashEntries || !cache->glyphs)
223	    goto bail;
224
225	for (j = 0; j < cache->hashSize; j++)
226	    cache->hashEntries[j] = -1;
227
228	cache->evictionPosition = rand() % cache->size;
229    }
230
231    /* Each cache references the picture individually */
232    FreePicture ((pointer) pPicture, (XID) 0);
233    return TRUE;
234
235bail:
236    exaUnrealizeGlyphCaches(pScreen, format);
237    return FALSE;
238}
239
240void
241exaGlyphsFini (ScreenPtr pScreen)
242{
243    ExaScreenPriv(pScreen);
244    int i;
245
246    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
247	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
248
249	if (cache->picture)
250	    exaUnrealizeGlyphCaches(pScreen, cache->format);
251    }
252}
253
254static int
255exaGlyphCacheHashLookup(ExaGlyphCachePtr cache,
256			GlyphPtr         pGlyph)
257{
258    int slot;
259
260    slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize;
261
262    while (TRUE) { /* hash table can never be full */
263	int entryPos = cache->hashEntries[slot];
264	if (entryPos == -1)
265	    return -1;
266
267	if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){
268	    return entryPos;
269	}
270
271	slot--;
272	if (slot < 0)
273	    slot = cache->hashSize - 1;
274    }
275}
276
277static void
278exaGlyphCacheHashInsert(ExaGlyphCachePtr cache,
279			GlyphPtr         pGlyph,
280			int              pos)
281{
282    int slot;
283
284    memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1));
285
286    slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize;
287
288    while (TRUE) { /* hash table can never be full */
289	if (cache->hashEntries[slot] == -1) {
290	    cache->hashEntries[slot] = pos;
291	    return;
292	}
293
294	slot--;
295	if (slot < 0)
296	    slot = cache->hashSize - 1;
297    }
298}
299
300static void
301exaGlyphCacheHashRemove(ExaGlyphCachePtr cache,
302			int              pos)
303{
304    int slot;
305    int emptiedSlot = -1;
306
307    slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize;
308
309    while (TRUE) { /* hash table can never be full */
310	int entryPos = cache->hashEntries[slot];
311
312	if (entryPos == -1)
313	    return;
314
315	if (entryPos == pos) {
316	    cache->hashEntries[slot] = -1;
317	    emptiedSlot = slot;
318	} else if (emptiedSlot != -1) {
319	    /* See if we can move this entry into the emptied slot, we can't
320	     * do that if if entry would have hashed between the current position
321	     * and the emptied slot. (taking wrapping into account). Bad positions
322	     * are:
323	     *
324	     * |   XXXXXXXXXX             |
325	     *     i         j
326	     *
327	     * |XXX                   XXXX|
328	     *     j                  i
329	     *
330	     * i - slot, j - emptiedSlot
331	     *
332	     * (Knuth 6.4R)
333	     */
334
335	    int entrySlot = (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize;
336
337	    if (!((entrySlot >= slot && entrySlot < emptiedSlot) ||
338		  (emptiedSlot < slot && (entrySlot < emptiedSlot || entrySlot >= slot))))
339	    {
340		cache->hashEntries[emptiedSlot] = entryPos;
341		cache->hashEntries[slot] = -1;
342		emptiedSlot = slot;
343	    }
344	}
345
346	slot--;
347	if (slot < 0)
348	    slot = cache->hashSize - 1;
349    }
350}
351
352#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth)
353#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight)
354
355/* The most efficient thing to way to upload the glyph to the screen
356 * is to use the UploadToScreen() driver hook; this allows us to
357 * pipeline glyph uploads and to avoid creating offscreen pixmaps for
358 * glyphs that we'll never use again.
359 */
360static Bool
361exaGlyphCacheUploadGlyph(ScreenPtr         pScreen,
362			 ExaGlyphCachePtr  cache,
363			 int               pos,
364			 GlyphPtr          pGlyph)
365{
366    ExaScreenPriv(pScreen);
367    PicturePtr pGlyphPicture = GlyphPicture(pGlyph)[pScreen->myNum];
368    PixmapPtr pGlyphPixmap = (PixmapPtr)pGlyphPicture->pDrawable;
369    ExaPixmapPriv(pGlyphPixmap);
370    PixmapPtr pCachePixmap = (PixmapPtr)cache->picture->pDrawable;
371    ExaMigrationRec pixmaps[1];
372
373    if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut || pExaPixmap->accel_blocked)
374	return FALSE;
375
376    /* If the glyph pixmap is already uploaded, no point in doing
377     * things this way */
378    if (exaPixmapIsOffscreen(pGlyphPixmap))
379	return FALSE;
380
381    /* UploadToScreen only works if bpp match */
382    if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel)
383	return FALSE;
384
385    /* cache pixmap must be offscreen. */
386    pixmaps[0].as_dst = TRUE;
387    pixmaps[0].as_src = FALSE;
388    pixmaps[0].pPix = pCachePixmap;
389    pixmaps[0].pReg = NULL;
390    exaDoMigration (pixmaps, 1, TRUE);
391
392    if (!exaPixmapIsOffscreen(pCachePixmap))
393	return FALSE;
394
395    /* CACHE_{X,Y} are in pixmap coordinates, no need for cache{X,Y}off */
396    if (!pExaScr->info->UploadToScreen(pCachePixmap,
397				       CACHE_X(pos),
398				       CACHE_Y(pos),
399				       pGlyph->info.width,
400				       pGlyph->info.height,
401				       (char *)pExaPixmap->sys_ptr,
402				       pExaPixmap->sys_pitch))
403	return FALSE;
404
405    /* This pixmap should never be bound to a window, so no need to offset coordinates. */
406    exaPixmapDirty (pCachePixmap,
407		    CACHE_X(pos),
408		    CACHE_Y(pos),
409		    CACHE_X(pos) + pGlyph->info.width,
410		    CACHE_Y(pos) + pGlyph->info.height);
411
412    return TRUE;
413}
414
415static ExaGlyphCacheResult
416exaGlyphCacheBufferGlyph(ScreenPtr         pScreen,
417			 ExaGlyphCachePtr  cache,
418			 ExaGlyphBufferPtr buffer,
419			 GlyphPtr          pGlyph,
420			 int               xGlyph,
421			 int               yGlyph)
422{
423    ExaCompositeRectPtr rect;
424    int pos;
425
426    if (buffer->source && buffer->source != cache->picture)
427	return ExaGlyphNeedFlush;
428
429    if (!cache->picture) {
430	if (!exaRealizeGlyphCaches(pScreen, cache->format))
431	    return ExaGlyphFail;
432    }
433
434    DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n",
435		     cache->glyphWidth, cache->glyphHeight, cache->format == PICT_a8 ? "A" : "ARGB",
436		     (long)*(CARD32 *) pGlyph->sha1));
437
438    pos = exaGlyphCacheHashLookup(cache, pGlyph);
439    if (pos != -1) {
440	DBG_GLYPH_CACHE(("  found existing glyph at %d\n", pos));
441    } else {
442	if (cache->glyphCount < cache->size) {
443	    /* Space remaining; we fill from the start */
444	    pos = cache->glyphCount;
445	    cache->glyphCount++;
446	    DBG_GLYPH_CACHE(("  storing glyph in free space at %d\n", pos));
447
448	    exaGlyphCacheHashInsert(cache, pGlyph, pos);
449
450	} else {
451	    /* Need to evict an entry. We have to see if any glyphs
452	     * already in the output buffer were at this position in
453	     * the cache
454	     */
455
456	    pos = cache->evictionPosition;
457	    DBG_GLYPH_CACHE(("  evicting glyph at %d\n", pos));
458	    if (buffer->count) {
459		int x, y;
460		int i;
461
462		x = CACHE_X(pos);
463		y = CACHE_Y(pos);
464
465		for (i = 0; i < buffer->count; i++) {
466		    if (buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y) {
467			DBG_GLYPH_CACHE(("  must flush buffer\n"));
468			return ExaGlyphNeedFlush;
469		    }
470		}
471	    }
472
473	    /* OK, we're all set, swap in the new glyph */
474	    exaGlyphCacheHashRemove(cache, pos);
475	    exaGlyphCacheHashInsert(cache, pGlyph, pos);
476
477	    /* And pick a new eviction position */
478	    cache->evictionPosition = rand() % cache->size;
479	}
480
481	/* Now actually upload the glyph into the cache picture; if
482	 * we can't do it with UploadToScreen (because the glyph is
483	 * offscreen, etc), we fall back to CompositePicture.
484	 */
485	if (!exaGlyphCacheUploadGlyph(pScreen, cache, pos, pGlyph)) {
486	    CompositePicture (PictOpSrc,
487			      GlyphPicture(pGlyph)[pScreen->myNum],
488			      None,
489			      cache->picture,
490			      0, 0,
491			      0, 0,
492			      CACHE_X(pos),
493			      CACHE_Y(pos),
494			      pGlyph->info.width,
495			      pGlyph->info.height);
496	}
497
498    }
499
500    buffer->source = cache->picture;
501
502    rect = &buffer->rects[buffer->count];
503    rect->xSrc = CACHE_X(pos);
504    rect->ySrc = CACHE_Y(pos);
505    rect->xDst = xGlyph - pGlyph->info.x;
506    rect->yDst = yGlyph - pGlyph->info.y;
507    rect->width = pGlyph->info.width;
508    rect->height = pGlyph->info.height;
509
510    buffer->count++;
511
512    return ExaGlyphSuccess;
513}
514
515#undef CACHE_X
516#undef CACHE_Y
517
518static ExaGlyphCacheResult
519exaBufferGlyph(ScreenPtr         pScreen,
520	       ExaGlyphBufferPtr buffer,
521	       GlyphPtr          pGlyph,
522	       int               xGlyph,
523	       int               yGlyph)
524{
525    ExaScreenPriv(pScreen);
526    unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format;
527    int width = pGlyph->info.width;
528    int height = pGlyph->info.height;
529    ExaCompositeRectPtr rect;
530    PicturePtr source;
531    int i;
532
533    if (buffer->count == GLYPH_BUFFER_SIZE)
534	return ExaGlyphNeedFlush;
535
536    if (PICT_FORMAT_BPP(format) == 1)
537	format = PICT_a8;
538
539    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
540	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
541
542	if (format == cache->format &&
543	    width <= cache->glyphWidth &&
544	    height <= cache->glyphHeight) {
545	    ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen, &pExaScr->glyphCaches[i],
546								  buffer,
547								  pGlyph, xGlyph, yGlyph);
548	    switch (result) {
549	    case ExaGlyphFail:
550		break;
551	    case ExaGlyphSuccess:
552	    case ExaGlyphNeedFlush:
553		return result;
554	    }
555	}
556    }
557
558    /* Couldn't find the glyph in the cache, use the glyph picture directly */
559
560    source = GlyphPicture(pGlyph)[pScreen->myNum];
561    if (buffer->source && buffer->source != source)
562	return ExaGlyphNeedFlush;
563
564    buffer->source = source;
565
566    rect = &buffer->rects[buffer->count];
567    rect->xSrc = 0;
568    rect->ySrc = 0;
569    rect->xDst = xGlyph - pGlyph->info.x;
570    rect->yDst = yGlyph - pGlyph->info.y;
571    rect->width = pGlyph->info.width;
572    rect->height = pGlyph->info.height;
573
574    buffer->count++;
575
576    return ExaGlyphSuccess;
577}
578
579static void
580exaGlyphsToMask(PicturePtr        pMask,
581		ExaGlyphBufferPtr buffer)
582{
583    exaCompositeRects(PictOpAdd, buffer->source, pMask,
584		      buffer->count, buffer->rects);
585
586    buffer->count = 0;
587    buffer->source = NULL;
588}
589
590static void
591exaGlyphsToDst(CARD8		 op,
592	       PicturePtr	 pSrc,
593	       PicturePtr	 pDst,
594	       ExaGlyphBufferPtr buffer,
595	       INT16		 xSrc,
596	       INT16		 ySrc,
597	       INT16		 xDst,
598	       INT16		 yDst)
599{
600    int i;
601
602    for (i = 0; i < buffer->count; i++) {
603	ExaCompositeRectPtr rect = &buffer->rects[i];
604
605	CompositePicture (op,
606			  pSrc,
607			  buffer->source,
608			  pDst,
609			  xSrc + rect->xDst - xDst,
610			  ySrc + rect->yDst - yDst,
611			  rect->xSrc,
612			  rect->ySrc,
613			  rect->xDst,
614			  rect->yDst,
615			  rect->width,
616			  rect->height);
617    }
618
619    buffer->count = 0;
620    buffer->source = NULL;
621}
622
623/* Cut and paste from render/glyph.c - probably should export it instead */
624static void
625GlyphExtents (int		nlist,
626	      GlyphListPtr	list,
627	      GlyphPtr	       *glyphs,
628	      BoxPtr		extents)
629{
630    int		x1, x2, y1, y2;
631    int		n;
632    GlyphPtr	glyph;
633    int		x, y;
634
635    x = 0;
636    y = 0;
637    extents->x1 = MAXSHORT;
638    extents->x2 = MINSHORT;
639    extents->y1 = MAXSHORT;
640    extents->y2 = MINSHORT;
641    while (nlist--)
642    {
643	x += list->xOff;
644	y += list->yOff;
645	n = list->len;
646	list++;
647	while (n--)
648	{
649	    glyph = *glyphs++;
650	    x1 = x - glyph->info.x;
651	    if (x1 < MINSHORT)
652		x1 = MINSHORT;
653	    y1 = y - glyph->info.y;
654	    if (y1 < MINSHORT)
655		y1 = MINSHORT;
656	    x2 = x1 + glyph->info.width;
657	    if (x2 > MAXSHORT)
658		x2 = MAXSHORT;
659	    y2 = y1 + glyph->info.height;
660	    if (y2 > MAXSHORT)
661		y2 = MAXSHORT;
662	    if (x1 < extents->x1)
663		extents->x1 = x1;
664	    if (x2 > extents->x2)
665		extents->x2 = x2;
666	    if (y1 < extents->y1)
667		extents->y1 = y1;
668	    if (y2 > extents->y2)
669		extents->y2 = y2;
670	    x += glyph->info.xOff;
671	    y += glyph->info.yOff;
672	}
673    }
674}
675
676/**
677 * Returns TRUE if the glyphs in the lists intersect.  Only checks based on
678 * bounding box, which appears to be good enough to catch most cases at least.
679 */
680static Bool
681exaGlyphsIntersect(int nlist, GlyphListPtr list, GlyphPtr *glyphs)
682{
683    int x1, x2, y1, y2;
684    int n;
685    GlyphPtr glyph;
686    int x, y;
687    BoxRec extents;
688    Bool first = TRUE;
689
690    x = 0;
691    y = 0;
692    while (nlist--) {
693       x += list->xOff;
694       y += list->yOff;
695       n = list->len;
696       list++;
697       while (n--) {
698           glyph = *glyphs++;
699
700           if (glyph->info.width == 0 || glyph->info.height == 0) {
701               x += glyph->info.xOff;
702               y += glyph->info.yOff;
703               continue;
704           }
705
706           x1 = x - glyph->info.x;
707           if (x1 < MINSHORT)
708               x1 = MINSHORT;
709           y1 = y - glyph->info.y;
710           if (y1 < MINSHORT)
711               y1 = MINSHORT;
712           x2 = x1 + glyph->info.width;
713           if (x2 > MAXSHORT)
714               x2 = MAXSHORT;
715           y2 = y1 + glyph->info.height;
716           if (y2 > MAXSHORT)
717               y2 = MAXSHORT;
718
719           if (first) {
720               extents.x1 = x1;
721               extents.y1 = y1;
722               extents.x2 = x2;
723               extents.y2 = y2;
724               first = FALSE;
725           } else {
726               if (x1 < extents.x2 && x2 > extents.x1 &&
727                   y1 < extents.y2 && y2 > extents.y1)
728               {
729                   return TRUE;
730               }
731
732               if (x1 < extents.x1)
733                  extents.x1 = x1;
734               if (x2 > extents.x2)
735                   extents.x2 = x2;
736               if (y1 < extents.y1)
737                   extents.y1 = y1;
738               if (y2 > extents.y2)
739                   extents.y2 = y2;
740           }
741           x += glyph->info.xOff;
742           y += glyph->info.yOff;
743       }
744    }
745
746    return FALSE;
747}
748
749void
750exaGlyphs (CARD8 	 op,
751	   PicturePtr	 pSrc,
752	   PicturePtr	 pDst,
753	   PictFormatPtr maskFormat,
754	   INT16	 xSrc,
755	   INT16	 ySrc,
756	   int		 nlist,
757	   GlyphListPtr	 list,
758	   GlyphPtr	*glyphs)
759{
760    PicturePtr	pPicture;
761    PixmapPtr   pMaskPixmap = 0;
762    PicturePtr  pMask;
763    ScreenPtr   pScreen = pDst->pDrawable->pScreen;
764    int		width = 0, height = 0;
765    int		x, y;
766    int		xDst = list->xOff, yDst = list->yOff;
767    int		n;
768    GlyphPtr	glyph;
769    int		error;
770    BoxRec	extents = {0, 0, 0, 0};
771    CARD32	component_alpha;
772    ExaGlyphBuffer buffer;
773
774    /* If we don't have a mask format but all the glyphs have the same format
775     * and don't intersect, use the glyph format as mask format for the full
776     * benefits of the glyph cache.
777     */
778    if (!maskFormat) {
779       Bool sameFormat = TRUE;
780       int i;
781
782       maskFormat = list[0].format;
783
784       for (i = 0; i < nlist; i++) {
785           if (maskFormat->format != list[i].format->format) {
786               sameFormat = FALSE;
787               break;
788           }
789       }
790
791       if (!sameFormat || (maskFormat->depth != 1 &&
792			   exaGlyphsIntersect(nlist, list, glyphs))) {
793	   maskFormat = NULL;
794       }
795    }
796
797    if (maskFormat)
798    {
799	GCPtr	    pGC;
800	xRectangle  rect;
801
802	GlyphExtents (nlist, list, glyphs, &extents);
803
804	if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1)
805	    return;
806	width = extents.x2 - extents.x1;
807	height = extents.y2 - extents.y1;
808
809	if (maskFormat->depth == 1) {
810	    PictFormatPtr a8Format = PictureMatchFormat (pScreen, 8, PICT_a8);
811
812	    if (a8Format)
813		maskFormat = a8Format;
814	}
815
816	pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height,
817						maskFormat->depth,
818						CREATE_PIXMAP_USAGE_SCRATCH);
819	if (!pMaskPixmap)
820	    return;
821	component_alpha = NeedsComponent(maskFormat->format);
822	pMask = CreatePicture (0, &pMaskPixmap->drawable,
823			       maskFormat, CPComponentAlpha, &component_alpha,
824			       serverClient, &error);
825	if (!pMask)
826	{
827	    (*pScreen->DestroyPixmap) (pMaskPixmap);
828	    return;
829	}
830	pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen);
831	ValidateGC (&pMaskPixmap->drawable, pGC);
832	rect.x = 0;
833	rect.y = 0;
834	rect.width = width;
835	rect.height = height;
836	(*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect);
837	FreeScratchGC (pGC);
838	x = -extents.x1;
839	y = -extents.y1;
840    }
841    else
842    {
843	pMask = pDst;
844	x = 0;
845	y = 0;
846    }
847    buffer.count = 0;
848    buffer.source = NULL;
849    while (nlist--)
850    {
851	x += list->xOff;
852	y += list->yOff;
853	n = list->len;
854	while (n--)
855	{
856	    glyph = *glyphs++;
857	    pPicture = GlyphPicture (glyph)[pScreen->myNum];
858
859	    if (glyph->info.width > 0 && glyph->info.height > 0 &&
860		exaBufferGlyph(pScreen, &buffer, glyph, x, y) == ExaGlyphNeedFlush)
861	    {
862		if (maskFormat)
863		    exaGlyphsToMask(pMask, &buffer);
864		else
865		    exaGlyphsToDst(op, pSrc, pDst, &buffer,
866				   xSrc, ySrc, xDst, yDst);
867
868		exaBufferGlyph(pScreen, &buffer, glyph, x, y);
869	    }
870
871	    x += glyph->info.xOff;
872	    y += glyph->info.yOff;
873	}
874	list++;
875    }
876
877    if (buffer.count) {
878        if (maskFormat)
879	    exaGlyphsToMask(pMask, &buffer);
880        else
881	    exaGlyphsToDst(op, pSrc, pDst, &buffer,
882		           xSrc, ySrc, xDst, yDst);
883    }
884
885    if (maskFormat)
886    {
887	x = extents.x1;
888	y = extents.y1;
889	CompositePicture (op,
890			  pSrc,
891			  pMask,
892			  pDst,
893			  xSrc + x - xDst,
894			  ySrc + y - yDst,
895			  0, 0,
896			  x, y,
897			  width, height);
898	FreePicture ((pointer) pMask, (XID) 0);
899	(*pScreen->DestroyPixmap) (pMaskPixmap);
900    }
901}
902