Various cosmetic changes.
[wine] / dlls / x11drv / xrender.c
1 /*
2  * Functions to use the XRender extension
3  *
4  * Copyright 2001 Huw D M Davies for CodeWeavers
5  */
6 #include "config.h"
7
8 #include "winnt.h"
9 #include "x11drv.h"
10 #include "bitmap.h"
11 #include "debugtools.h"
12 #include "region.h"
13 #include <string.h>
14 #include <stdlib.h>
15 #include "wine/unicode.h"
16 #include <assert.h>
17
18 BOOL X11DRV_XRender_Installed = FALSE;
19 DEFAULT_DEBUG_CHANNEL(xrender);
20
21 #ifdef HAVE_LIBXRENDER
22
23 #include "ts_xlib.h"
24 #include "ts_xrender.h"
25
26 static XRenderPictFormat *screen_format; /* format of screen */
27 static XRenderPictFormat *mono_format; /* format of mono bitmap */
28
29 typedef struct
30 {
31   LOGFONTW lf;
32   XFORM  xform; /* this is dum as we don't care about offsets */
33   DWORD hash;
34 } LFANDSIZE;
35
36 #define INITIAL_REALIZED_BUF_SIZE 128
37
38
39 typedef struct
40 {
41   LFANDSIZE lfsz;
42   GlyphSet glyphset;
43   XRenderPictFormat *font_format;
44   int nrealized;
45   BOOL *realized;
46   UINT count;
47   INT next;
48 } gsCacheEntry;
49
50 struct tagXRENDERINFO
51 {
52     gsCacheEntry       *cacheEntry;
53     Picture            pict;
54     Picture            tile_pict;
55     Pixmap             tile_xpm;
56     COLORREF           lastTextColor;
57 };
58
59
60 static gsCacheEntry *glyphsetCache = NULL;
61 static DWORD glyphsetCacheSize = 0;
62 static INT lastfree = -1;
63 static INT mru = -1;
64
65 #define INIT_CACHE_SIZE 10
66
67 static int antialias = 1;
68
69 /***********************************************************************
70  *   X11DRV_XRender_Init
71  *
72  * Let's see if our XServer has the extension available
73  *
74  */
75 void X11DRV_XRender_Init(void)
76 {
77     int error_base, event_base, i;
78     XRenderPictFormat pf;
79
80     if(TSXRenderQueryExtension(gdi_display, &event_base, &error_base)) {
81         X11DRV_XRender_Installed = TRUE;
82         TRACE("Xrender is up and running error_base = %d\n", error_base);
83         screen_format = TSXRenderFindVisualFormat(gdi_display, visual);
84         pf.type = PictTypeDirect;
85         pf.depth = 1;
86         pf.direct.alpha = 0;
87         pf.direct.alphaMask = 1;
88         mono_format = TSXRenderFindFormat(gdi_display, PictFormatType |
89                                           PictFormatDepth | PictFormatAlpha |
90                                           PictFormatAlphaMask, &pf, 0);
91
92         glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
93                                   sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
94
95         glyphsetCacheSize = INIT_CACHE_SIZE;
96         lastfree = 0;
97         for(i = 0; i < INIT_CACHE_SIZE; i++) {
98           glyphsetCache[i].next = i + 1;
99           glyphsetCache[i].count = -1;
100         }
101         glyphsetCache[i-1].next = -1;
102     } else {
103         TRACE("Xrender is not available on this server\n");
104     }
105     return;
106 }
107
108 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
109 {
110   if(p1->hash != p2->hash) return TRUE;
111   if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
112   if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
113   return strcmpW(p1->lf.lfFaceName, p2->lf.lfFaceName);
114 }
115
116 static void walk_cache(void)
117 {
118   int i;
119
120   for(i=mru; i >= 0; i = glyphsetCache[i].next)
121     TRACE("item %d\n", i);
122 }
123
124 static gsCacheEntry *LookupEntry(LFANDSIZE *plfsz)
125 {
126   int i, prev_i = -1;
127
128   for(i = mru; i >= 0; i = glyphsetCache[i].next) {
129     TRACE("%d\n", i);
130     if(glyphsetCache[i].count == -1) { /* reached free list so stop */
131       i = -1;
132       break;
133     }
134
135     if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
136       glyphsetCache[i].count++;
137       if(prev_i >= 0) {
138         glyphsetCache[prev_i].next = glyphsetCache[i].next;
139         glyphsetCache[i].next = mru;
140         mru = i;
141       }
142       TRACE("found font in cache %d\n", i);
143       return glyphsetCache + i;
144     }
145     prev_i = i;
146   }
147   TRACE("font not in cache\n");
148   return NULL;
149 }
150
151 static gsCacheEntry *AllocEntry(void)
152 {
153   int best = -1, prev_best = -1, i, prev_i = -1;
154
155   if(lastfree >= 0) {
156     assert(glyphsetCache[lastfree].count == -1);
157     glyphsetCache[lastfree].count = 1;
158     best = lastfree;
159     lastfree = glyphsetCache[lastfree].next;    
160     assert(best != mru);
161     glyphsetCache[best].next = mru;
162     mru = best;
163
164     TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
165     return glyphsetCache + mru;
166   }
167
168   for(i = mru; i >= 0; i = glyphsetCache[i].next) {
169     if(glyphsetCache[i].count == 0) {
170       best = i;
171       prev_best = prev_i;
172     }
173     prev_i = i;
174   }
175
176   if(best >= 0) {
177     TRACE("freeing unused glyphset at cache %d\n", best);
178     TSXRenderFreeGlyphSet(gdi_display, glyphsetCache[best].glyphset);
179     glyphsetCache[best].glyphset = 0;
180     if(glyphsetCache[best].nrealized) { /* do we really want to do this? */
181       HeapFree(GetProcessHeap(), 0, glyphsetCache[best].realized);
182       glyphsetCache[best].realized = NULL;
183       glyphsetCache[best].nrealized = 0;
184     }
185     glyphsetCache[best].count = 1;
186     if(prev_best >= 0) {
187       glyphsetCache[prev_best].next = glyphsetCache[best].next;
188       glyphsetCache[best].next = mru;
189       mru = best;
190     } else {
191       assert(mru == best);
192     }
193     return glyphsetCache + mru;
194   }
195
196   TRACE("Growing cache\n");
197   glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
198                               glyphsetCache,
199                               (glyphsetCacheSize + INIT_CACHE_SIZE)
200                               * sizeof(*glyphsetCache));
201   for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
202       i++) {
203     glyphsetCache[i].next = i + 1;
204     glyphsetCache[i].count = -1;
205   }
206   glyphsetCache[i-1].next = -1;
207   glyphsetCacheSize += INIT_CACHE_SIZE;
208
209   lastfree = glyphsetCache[best].next;
210   glyphsetCache[best].count = 1;
211   glyphsetCache[best].next = mru;
212   mru = best;
213   TRACE("new free cache slot at %d\n", mru);
214   return glyphsetCache + mru;
215 }    
216
217 static gsCacheEntry *GetCacheEntry(LFANDSIZE *plfsz)
218 {
219   XRenderPictFormat pf;
220   gsCacheEntry *ret;
221
222   if((ret = LookupEntry(plfsz)) != NULL) return ret;
223
224   ret = AllocEntry();
225   ret->lfsz = *plfsz;
226   assert(ret->nrealized == 0);
227
228
229   if(antialias && abs(plfsz->lf.lfHeight) > 16) {
230     pf.depth = 8;
231     pf.direct.alphaMask = 0xff;
232   } else {
233     pf.depth = 1;
234     pf.direct.alphaMask = 1;
235   }
236   pf.type = PictTypeDirect;
237   pf.direct.alpha = 0;
238
239   ret->font_format = TSXRenderFindFormat(gdi_display,
240                                          PictFormatType |
241                                          PictFormatDepth |
242                                          PictFormatAlpha |
243                                          PictFormatAlphaMask,
244                                          &pf, 0);
245
246   ret->glyphset = TSXRenderCreateGlyphSet(gdi_display, ret->font_format);
247   return ret;
248 }
249
250 static void dec_ref_cache(gsCacheEntry *entry)
251 {
252   TRACE("dec'ing entry %d to %d\n", entry - glyphsetCache, entry->count - 1);
253   assert(entry->count > 0);
254   entry->count--;
255 }
256
257 static void lfsz_calc_hash(LFANDSIZE *plfsz)
258 {
259   DWORD hash = 0, *ptr;
260   int i;
261
262   for(ptr = (DWORD*)&plfsz->xform; ptr < (DWORD*)(&plfsz->xform + 1); ptr++)
263     hash ^= *ptr;
264   for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
265     hash ^= *ptr;
266   for(i = 0, ptr = (DWORD*)&plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
267     WCHAR *pwc = (WCHAR *)ptr;
268     if(!*pwc) break;
269     hash ^= *ptr;
270     pwc++;
271     if(!*pwc) break;
272   }
273   plfsz->hash = hash;
274   return;
275 }
276
277 /***********************************************************************
278  *   X11DRV_XRender_Finalize
279  */
280 void X11DRV_XRender_Finalize(void)
281 {
282     FIXME("Free cached glyphsets\n");
283     return;
284 }
285
286
287 /***********************************************************************
288  *   X11DRV_XRender_SelectFont
289  */
290 BOOL X11DRV_XRender_SelectFont(DC *dc, HFONT hfont)
291 {
292     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
293     LFANDSIZE lfsz;
294
295     GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
296     TRACE("h=%ld w=%ld weight=%ld it=%d charset=%d name=%s\n",
297           lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
298           lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
299     lfsz.xform = dc->xformWorld2Vport;
300     lfsz_calc_hash(&lfsz);
301
302     if(!physDev->xrender)
303         physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
304                                      sizeof(*physDev->xrender));
305
306     else if(physDev->xrender->cacheEntry)
307         dec_ref_cache(physDev->xrender->cacheEntry);
308     physDev->xrender->cacheEntry = GetCacheEntry(&lfsz);
309     return 0;
310 }
311
312 /***********************************************************************
313  *   X11DRV_XRender_DeleteDC
314  */
315 void X11DRV_XRender_DeleteDC(DC *dc)
316 {
317     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
318     
319     if(physDev->xrender->tile_pict)
320         TSXRenderFreePicture(gdi_display, physDev->xrender->tile_pict);
321
322     if(physDev->xrender->tile_xpm)
323         TSXFreePixmap(gdi_display, physDev->xrender->tile_xpm);
324
325     if(physDev->xrender->pict) {
326         TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, dc);
327         TSXRenderFreePicture(gdi_display, physDev->xrender->pict);
328     }
329
330     if(physDev->xrender->cacheEntry)
331         dec_ref_cache(physDev->xrender->cacheEntry);
332     
333     HeapFree(GetProcessHeap(), 0, physDev->xrender);
334     physDev->xrender = NULL;
335     return;
336 }
337
338 /***********************************************************************
339  *   X11DRV_XRender_UpdateDrawable
340  *
341  * This gets called from X11DRV_SetDrawable and deletes the pict when the
342  * drawable changes.  However at the moment we delete the pict at the end of
343  * every ExtTextOut so this is basically a NOP.
344  */
345 void X11DRV_XRender_UpdateDrawable(DC *dc)
346 {
347     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
348
349     if(physDev->xrender->pict) {
350         TRACE("freeing pict %08lx from dc %p\n", physDev->xrender->pict, dc);
351         TSXRenderFreePicture(gdi_display, physDev->xrender->pict);
352     }
353     physDev->xrender->pict = 0;
354     return;
355 }
356
357 static BOOL UploadGlyph(DC *dc, WCHAR glyph)
358 {
359     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
360     int buflen;
361     char *buf;
362     Glyph gid;
363     GLYPHMETRICS gm;
364     XGlyphInfo gi;
365     gsCacheEntry *entry = physDev->xrender->cacheEntry;
366     UINT ggo_format;
367     BOOL aa;
368
369     if(entry->nrealized <= glyph) {
370         entry->nrealized = (glyph / 128 + 1) * 128;
371         entry->realized = HeapReAlloc(GetProcessHeap(),
372                                       HEAP_ZERO_MEMORY,
373                                       entry->realized,
374                                       entry->nrealized * sizeof(BOOL));
375     }
376     entry->realized[glyph] = TRUE;
377
378     if(entry->font_format->depth == 8) {
379         aa = TRUE;
380         ggo_format = WINE_GGO_GRAY16_BITMAP;
381     } else {
382         aa = FALSE;
383         ggo_format = GGO_BITMAP;
384     }
385
386     buflen = GetGlyphOutlineW(dc->hSelf, glyph, ggo_format, &gm, 0, NULL,
387                               NULL);
388     if(buflen == GDI_ERROR)
389         return FALSE;
390
391     buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
392     GetGlyphOutlineW(dc->hSelf, glyph, ggo_format, &gm, buflen, buf, NULL);
393
394     TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%ld,%ld\n",
395           buflen,
396           gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
397           gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
398
399     gi.width = gm.gmBlackBoxX;
400     gi.height = gm.gmBlackBoxY;
401     gi.x = -gm.gmptGlyphOrigin.x;
402     gi.y = gm.gmptGlyphOrigin.y;
403     gi.xOff = gm.gmCellIncX;
404     gi.yOff = gm.gmCellIncY;
405
406     if(TRACE_ON(xrender)) {
407         int pitch, i, j;
408         char output[300];
409         unsigned char *line;
410
411         if(!aa) {
412             pitch = ((gi.width + 31) / 32) * 4;
413             for(i = 0; i < gi.height; i++) {
414                 line = buf + i * pitch;
415                 output[0] = '\0';
416                 for(j = 0; j < pitch * 8; j++) {
417                     strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
418                 }
419                 strcat(output, "\n");
420                 TRACE(output);
421             }
422         } else {
423             char blks[] = " .:;!o*#";
424             char str[2];
425
426             str[1] = '\0';
427             pitch = ((gi.width + 3) / 4) * 4;
428             for(i = 0; i < gi.height; i++) {
429                 line = buf + i * pitch;
430                 output[0] = '\0';
431                 for(j = 0; j < pitch; j++) {
432                     str[0] = blks[line[j] >> 5];
433                     strcat(output, str);
434                 }
435                 strcat(output, "\n");
436                 TRACE(output);
437             }
438         }
439     }
440
441     if(!aa && BitmapBitOrder(gdi_display) != MSBFirst) {
442         unsigned char *byte = buf, c;
443         int i = buflen;
444
445         while(i--) {
446             c = *byte;
447
448             /* magic to flip bit order */
449             c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
450             c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
451             c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
452
453             *byte++ = c;
454         }
455     }
456     gid = glyph;
457     TSXRenderAddGlyphs(gdi_display, entry->glyphset, &gid, &gi, 1,
458                        buf, buflen);
459     HeapFree(GetProcessHeap(), 0, buf);
460     return TRUE;
461 }
462
463 /***********************************************************************
464  *   X11DRV_XRender_ExtTextOut
465  */
466 BOOL X11DRV_XRender_ExtTextOut( DC *dc, INT x, INT y, UINT flags,
467                                 const RECT *lprect, LPCWSTR wstr, UINT count,
468                                 const INT *lpDx )
469 {
470     XRenderColor col;
471     int idx;
472     TEXTMETRICW tm;
473     X11DRV_PDEVICE *physDev = (X11DRV_PDEVICE *)dc->physDev;
474     RGNOBJ *obj;
475     XRectangle *pXrect;
476     SIZE sz;
477     RECT rc;
478     BOOL done_extents = FALSE;
479     INT width, xwidth, ywidth;
480     double cosEsc, sinEsc;
481     XGCValues xgcval;
482     LOGFONTW lf;
483     int render_op = PictOpOver;
484
485     TRACE("%04x, %d, %d, %08x, %p, %s, %d, %p)\n", dc->hSelf, x, y, flags,
486           lprect, debugstr_wn(wstr, count), count, lpDx);
487     if(lprect)
488       TRACE("rect: %d,%d - %d,%d\n", lprect->left, lprect->top, lprect->right,
489             lprect->bottom);
490     TRACE("align = %x bkmode = %x mapmode = %x\n", dc->textAlign,
491           dc->backgroundMode,
492           dc->MapMode);
493
494     if(dc->textAlign & TA_UPDATECP) {
495         x = dc->CursPosX;
496         y = dc->CursPosY;
497     }
498
499     GetObjectW(GetCurrentObject(dc->hSelf, OBJ_FONT), sizeof(lf), &lf);
500     if(lf.lfEscapement != 0) {
501         cosEsc = cos(lf.lfEscapement * M_PI / 1800);
502         sinEsc = sin(lf.lfEscapement * M_PI / 1800);
503     } else {
504         cosEsc = 1;
505         sinEsc = 0;
506     }
507
508     if(flags & (ETO_CLIPPED | ETO_OPAQUE)) {
509         if(!lprect) {
510             if(flags & ETO_CLIPPED) return FALSE;
511                 GetTextExtentPointW(dc->hSelf, wstr, count, &sz);
512                 done_extents = TRUE;
513                 rc.left = x;
514                 rc.top = y;
515                 rc.right = x + sz.cx;
516                 rc.bottom = y + sz.cy;
517         } else {
518             rc = *lprect;
519         }
520
521         rc.left = INTERNAL_XWPTODP(dc, rc.left, rc.top);
522         rc.top = INTERNAL_YWPTODP(dc, rc.left, rc.top);
523         rc.right = INTERNAL_XWPTODP(dc, rc.right, rc.bottom);
524         rc.bottom = INTERNAL_YWPTODP(dc, rc.right, rc.bottom);
525
526         if(rc.left > rc.right) {INT tmp = rc.left; rc.left = rc.right; rc.right = tmp;}
527         if(rc.top > rc.bottom) {INT tmp = rc.top; rc.top = rc.bottom; rc.bottom = tmp;}
528     }
529
530     xgcval.function = GXcopy;
531     xgcval.background = physDev->backgroundPixel;
532     xgcval.fill_style = FillSolid;
533     TSXChangeGC( gdi_display, physDev->gc,
534                  GCFunction | GCBackground | GCFillStyle, &xgcval );
535
536     X11DRV_LockDIBSection( dc, DIB_Status_GdiMod, FALSE );
537
538     if(flags & ETO_OPAQUE) {
539         TSXSetForeground( gdi_display, physDev->gc, physDev->backgroundPixel );
540         TSXFillRectangle( gdi_display, physDev->drawable, physDev->gc,
541                           dc->DCOrgX + rc.left, dc->DCOrgY + rc.top,
542                           rc.right - rc.left, rc.bottom - rc.top );
543     }
544
545     if(count == 0) {
546         X11DRV_UnlockDIBSection( dc, TRUE );
547         return TRUE;
548     }
549
550     x = INTERNAL_XWPTODP( dc, x, y );
551     y = INTERNAL_YWPTODP( dc, x, y );
552
553     TRACE("real x,y %d,%d\n", x, y);
554
555     if(lpDx) {
556         width = 0;
557         for(idx = 0; idx < count; idx++)
558             width += lpDx[idx];
559     } else {
560         if(!done_extents) {
561             GetTextExtentPointW(dc->hSelf, wstr, count, &sz);
562             done_extents = TRUE;
563         }
564         width = sz.cx;
565     }
566     width = INTERNAL_XWSTODS(dc, width);
567     xwidth = width * cosEsc;
568     ywidth = width * sinEsc;
569
570     GetTextMetricsW(dc->hSelf, &tm);
571
572     switch( dc->textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) ) {
573     case TA_LEFT:
574         if (dc->textAlign & TA_UPDATECP) {
575             dc->CursPosX = INTERNAL_XDPTOWP( dc, x + xwidth, y - ywidth );
576             dc->CursPosY = INTERNAL_YDPTOWP( dc, x + xwidth, y - ywidth );
577         }
578         break;
579       
580     case TA_CENTER:
581         x -= xwidth / 2;
582         y += ywidth / 2;
583         break;
584
585     case TA_RIGHT:
586         x -= xwidth;
587         y += ywidth;
588         if (dc->textAlign & TA_UPDATECP) {
589             dc->CursPosX = INTERNAL_XDPTOWP( dc, x + xwidth, y - ywidth );
590             dc->CursPosY = INTERNAL_YDPTOWP( dc, x + xwidth, y - ywidth );
591         }
592         break;
593     }
594
595     switch( dc->textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) ) {
596     case TA_TOP:
597         y += tm.tmAscent * cosEsc;
598         x += tm.tmAscent * sinEsc;
599         break;
600
601     case TA_BOTTOM:
602         y -= tm.tmDescent * cosEsc;
603         x -= tm.tmDescent * sinEsc;
604         break;
605
606     case TA_BASELINE:
607         break;
608     }
609
610     if (flags & ETO_CLIPPED)
611     {
612         SaveVisRgn16( dc->hSelf );
613         CLIPPING_IntersectVisRect( dc, rc.left, rc.top, rc.right,
614                                    rc.bottom, FALSE );
615     }
616
617
618
619     if(!physDev->xrender->pict) {
620         XRenderPictureAttributes pa;
621         pa.subwindow_mode = IncludeInferiors;
622
623         physDev->xrender->pict =
624           TSXRenderCreatePicture(gdi_display,
625                                  physDev->drawable,
626                                  (dc->bitsPerPixel == 1) ?
627                                    mono_format : screen_format,
628                                  CPSubwindowMode, &pa);
629
630         TRACE("allocing pict = %lx dc = %p drawable = %08lx\n", physDev->xrender->pict, dc, physDev->drawable);
631     } else {
632         TRACE("using existing pict = %lx dc = %p\n", physDev->xrender->pict, dc);
633     }
634
635     obj = (RGNOBJ *) GDI_GetObjPtr(dc->hGCClipRgn, REGION_MAGIC);
636     if (!obj)
637     {
638         ERR("Rgn is 0. Please report this.\n");
639         return FALSE;
640     }
641     
642     if (obj->rgn->numRects > 0)
643     {
644         XRectangle *pXr;
645         RECT *pRect = obj->rgn->rects;
646         RECT *pEndRect = obj->rgn->rects + obj->rgn->numRects;
647
648         pXrect = HeapAlloc( GetProcessHeap(), 0, 
649                             sizeof(*pXrect) * obj->rgn->numRects );
650         if(!pXrect)
651         {
652             WARN("Can't alloc buffer\n");
653             GDI_ReleaseObj( dc->hGCClipRgn );
654             return FALSE;
655         }
656
657         for(pXr = pXrect; pRect < pEndRect; pRect++, pXr++)
658         {
659             pXr->x = pRect->left;
660             pXr->y = pRect->top;
661             pXr->width = pRect->right - pRect->left;
662             pXr->height = pRect->bottom - pRect->top;
663             TRACE("Adding clip rect %d,%d - %d,%d\n", pRect->left, pRect->top,
664                   pRect->right, pRect->bottom);
665         }
666     }
667     else {
668         TRACE("no clip rgn\n");
669         pXrect = NULL;
670     }
671
672     TSXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
673                                        0, 0, pXrect, obj->rgn->numRects );
674
675     if(pXrect)
676         HeapFree( GetProcessHeap(), 0, pXrect );
677
678     GDI_ReleaseObj( dc->hGCClipRgn );
679
680     if(dc->backgroundMode != TRANSPARENT) {
681         if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE))) {
682             if(!(flags & ETO_OPAQUE) || x < rc.left || x + width >= rc.right ||
683                y - tm.tmAscent < rc.top || y + tm.tmDescent >= rc.bottom) {
684                 TSXSetForeground( gdi_display, physDev->gc, physDev->backgroundPixel );
685                 TSXFillRectangle( gdi_display, physDev->drawable, physDev->gc,
686                                   dc->DCOrgX + x, dc->DCOrgY + y - tm.tmAscent,
687                                   width, tm.tmAscent + tm.tmDescent );
688             }
689         }
690     }
691
692     /* Create a 1x1 pixmap to tile over the font mask */
693     if(!physDev->xrender->tile_xpm) {
694         XRenderPictureAttributes pa;
695
696         XRenderPictFormat *format = (dc->bitsPerPixel == 1) ? mono_format : screen_format;
697         physDev->xrender->tile_xpm = TSXCreatePixmap(gdi_display,
698                                                      physDev->drawable,
699                                                      1, 1,
700                                                      format->depth);
701         pa.repeat = True;
702         physDev->xrender->tile_pict = TSXRenderCreatePicture(gdi_display,
703                                                              physDev->xrender->tile_xpm,
704                                                              format,
705                                                              CPRepeat, &pa);
706         TRACE("Created pixmap of depth %d\n", format->depth);
707         /* init lastTextColor to something different from dc->textColor */
708         physDev->xrender->lastTextColor = ~dc->textColor;
709
710     }
711     
712     if(dc->textColor != physDev->xrender->lastTextColor) {
713         if(dc->bitsPerPixel != 1) {
714             /* Map 0 -- 0xff onto 0 -- 0xffff */
715             col.red = GetRValue(dc->textColor);
716             col.red |= col.red << 8;
717             col.green = GetGValue(dc->textColor);
718             col.green |= col.green << 8;
719             col.blue = GetBValue(dc->textColor);
720             col.blue |= col.blue << 8;
721             col.alpha = 0x0;
722         } else { /* for a 1bpp bitmap we always need a 1 in the tile */
723             col.red = col.green = col.blue = 0;
724             col.alpha = 0xffff;
725         }
726         TSXRenderFillRectangle(gdi_display, PictOpSrc,
727                                physDev->xrender->tile_pict,
728                                &col, 0, 0, 1, 1);
729         physDev->xrender->lastTextColor = dc->textColor;
730     }
731
732     /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
733      */
734     if((dc->bitsPerPixel == 1) && ((dc->textColor & 0xffffff) == 0))
735         render_op = PictOpOutReverse; /* This gives us 'black' text */
736     
737     for(idx = 0; idx < count; idx++) {
738         if(wstr[idx] >= physDev->xrender->cacheEntry->nrealized ||
739            physDev->xrender->cacheEntry->realized[wstr[idx]] == FALSE) {
740             UploadGlyph(dc, wstr[idx]);
741         }
742     }
743
744
745     TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count), dc->DCOrgX + x,
746           dc->DCOrgY + y);
747     if(!lpDx)
748         TSXRenderCompositeString16(gdi_display, render_op, 
749                                    physDev->xrender->tile_pict,
750                                    physDev->xrender->pict,
751                                    physDev->xrender->cacheEntry->font_format,
752                                    physDev->xrender->cacheEntry->glyphset,
753                                    0, 0, dc->DCOrgX + x, dc->DCOrgY + y, wstr,
754                                    count);
755
756     else {
757         INT offset = 0, xoff = 0, yoff = 0;
758         for(idx = 0; idx < count; idx++) {
759             TSXRenderCompositeString16(gdi_display, render_op, 
760                                        physDev->xrender->tile_pict,
761                                        physDev->xrender->pict,
762                                        physDev->xrender->cacheEntry->font_format,
763                                        physDev->xrender->cacheEntry->glyphset,
764                                        0, 0, dc->DCOrgX + x + xoff,
765                                        dc->DCOrgY + y + yoff,
766                                        wstr + idx, 1);
767             offset += INTERNAL_XWSTODS(dc, lpDx[idx]);
768             xoff = offset * cosEsc;
769             yoff = offset * sinEsc;
770         }
771     }
772
773     if(physDev->xrender->pict) {
774         TSXRenderFreePicture(gdi_display, physDev->xrender->pict);
775     }
776     physDev->xrender->pict = 0;
777
778
779     if (flags & ETO_CLIPPED) 
780         RestoreVisRgn16( dc->hSelf );
781
782     X11DRV_UnlockDIBSection( dc, TRUE );
783     return TRUE;
784 }
785
786 #else /* #ifdef HAVE_LIBXRENDER */
787
788 void X11DRV_XRender_Init(void)
789 {
790     TRACE("XRender support not compiled in.\n");
791     return;
792 }
793
794 void X11DRV_XRender_Finalize(void)
795 {
796   assert(0);
797   return;
798 }
799
800 BOOL X11DRV_XRender_SelectFont(DC *dc, HFONT hfont)
801 {
802   assert(0);
803   return FALSE;
804 }
805
806 void X11DRV_XRender_DeleteDC(DC *dc)
807 {
808   assert(0);
809   return;
810 }
811
812 BOOL X11DRV_XRender_ExtTextOut( DC *dc, INT x, INT y, UINT flags,
813                                 const RECT *lprect, LPCWSTR wstr, UINT count,
814                                 const INT *lpDx )
815 {
816   assert(0);
817   return FALSE;
818 }
819
820 void X11DRV_XRender_UpdateDrawable(DC *dc)
821 {
822   assert(0);
823   return;
824 }
825
826 #endif