Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | ||
70 | typedef struct { | |
71 | PicturePtr mask; | |
72 | ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE]; | |
73 | int count; | |
74 | } ExaGlyphBuffer, *ExaGlyphBufferPtr; | |
75 | ||
76 | typedef 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 | ||
82 | void | |
83 | exaGlyphsInit(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 | ||
117 | static void | |
118 | exaUnrealizeGlyphCaches(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((pointer) 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 | */ | |
154 | static Bool | |
155 | exaRealizeGlyphCaches(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 = malloc(sizeof(int) * cache->hashSize); | |
215 | cache->glyphs = malloc(sizeof(ExaCachedGlyphRec) * cache->size); | |
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((pointer) pPicture, (XID) 0); | |
229 | return TRUE; | |
230 | ||
231 | bail: | |
232 | exaUnrealizeGlyphCaches(pScreen, format); | |
233 | return FALSE; | |
234 | } | |
235 | ||
236 | void | |
237 | exaGlyphsFini(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 | ||
250 | static int | |
251 | exaGlyphCacheHashLookup(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 | ||
275 | static void | |
276 | exaGlyphCacheHashInsert(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 | ||
296 | static void | |
297 | exaGlyphCacheHashRemove(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 | */ | |
363 | static void | |
364 | exaGlyphCacheUploadGlyph(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 | ||
425 | static ExaGlyphCacheResult | |
426 | exaGlyphCacheBufferGlyph(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 | ||
537 | static ExaGlyphCacheResult | |
538 | exaBufferGlyph(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 | ||
610 | static void | |
611 | exaGlyphsToMask(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 | ||
620 | static void | |
621 | exaGlyphsToDst(PicturePtr pSrc, PicturePtr pDst, ExaGlyphBufferPtr buffer) | |
622 | { | |
623 | exaCompositeRects(PictOpOver, 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 */ | |
631 | static void | |
632 | GlyphExtents(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 | ||
678 | void | |
679 | exaGlyphs(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((pointer) 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(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(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((pointer) pMask, (XID) 0); | |
837 | (*pScreen->DestroyPixmap) (pMaskPixmap); | |
838 | } | |
839 | } |