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