comdlg32/tests: PrintDlgExW returns E_NOTIMPL on some versions of winME.
[wine] / dlls / winex11.drv / xrender.c
1 /*
2  * Functions to use the XRender extension
3  *
4  * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
5  *
6  * Some parts also:
7  * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <assert.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "x11drv.h"
34 #include "winternl.h"
35 #include "wine/library.h"
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
38
39 int using_client_side_fonts = FALSE;
40
41 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
42
43 #ifdef SONAME_LIBXRENDER
44
45 static BOOL X11DRV_XRender_Installed = FALSE;
46
47 #include <X11/Xlib.h>
48 #include <X11/extensions/Xrender.h>
49
50 #ifndef RepeatNone  /* added in 0.10 */
51 #define RepeatNone    0
52 #define RepeatNormal  1
53 #define RepeatPad     2
54 #define RepeatReflect 3
55 #endif
56
57 enum drawable_depth_type {mono_drawable, color_drawable};
58 static XRenderPictFormat *pict_formats[2];
59
60 typedef struct
61 {
62     LOGFONTW lf;
63     XFORM    xform;
64     SIZE     devsize;  /* size in device coords */
65     DWORD    hash;
66 } LFANDSIZE;
67
68 #define INITIAL_REALIZED_BUF_SIZE 128
69
70 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
71
72 typedef struct
73 {
74     GlyphSet glyphset;
75     XRenderPictFormat *font_format;
76     int nrealized;
77     BOOL *realized;
78     void **bitmaps;
79     XGlyphInfo *gis;
80 } gsCacheEntryFormat;
81
82 typedef struct
83 {
84     LFANDSIZE lfsz;
85     AA_Type aa_default;
86     gsCacheEntryFormat * format[AA_MAXVALUE];
87     INT count;
88     INT next;
89 } gsCacheEntry;
90
91 struct tagXRENDERINFO
92 {
93     int                cache_index;
94     Picture            pict;
95 };
96
97
98 static gsCacheEntry *glyphsetCache = NULL;
99 static DWORD glyphsetCacheSize = 0;
100 static INT lastfree = -1;
101 static INT mru = -1;
102
103 #define INIT_CACHE_SIZE 10
104
105 static int antialias = 1;
106
107 static void *xrender_handle;
108
109 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
110 MAKE_FUNCPTR(XRenderAddGlyphs)
111 MAKE_FUNCPTR(XRenderComposite)
112 MAKE_FUNCPTR(XRenderCompositeString8)
113 MAKE_FUNCPTR(XRenderCompositeString16)
114 MAKE_FUNCPTR(XRenderCompositeString32)
115 MAKE_FUNCPTR(XRenderCompositeText16)
116 MAKE_FUNCPTR(XRenderCreateGlyphSet)
117 MAKE_FUNCPTR(XRenderCreatePicture)
118 MAKE_FUNCPTR(XRenderFillRectangle)
119 MAKE_FUNCPTR(XRenderFindFormat)
120 MAKE_FUNCPTR(XRenderFindVisualFormat)
121 MAKE_FUNCPTR(XRenderFreeGlyphSet)
122 MAKE_FUNCPTR(XRenderFreePicture)
123 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
124 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
125 MAKE_FUNCPTR(XRenderSetPictureTransform)
126 #endif
127 MAKE_FUNCPTR(XRenderQueryExtension)
128 #undef MAKE_FUNCPTR
129
130 static CRITICAL_SECTION xrender_cs;
131 static CRITICAL_SECTION_DEBUG critsect_debug =
132 {
133     0, 0, &xrender_cs,
134     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
135       0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
136 };
137 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
138
139 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
140           ( ( (ULONG)_x4 << 24 ) |     \
141             ( (ULONG)_x3 << 16 ) |     \
142             ( (ULONG)_x2 <<  8 ) |     \
143               (ULONG)_x1         )
144
145 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
146
147 #define GASP_GRIDFIT 0x01
148 #define GASP_DOGRAY  0x02
149
150 #ifdef WORDS_BIGENDIAN
151 #define get_be_word(x) (x)
152 #define NATIVE_BYTE_ORDER MSBFirst
153 #else
154 #define get_be_word(x) RtlUshortByteSwap(x)
155 #define NATIVE_BYTE_ORDER LSBFirst
156 #endif
157
158 /***********************************************************************
159  *   X11DRV_XRender_Init
160  *
161  * Let's see if our XServer has the extension available
162  *
163  */
164 void X11DRV_XRender_Init(void)
165 {
166     int event_base, i;
167     XRenderPictFormat pf;
168
169     if (client_side_with_render &&
170         wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
171         wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) && 
172         (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
173     {
174
175 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
176 LOAD_FUNCPTR(XRenderAddGlyphs)
177 LOAD_FUNCPTR(XRenderComposite)
178 LOAD_FUNCPTR(XRenderCompositeString8)
179 LOAD_FUNCPTR(XRenderCompositeString16)
180 LOAD_FUNCPTR(XRenderCompositeString32)
181 LOAD_FUNCPTR(XRenderCompositeText16)
182 LOAD_FUNCPTR(XRenderCreateGlyphSet)
183 LOAD_FUNCPTR(XRenderCreatePicture)
184 LOAD_FUNCPTR(XRenderFillRectangle)
185 LOAD_FUNCPTR(XRenderFindFormat)
186 LOAD_FUNCPTR(XRenderFindVisualFormat)
187 LOAD_FUNCPTR(XRenderFreeGlyphSet)
188 LOAD_FUNCPTR(XRenderFreePicture)
189 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
190 LOAD_FUNCPTR(XRenderQueryExtension)
191 #undef LOAD_FUNCPTR
192 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
193 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
194 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
195 #undef LOAD_OPTIONAL_FUNCPTR
196 #endif
197
198
199         wine_tsx11_lock();
200         if(pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) {
201             X11DRV_XRender_Installed = TRUE;
202             TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
203             pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, visual);
204             if(!pict_formats[color_drawable])
205             {
206                 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
207                 if (visual->class == DirectColor)
208                 {
209                     XVisualInfo info;
210                     if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
211                                           screen_depth, TrueColor, &info ))
212                     {
213                         pict_formats[color_drawable] = pXRenderFindVisualFormat(gdi_display, info.visual);
214                         if (pict_formats[color_drawable]) visual = info.visual;
215                     }
216                 }
217             }
218             if(!pict_formats[color_drawable]) /* This fails in buggy versions of libXrender.so */
219             {
220                 wine_tsx11_unlock();
221                 WINE_MESSAGE(
222                     "Wine has detected that you probably have a buggy version\n"
223                     "of libXrender.so .  Because of this client side font rendering\n"
224                     "will be disabled.  Please upgrade this library.\n");
225                 X11DRV_XRender_Installed = FALSE;
226                 return;
227             }
228             pf.type = PictTypeDirect;
229             pf.depth = 1;
230             pf.direct.alpha = 0;
231             pf.direct.alphaMask = 1;
232             pict_formats[mono_drawable] = pXRenderFindFormat(gdi_display, PictFormatType |
233                                                              PictFormatDepth | PictFormatAlpha |
234                                                              PictFormatAlphaMask, &pf, 0);
235             if(!pict_formats[mono_drawable]) {
236                 ERR("mono_format == NULL?\n");
237                 X11DRV_XRender_Installed = FALSE;
238             }
239             if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
240                 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
241                 X11DRV_XRender_Installed = FALSE;
242             }
243         }
244         wine_tsx11_unlock();
245     }
246
247 sym_not_found:
248     if(X11DRV_XRender_Installed || client_side_with_core)
249     {
250         glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
251                                   sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
252
253         glyphsetCacheSize = INIT_CACHE_SIZE;
254         lastfree = 0;
255         for(i = 0; i < INIT_CACHE_SIZE; i++) {
256           glyphsetCache[i].next = i + 1;
257           glyphsetCache[i].count = -1;
258         }
259         glyphsetCache[i-1].next = -1;
260         using_client_side_fonts = 1;
261
262         if(!X11DRV_XRender_Installed) {
263             TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
264             if(screen_depth <= 8 || !client_side_antialias_with_core)
265                 antialias = 0;
266         } else {
267             if(screen_depth <= 8 || !client_side_antialias_with_render)
268                 antialias = 0;
269         }
270     }
271     else TRACE("Using X11 core fonts\n");
272 }
273
274 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
275 {
276   if(p1->hash != p2->hash) return TRUE;
277   if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
278   if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
279   if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
280   return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
281 }
282
283 #if 0
284 static void walk_cache(void)
285 {
286   int i;
287
288   EnterCriticalSection(&xrender_cs);
289   for(i=mru; i >= 0; i = glyphsetCache[i].next)
290     TRACE("item %d\n", i);
291   LeaveCriticalSection(&xrender_cs);
292 }
293 #endif
294
295 static int LookupEntry(LFANDSIZE *plfsz)
296 {
297   int i, prev_i = -1;
298
299   for(i = mru; i >= 0; i = glyphsetCache[i].next) {
300     TRACE("%d\n", i);
301     if(glyphsetCache[i].count == -1) { /* reached free list so stop */
302       i = -1;
303       break;
304     }
305
306     if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
307       glyphsetCache[i].count++;
308       if(prev_i >= 0) {
309         glyphsetCache[prev_i].next = glyphsetCache[i].next;
310         glyphsetCache[i].next = mru;
311         mru = i;
312       }
313       TRACE("found font in cache %d\n", i);
314       return i;
315     }
316     prev_i = i;
317   }
318   TRACE("font not in cache\n");
319   return -1;
320 }
321
322 static void FreeEntry(int entry)
323 {
324     int i, format;
325   
326     for(format = 0; format < AA_MAXVALUE; format++) {
327         gsCacheEntryFormat * formatEntry;
328
329         if( !glyphsetCache[entry].format[format] )
330             continue;
331
332         formatEntry = glyphsetCache[entry].format[format];
333
334         if(formatEntry->glyphset) {
335             wine_tsx11_lock();
336             pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
337             wine_tsx11_unlock();
338             formatEntry->glyphset = 0;
339         }
340         if(formatEntry->nrealized) {
341             HeapFree(GetProcessHeap(), 0, formatEntry->realized);
342             formatEntry->realized = NULL;
343             if(formatEntry->bitmaps) {
344                 for(i = 0; i < formatEntry->nrealized; i++)
345                     HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
346                 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
347                 formatEntry->bitmaps = NULL;
348             }
349             HeapFree(GetProcessHeap(), 0, formatEntry->gis);
350             formatEntry->gis = NULL;
351             formatEntry->nrealized = 0;
352         }
353
354         HeapFree(GetProcessHeap(), 0, formatEntry);
355         glyphsetCache[entry].format[format] = NULL;
356     }
357 }
358
359 static int AllocEntry(void)
360 {
361   int best = -1, prev_best = -1, i, prev_i = -1;
362
363   if(lastfree >= 0) {
364     assert(glyphsetCache[lastfree].count == -1);
365     glyphsetCache[lastfree].count = 1;
366     best = lastfree;
367     lastfree = glyphsetCache[lastfree].next;
368     assert(best != mru);
369     glyphsetCache[best].next = mru;
370     mru = best;
371
372     TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
373     return mru;
374   }
375
376   for(i = mru; i >= 0; i = glyphsetCache[i].next) {
377     if(glyphsetCache[i].count == 0) {
378       best = i;
379       prev_best = prev_i;
380     }
381     prev_i = i;
382   }
383
384   if(best >= 0) {
385     TRACE("freeing unused glyphset at cache %d\n", best);
386     FreeEntry(best);
387     glyphsetCache[best].count = 1;
388     if(prev_best >= 0) {
389       glyphsetCache[prev_best].next = glyphsetCache[best].next;
390       glyphsetCache[best].next = mru;
391       mru = best;
392     } else {
393       assert(mru == best);
394     }
395     return mru;
396   }
397
398   TRACE("Growing cache\n");
399   
400   if (glyphsetCache)
401     glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
402                               glyphsetCache,
403                               (glyphsetCacheSize + INIT_CACHE_SIZE)
404                               * sizeof(*glyphsetCache));
405   else
406     glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
407                               (glyphsetCacheSize + INIT_CACHE_SIZE)
408                               * sizeof(*glyphsetCache));
409
410   for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
411       i++) {
412     glyphsetCache[i].next = i + 1;
413     glyphsetCache[i].count = -1;
414   }
415   glyphsetCache[i-1].next = -1;
416   glyphsetCacheSize += INIT_CACHE_SIZE;
417
418   lastfree = glyphsetCache[best].next;
419   glyphsetCache[best].count = 1;
420   glyphsetCache[best].next = mru;
421   mru = best;
422   TRACE("new free cache slot at %d\n", mru);
423   return mru;
424 }
425
426 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
427 {
428     DWORD size;
429     WORD *gasp, *buffer;
430     WORD num_recs;
431     DWORD ppem;
432     TEXTMETRICW tm;
433
434     *flags = 0;
435
436     size = GetFontData(physDev->hdc, MS_GASP_TAG,  0, NULL, 0);
437     if(size == GDI_ERROR)
438         return FALSE;
439
440     gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
441     GetFontData(physDev->hdc, MS_GASP_TAG,  0, gasp, size);
442
443     GetTextMetricsW(physDev->hdc, &tm);
444     ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
445
446     gasp++;
447     num_recs = get_be_word(*gasp);
448     gasp++;
449     while(num_recs--)
450     {
451         *flags = get_be_word(*(gasp + 1));
452         if(ppem <= get_be_word(*gasp))
453             break;
454         gasp += 2;
455     }
456     TRACE("got flags %04x for ppem %d\n", *flags, ppem);
457
458     HeapFree(GetProcessHeap(), 0, buffer);
459     return TRUE;
460 }
461
462 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
463 {
464     AA_Type ret;
465     WORD flags;
466     UINT font_smoothing_type, font_smoothing_orientation;
467
468     if (X11DRV_XRender_Installed && subpixel &&
469         SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
470         font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
471     {
472         if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
473                                     &font_smoothing_orientation, 0) &&
474              font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
475         {
476             ret = AA_BGR;
477         }
478         else
479             ret = AA_RGB;
480         /*FIXME
481           If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
482           But, Wine's subpixel rendering can support the portrait mode.
483          */
484     }
485     else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
486         ret = AA_Grey;
487     else
488         ret = AA_None;
489
490     return ret;
491 }
492
493 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
494 {
495     int ret;
496     int format;
497     gsCacheEntry *entry;
498     static int hinter = -1;
499     static int subpixel = -1;
500     BOOL font_smoothing;
501
502     if((ret = LookupEntry(plfsz)) != -1) return ret;
503
504     ret = AllocEntry();
505     entry = glyphsetCache + ret;
506     entry->lfsz = *plfsz;
507     for( format = 0; format < AA_MAXVALUE; format++ ) {
508         assert( !entry->format[format] );
509     }
510
511     if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
512     {
513         if(hinter == -1 || subpixel == -1)
514         {
515             RASTERIZER_STATUS status;
516             GetRasterizerCaps(&status, sizeof(status));
517             hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
518             subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
519         }
520
521         switch (plfsz->lf.lfQuality)
522         {
523             case ANTIALIASED_QUALITY:
524                 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
525                 break;
526             case CLEARTYPE_QUALITY:
527             case CLEARTYPE_NATURAL_QUALITY:
528                 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
529                 break;
530             case DEFAULT_QUALITY:
531             case DRAFT_QUALITY:
532             case PROOF_QUALITY:
533             default:
534                 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
535                      font_smoothing)
536                 {
537                     entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
538                 }
539                 else
540                     entry->aa_default = AA_None;
541                 break;
542         }
543     }
544     else
545         entry->aa_default = AA_None;
546
547     return ret;
548 }
549
550 static void dec_ref_cache(int index)
551 {
552     assert(index >= 0);
553     TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
554     assert(glyphsetCache[index].count > 0);
555     glyphsetCache[index].count--;
556 }
557
558 static void lfsz_calc_hash(LFANDSIZE *plfsz)
559 {
560   DWORD hash = 0, *ptr, two_chars;
561   WORD *pwc;
562   int i;
563
564   hash ^= plfsz->devsize.cx;
565   hash ^= plfsz->devsize.cy;
566   for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
567     hash ^= *ptr;
568   for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
569     hash ^= *ptr;
570   for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
571     two_chars = *ptr;
572     pwc = (WCHAR *)&two_chars;
573     if(!*pwc) break;
574     *pwc = toupperW(*pwc);
575     pwc++;
576     *pwc = toupperW(*pwc);
577     hash ^= two_chars;
578     if(!*pwc) break;
579   }
580   plfsz->hash = hash;
581   return;
582 }
583
584 /***********************************************************************
585  *   X11DRV_XRender_Finalize
586  */
587 void X11DRV_XRender_Finalize(void)
588 {
589     int i;
590
591     EnterCriticalSection(&xrender_cs);
592     for(i = mru; i >= 0; i = glyphsetCache[i].next)
593         FreeEntry(i);
594     LeaveCriticalSection(&xrender_cs);
595 }
596
597
598 /***********************************************************************
599  *   X11DRV_XRender_SelectFont
600  */
601 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
602 {
603     LFANDSIZE lfsz;
604
605     GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
606     TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
607           lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
608           lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
609     lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
610     lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
611     lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
612     GetWorldTransform( physDev->hdc, &lfsz.xform );
613     lfsz_calc_hash(&lfsz);
614
615     EnterCriticalSection(&xrender_cs);
616     if(!physDev->xrender) {
617         physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
618                                      sizeof(*physDev->xrender));
619         physDev->xrender->cache_index = -1;
620     }
621     else if(physDev->xrender->cache_index != -1)
622         dec_ref_cache(physDev->xrender->cache_index);
623     physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
624     LeaveCriticalSection(&xrender_cs);
625     return 0;
626 }
627
628 /***********************************************************************
629  *   X11DRV_XRender_DeleteDC
630  */
631 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
632 {
633     X11DRV_XRender_UpdateDrawable(physDev);
634
635     EnterCriticalSection(&xrender_cs);
636     if(physDev->xrender->cache_index != -1)
637         dec_ref_cache(physDev->xrender->cache_index);
638     LeaveCriticalSection(&xrender_cs);
639
640     HeapFree(GetProcessHeap(), 0, physDev->xrender);
641     physDev->xrender = NULL;
642     return;
643 }
644
645 /***********************************************************************
646  *   X11DRV_XRender_UpdateDrawable
647  *
648  * Deletes the pict and tile when the drawable changes.
649  */
650 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
651 {
652     wine_tsx11_lock();
653
654     if(physDev->xrender->pict)
655     {
656         TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
657         XFlush(gdi_display);
658         pXRenderFreePicture(gdi_display, physDev->xrender->pict);
659         physDev->xrender->pict = 0;
660     }
661     wine_tsx11_unlock();
662
663     return;
664 }
665
666 /************************************************************************
667  *   UploadGlyph
668  *
669  * Helper to ExtTextOut.  Must be called inside xrender_cs
670  */
671 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
672 {
673     unsigned int buflen;
674     char *buf;
675     Glyph gid;
676     GLYPHMETRICS gm;
677     XGlyphInfo gi;
678     gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
679     gsCacheEntryFormat *formatEntry;
680     UINT ggo_format = GGO_GLYPH_INDEX;
681     XRenderPictFormat pf;
682     unsigned long pf_mask;
683     static const char zero[4];
684     static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
685
686     switch(format) {
687     case AA_Grey:
688         ggo_format |= WINE_GGO_GRAY16_BITMAP;
689         break;
690     case AA_RGB:
691         ggo_format |= WINE_GGO_HRGB_BITMAP;
692         break;
693     case AA_BGR:
694         ggo_format |= WINE_GGO_HBGR_BITMAP;
695         break;
696     case AA_VRGB:
697         ggo_format |= WINE_GGO_VRGB_BITMAP;
698         break;
699     case AA_VBGR:
700         ggo_format |= WINE_GGO_VBGR_BITMAP;
701         break;
702
703     default:
704         ERR("aa = %d - not implemented\n", format);
705     case AA_None:
706         ggo_format |= GGO_BITMAP;
707         break;
708     }
709
710     buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
711     if(buflen == GDI_ERROR) {
712         if(format != AA_None) {
713             format = AA_None;
714             entry->aa_default = AA_None;
715             ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
716             buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
717         }
718         if(buflen == GDI_ERROR) {
719             WARN("GetGlyphOutlineW failed\n");
720             return FALSE;
721         }
722         TRACE("Turning off antialiasing for this monochrome font\n");
723     }
724
725     /* If there is nothing for the current type, we create the entry. */
726     if( !entry->format[format] ) {
727         entry->format[format] = HeapAlloc(GetProcessHeap(),
728                                           HEAP_ZERO_MEMORY,
729                                           sizeof(gsCacheEntryFormat));
730     }
731     formatEntry = entry->format[format];
732
733     if(formatEntry->nrealized <= glyph) {
734         formatEntry->nrealized = (glyph / 128 + 1) * 128;
735
736         if (formatEntry->realized)
737             formatEntry->realized = HeapReAlloc(GetProcessHeap(),
738                                       HEAP_ZERO_MEMORY,
739                                       formatEntry->realized,
740                                       formatEntry->nrealized * sizeof(BOOL));
741         else
742             formatEntry->realized = HeapAlloc(GetProcessHeap(),
743                                       HEAP_ZERO_MEMORY,
744                                       formatEntry->nrealized * sizeof(BOOL));
745
746         if(!X11DRV_XRender_Installed) {
747           if (formatEntry->bitmaps)
748             formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
749                                       HEAP_ZERO_MEMORY,
750                                       formatEntry->bitmaps,
751                                       formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
752           else
753             formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
754                                       HEAP_ZERO_MEMORY,
755                                       formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
756         }
757         if (formatEntry->gis)
758             formatEntry->gis = HeapReAlloc(GetProcessHeap(),
759                                    HEAP_ZERO_MEMORY,
760                                    formatEntry->gis,
761                                    formatEntry->nrealized * sizeof(formatEntry->gis[0]));
762         else
763             formatEntry->gis = HeapAlloc(GetProcessHeap(),
764                                    HEAP_ZERO_MEMORY,
765                                    formatEntry->nrealized * sizeof(formatEntry->gis[0]));
766     }
767
768
769     if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
770         switch(format) {
771         case AA_Grey:
772             pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
773             pf.type = PictTypeDirect;
774             pf.depth = 8;
775             pf.direct.alpha = 0;
776             pf.direct.alphaMask = 0xff;
777             break;
778
779         case AA_RGB:
780         case AA_BGR:
781         case AA_VRGB:
782         case AA_VBGR:
783             pf_mask = PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask |
784                       PictFormatGreen | PictFormatGreenMask | PictFormatBlue |
785                       PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask;
786             pf.type             = PictTypeDirect;
787             pf.depth            = 32;
788             pf.direct.red       = 16;
789             pf.direct.redMask   = 0xff;
790             pf.direct.green     = 8;
791             pf.direct.greenMask = 0xff;
792             pf.direct.blue      = 0;
793             pf.direct.blueMask  = 0xff;
794             pf.direct.alpha     = 24;
795             pf.direct.alphaMask = 0xff;
796             break;
797
798         default:
799             ERR("aa = %d - not implemented\n", format);
800         case AA_None:
801             pf_mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask,
802             pf.type = PictTypeDirect;
803             pf.depth = 1;
804             pf.direct.alpha = 0;
805             pf.direct.alphaMask = 1;
806             break;
807         }
808
809         wine_tsx11_lock();
810         formatEntry->font_format = pXRenderFindFormat(gdi_display, pf_mask, &pf, 0);
811         formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
812         wine_tsx11_unlock();
813     }
814
815
816     buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
817     GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
818     formatEntry->realized[glyph] = TRUE;
819
820     TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
821           buflen,
822           gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
823           gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
824
825     gi.width = gm.gmBlackBoxX;
826     gi.height = gm.gmBlackBoxY;
827     gi.x = -gm.gmptGlyphOrigin.x;
828     gi.y = gm.gmptGlyphOrigin.y;
829     gi.xOff = gm.gmCellIncX;
830     gi.yOff = gm.gmCellIncY;
831
832     if(TRACE_ON(xrender)) {
833         int pitch, i, j;
834         char output[300];
835         unsigned char *line;
836
837         if(format == AA_None) {
838             pitch = ((gi.width + 31) / 32) * 4;
839             for(i = 0; i < gi.height; i++) {
840                 line = (unsigned char*) buf + i * pitch;
841                 output[0] = '\0';
842                 for(j = 0; j < pitch * 8; j++) {
843                     strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
844                 }
845                 TRACE("%s\n", output);
846             }
847         } else {
848             static const char blks[] = " .:;!o*#";
849             char str[2];
850
851             str[1] = '\0';
852             pitch = ((gi.width + 3) / 4) * 4;
853             for(i = 0; i < gi.height; i++) {
854                 line = (unsigned char*) buf + i * pitch;
855                 output[0] = '\0';
856                 for(j = 0; j < pitch; j++) {
857                     str[0] = blks[line[j] >> 5];
858                     strcat(output, str);
859                 }
860                 TRACE("%s\n", output);
861             }
862         }
863     }
864
865
866     if(formatEntry->glyphset) {
867         if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
868             unsigned char *byte = (unsigned char*) buf, c;
869             int i = buflen;
870
871             while(i--) {
872                 c = *byte;
873
874                 /* magic to flip bit order */
875                 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
876                 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
877                 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
878
879                 *byte++ = c;
880             }
881         }
882         else if ( format != AA_Grey &&
883                   ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
884         {
885             unsigned int i, *data = (unsigned int *)buf;
886             for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
887         }
888         gid = glyph;
889
890         /*
891           XRenderCompositeText seems to ignore 0x0 glyphs when
892           AA_None, which means we lose the advance width of glyphs
893           like the space.  We'll pretend that such glyphs are 1x1
894           bitmaps.
895         */
896
897         if(buflen == 0)
898             gi.width = gi.height = 1;
899
900         wine_tsx11_lock();
901         pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
902                           buflen ? buf : zero, buflen ? buflen : sizeof(zero));
903         wine_tsx11_unlock();
904         HeapFree(GetProcessHeap(), 0, buf);
905     } else {
906         formatEntry->bitmaps[glyph] = buf;
907     }
908
909     formatEntry->gis[glyph] = gi;
910
911     return TRUE;
912 }
913
914 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
915                             void *bitmap, XGlyphInfo *gi)
916 {
917     unsigned char   *srcLine = bitmap, *src;
918     unsigned char   bits, bitsMask;
919     int             width = gi->width;
920     int             stride = ((width + 31) & ~31) >> 3;
921     int             height = gi->height;
922     int             w;
923     int             xspan, lenspan;
924
925     TRACE("%d, %d\n", x, y);
926     x -= gi->x;
927     y -= gi->y;
928     while (height--)
929     {
930         src = srcLine;
931         srcLine += stride;
932         w = width;
933         
934         bitsMask = 0x80;    /* FreeType is always MSB first */
935         bits = *src++;
936         
937         xspan = x;
938         while (w)
939         {
940             if (bits & bitsMask)
941             {
942                 lenspan = 0;
943                 do
944                 {
945                     lenspan++;
946                     if (lenspan == w)
947                         break;
948                     bitsMask = bitsMask >> 1;
949                     if (!bitsMask)
950                     {
951                         bits = *src++;
952                         bitsMask = 0x80;
953                     }
954                 } while (bits & bitsMask);
955                 XFillRectangle (gdi_display, physDev->drawable, 
956                                 physDev->gc, xspan, y, lenspan, 1);
957                 xspan += lenspan;
958                 w -= lenspan;
959             }
960             else
961             {
962                 do
963                 {
964                     w--;
965                     xspan++;
966                     if (!w)
967                         break;
968                     bitsMask = bitsMask >> 1;
969                     if (!bitsMask)
970                     {
971                         bits = *src++;
972                         bitsMask = 0x80;
973                     }
974                 } while (!(bits & bitsMask));
975             }
976         }
977         y++;
978     }
979 }
980
981 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
982                             void *bitmap, XGlyphInfo *gi)
983 {
984     unsigned char   *srcLine = bitmap, *src, bits;
985     int             width = gi->width;
986     int             stride = ((width + 3) & ~3);
987     int             height = gi->height;
988     int             w;
989     int             xspan, lenspan;
990
991     x -= gi->x;
992     y -= gi->y;
993     while (height--)
994     {
995         src = srcLine;
996         srcLine += stride;
997         w = width;
998         
999         bits = *src++;
1000         xspan = x;
1001         while (w)
1002         {
1003             if (bits >= 0x80)
1004             {
1005                 lenspan = 0;
1006                 do
1007                 {
1008                     lenspan++;
1009                     if (lenspan == w)
1010                         break;
1011                     bits = *src++;
1012                 } while (bits >= 0x80);
1013                 XFillRectangle (gdi_display, physDev->drawable, 
1014                                 physDev->gc, xspan, y, lenspan, 1);
1015                 xspan += lenspan;
1016                 w -= lenspan;
1017             }
1018             else
1019             {
1020                 do
1021                 {
1022                     w--;
1023                     xspan++;
1024                     if (!w)
1025                         break;
1026                     bits = *src++;
1027                 } while (bits < 0x80);
1028             }
1029         }
1030         y++;
1031     }
1032 }
1033
1034
1035 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1036 {
1037     int s, l;
1038
1039     s = 0;
1040     while ((mask & 1) == 0)
1041     {
1042         mask >>= 1;
1043         s++;
1044     }
1045     l = 0;
1046     while ((mask & 1) == 1)
1047     {
1048         mask >>= 1;
1049         l++;
1050     }
1051     *shift = s;
1052     *len = l;
1053 }
1054
1055 static DWORD GetField (DWORD pixel, int shift, int len)
1056 {
1057     pixel = pixel & (((1 << (len)) - 1) << shift);
1058     pixel = pixel << (32 - (shift + len)) >> 24;
1059     while (len < 8)
1060     {
1061         pixel |= (pixel >> len);
1062         len <<= 1;
1063     }
1064     return pixel;
1065 }
1066
1067
1068 static DWORD PutField (DWORD pixel, int shift, int len)
1069 {
1070     shift = shift - (8 - len);
1071     if (len <= 8)
1072         pixel &= (((1 << len) - 1) << (8 - len));
1073     if (shift < 0)
1074         pixel >>= -shift;
1075     else
1076         pixel <<= shift;
1077     return pixel;
1078 }
1079
1080 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1081                             int color)
1082 {
1083     int             r_shift, r_len;
1084     int             g_shift, g_len;
1085     int             b_shift, b_len;
1086     BYTE            *maskLine, *mask, m;
1087     int             maskStride;
1088     DWORD           pixel;
1089     int             width, height;
1090     int             w, tx;
1091     BYTE            src_r, src_g, src_b;
1092
1093     x -= gi->x;
1094     y -= gi->y;
1095     width = gi->width;
1096     height = gi->height;
1097
1098     maskLine = bitmap;
1099     maskStride = (width + 3) & ~3;
1100
1101     ExamineBitfield (image->red_mask, &r_shift, &r_len);
1102     ExamineBitfield (image->green_mask, &g_shift, &g_len);
1103     ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1104
1105     src_r = GetField(color, r_shift, r_len);
1106     src_g = GetField(color, g_shift, g_len);
1107     src_b = GetField(color, b_shift, b_len);
1108     
1109     for(; height--; y++)
1110     {
1111         mask = maskLine;
1112         maskLine += maskStride;
1113         w = width;
1114         tx = x;
1115
1116         if(y < 0) continue;
1117         if(y >= image->height) break;
1118
1119         for(; w--; tx++)
1120         {
1121             if(tx >= image->width) break;
1122
1123             m = *mask++;
1124             if(tx < 0) continue;
1125
1126             if (m == 0xff)
1127                 XPutPixel (image, tx, y, color);
1128             else if (m)
1129             {
1130                 BYTE r, g, b;
1131
1132                 pixel = XGetPixel (image, tx, y);
1133
1134                 r = GetField(pixel, r_shift, r_len);
1135                 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1136                 g = GetField(pixel, g_shift, g_len);
1137                 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1138                 b = GetField(pixel, b_shift, b_len);
1139                 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1140
1141                 pixel = (PutField (r, r_shift, r_len) |
1142                          PutField (g, g_shift, g_len) |
1143                          PutField (b, b_shift, b_len));
1144                 XPutPixel (image, tx, y, pixel);
1145             }
1146         }
1147     }
1148 }
1149
1150 /*************************************************************
1151  *                 get_tile_pict
1152  *
1153  * Returns an appropriate Picture for tiling the text colour.
1154  * Call and use result within the xrender_cs
1155  */
1156 static Picture get_tile_pict(enum drawable_depth_type type, int text_pixel)
1157 {
1158     static struct
1159     {
1160         Pixmap xpm;
1161         Picture pict;
1162         int current_color;
1163     } tiles[2], *tile;
1164     XRenderColor col;
1165
1166     tile = &tiles[type];
1167
1168     if(!tile->xpm)
1169     {
1170         XRenderPictureAttributes pa;
1171
1172         wine_tsx11_lock();
1173         tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_formats[type]->depth);
1174
1175         pa.repeat = RepeatNormal;
1176         tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_formats[type], CPRepeat, &pa);
1177         wine_tsx11_unlock();
1178
1179         /* init current_color to something different from text_pixel */
1180         tile->current_color = ~text_pixel;
1181
1182         if(type == mono_drawable)
1183         {
1184             /* for a 1bpp bitmap we always need a 1 in the tile */
1185             col.red = col.green = col.blue = 0;
1186             col.alpha = 0xffff;
1187             wine_tsx11_lock();
1188             pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1189             wine_tsx11_unlock();
1190         }
1191     }
1192
1193     if(text_pixel != tile->current_color && type == color_drawable)
1194     {
1195         /* Map 0 -- 0xff onto 0 -- 0xffff */
1196         int r_shift, r_len;
1197         int g_shift, g_len;
1198         int b_shift, b_len;
1199
1200         ExamineBitfield (visual->red_mask, &r_shift, &r_len );
1201         ExamineBitfield (visual->green_mask, &g_shift, &g_len);
1202         ExamineBitfield (visual->blue_mask, &b_shift, &b_len);
1203
1204         col.red = GetField(text_pixel, r_shift, r_len);
1205         col.red |= col.red << 8;
1206         col.green = GetField(text_pixel, g_shift, g_len);
1207         col.green |= col.green << 8;
1208         col.blue = GetField(text_pixel, b_shift, b_len);
1209         col.blue |= col.blue << 8;
1210         col.alpha = 0xffff;
1211
1212         wine_tsx11_lock();
1213         pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1214         wine_tsx11_unlock();
1215         tile->current_color = text_pixel;
1216     }
1217     return tile->pict;
1218 }
1219
1220 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1221 {
1222     return 1;
1223 }
1224
1225 /***********************************************************************
1226  *   X11DRV_XRender_ExtTextOut
1227  */
1228 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1229                                 const RECT *lprect, LPCWSTR wstr, UINT count,
1230                                 const INT *lpDx )
1231 {
1232     RGNDATA *data;
1233     XGCValues xgcval;
1234     gsCacheEntry *entry;
1235     gsCacheEntryFormat *formatEntry;
1236     BOOL retv = FALSE;
1237     HDC hdc = physDev->hdc;
1238     int textPixel, backgroundPixel;
1239     HRGN saved_region = 0;
1240     BOOL disable_antialias = FALSE;
1241     AA_Type aa_type = AA_None;
1242     DIBSECTION bmp;
1243     unsigned int idx;
1244     double cosEsc, sinEsc;
1245     LOGFONTW lf;
1246     enum drawable_depth_type depth_type = (physDev->depth == 1) ? mono_drawable : color_drawable;
1247     Picture tile_pict = 0;
1248
1249     /* Do we need to disable antialiasing because of palette mode? */
1250     if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1251         TRACE("bitmap is not a DIB\n");
1252     }
1253     else if (bmp.dsBmih.biBitCount <= 8) {
1254         TRACE("Disabling antialiasing\n");
1255         disable_antialias = TRUE;
1256     }
1257
1258     xgcval.function = GXcopy;
1259     xgcval.background = physDev->backgroundPixel;
1260     xgcval.fill_style = FillSolid;
1261     wine_tsx11_lock();
1262     XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1263     wine_tsx11_unlock();
1264
1265     X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1266
1267     if(physDev->depth == 1) {
1268         if((physDev->textPixel & 0xffffff) == 0) {
1269             textPixel = 0;
1270             backgroundPixel = 1;
1271         } else {
1272             textPixel = 1;
1273             backgroundPixel = 0;
1274         }
1275     } else {
1276         textPixel = physDev->textPixel;
1277         backgroundPixel = physDev->backgroundPixel;
1278     }
1279
1280     if(flags & ETO_OPAQUE)
1281     {
1282         wine_tsx11_lock();
1283         XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1284         XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1285                         physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1286                         lprect->right - lprect->left, lprect->bottom - lprect->top );
1287         wine_tsx11_unlock();
1288     }
1289
1290     if(count == 0)
1291     {
1292         retv = TRUE;
1293         goto done_unlock;
1294     }
1295
1296     
1297     GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1298     if(lf.lfEscapement != 0) {
1299         cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1300         sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1301     } else {
1302         cosEsc = 1;
1303         sinEsc = 0;
1304     }
1305
1306     if (flags & ETO_CLIPPED)
1307     {
1308         HRGN clip_region;
1309
1310         clip_region = CreateRectRgnIndirect( lprect );
1311         /* make a copy of the current device region */
1312         saved_region = CreateRectRgn( 0, 0, 0, 0 );
1313         CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1314         X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1315         DeleteObject( clip_region );
1316     }
1317
1318     if(X11DRV_XRender_Installed) {
1319         if(!physDev->xrender->pict) {
1320             XRenderPictureAttributes pa;
1321             pa.subwindow_mode = IncludeInferiors;
1322
1323             wine_tsx11_lock();
1324             physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1325                                                            physDev->drawable,
1326                                                            pict_formats[depth_type],
1327                                                            CPSubwindowMode, &pa);
1328             wine_tsx11_unlock();
1329
1330             TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1331                   physDev->xrender->pict, hdc, physDev->drawable);
1332         } else {
1333             TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1334                   physDev->xrender->pict, hdc, physDev->drawable);
1335         }
1336
1337         if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1338         {
1339             wine_tsx11_lock();
1340             pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1341                                               physDev->dc_rect.left, physDev->dc_rect.top,
1342                                               (XRectangle *)data->Buffer, data->rdh.nCount );
1343             wine_tsx11_unlock();
1344             HeapFree( GetProcessHeap(), 0, data );
1345         }
1346     }
1347
1348     EnterCriticalSection(&xrender_cs);
1349
1350     entry = glyphsetCache + physDev->xrender->cache_index;
1351     if( disable_antialias == FALSE )
1352         aa_type = entry->aa_default;
1353     formatEntry = entry->format[aa_type];
1354
1355     for(idx = 0; idx < count; idx++) {
1356         if( !formatEntry ) {
1357             UploadGlyph(physDev, wstr[idx], aa_type);
1358             /* re-evaluate antialias since aa_default may have changed */
1359             if( disable_antialias == FALSE )
1360                 aa_type = entry->aa_default;
1361             formatEntry = entry->format[aa_type];
1362         } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1363             UploadGlyph(physDev, wstr[idx], aa_type);
1364         }
1365     }
1366     if (!formatEntry)
1367     {
1368         WARN("could not upload requested glyphs\n");
1369         LeaveCriticalSection(&xrender_cs);
1370         goto done_unlock;
1371     }
1372
1373     TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1374           physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1375
1376     if(X11DRV_XRender_Installed)
1377     {
1378         XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1379         INT offset = 0;
1380         POINT desired, current;
1381         int render_op = PictOpOver;
1382
1383         /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1384            So we pass zeros to the function and move to our starting position using the first
1385            element of the elts array. */
1386
1387         desired.x = physDev->dc_rect.left + x;
1388         desired.y = physDev->dc_rect.top + y;
1389         current.x = current.y = 0;
1390
1391         tile_pict = get_tile_pict(depth_type, physDev->textPixel);
1392
1393         /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1394          */
1395         if((depth_type == mono_drawable) && (textPixel == 0))
1396             render_op = PictOpOutReverse; /* This gives us 'black' text */
1397
1398         for(idx = 0; idx < count; idx++)
1399         {
1400             elts[idx].glyphset = formatEntry->glyphset;
1401             elts[idx].chars = wstr + idx;
1402             elts[idx].nchars = 1;
1403             elts[idx].xOff = desired.x - current.x;
1404             elts[idx].yOff = desired.y - current.y;
1405
1406             current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1407             current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1408
1409             if(!lpDx)
1410             {
1411                 desired.x += formatEntry->gis[wstr[idx]].xOff;
1412                 desired.y += formatEntry->gis[wstr[idx]].yOff;
1413             }
1414             else
1415             {
1416                 offset += lpDx[idx];
1417                 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1418                 desired.y = physDev->dc_rect.top  + y - offset * sinEsc;
1419             }
1420         }
1421         wine_tsx11_lock();
1422         pXRenderCompositeText16(gdi_display, render_op,
1423                                 tile_pict,
1424                                 physDev->xrender->pict,
1425                                 formatEntry->font_format,
1426                                 0, 0, 0, 0, elts, count);
1427         wine_tsx11_unlock();
1428         HeapFree(GetProcessHeap(), 0, elts);
1429     } else {
1430         INT offset = 0, xoff = 0, yoff = 0;
1431         wine_tsx11_lock();
1432         XSetForeground( gdi_display, physDev->gc, textPixel );
1433
1434         if(aa_type == AA_None || physDev->depth == 1)
1435         {
1436             void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1437
1438             if(aa_type == AA_None)
1439                 sharp_glyph_fn = SharpGlyphMono;
1440             else
1441                 sharp_glyph_fn = SharpGlyphGray;
1442
1443             for(idx = 0; idx < count; idx++) {
1444                 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1445                                physDev->dc_rect.top + y + yoff,
1446                                formatEntry->bitmaps[wstr[idx]],
1447                                &formatEntry->gis[wstr[idx]]);
1448                 if(lpDx) {
1449                     offset += lpDx[idx];
1450                     xoff = offset * cosEsc;
1451                     yoff = offset * -sinEsc;
1452                 } else {
1453                     xoff += formatEntry->gis[wstr[idx]].xOff;
1454                     yoff += formatEntry->gis[wstr[idx]].yOff;
1455                 }
1456             }
1457         } else {
1458             XImage *image;
1459             int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1460             RECT extents = {0, 0, 0, 0};
1461             POINT cur = {0, 0};
1462             int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1463             int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1464
1465             TRACE("drawable %dx%d\n", w, h);
1466
1467             for(idx = 0; idx < count; idx++) {
1468                 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1469                     extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1470                 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1471                     extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1472                 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1473                     extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1474                 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1475                     extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1476                 if(lpDx) {
1477                     offset += lpDx[idx];
1478                     cur.x = offset * cosEsc;
1479                     cur.y = offset * -sinEsc;
1480                 } else {
1481                     cur.x += formatEntry->gis[wstr[idx]].xOff;
1482                     cur.y += formatEntry->gis[wstr[idx]].yOff;
1483                 }
1484             }
1485             TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1486                   extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1487
1488             if(physDev->dc_rect.left + x + extents.left >= 0) {
1489                 image_x = physDev->dc_rect.left + x + extents.left;
1490                 image_off_x = 0;
1491             } else {
1492                 image_x = 0;
1493                 image_off_x = physDev->dc_rect.left + x + extents.left;
1494             }
1495             if(physDev->dc_rect.top + y + extents.top >= 0) {
1496                 image_y = physDev->dc_rect.top + y + extents.top;
1497                 image_off_y = 0;
1498             } else {
1499                 image_y = 0;
1500                 image_off_y = physDev->dc_rect.top + y + extents.top;
1501             }
1502             if(physDev->dc_rect.left + x + extents.right < w)
1503                 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1504             else
1505                 image_w = w - image_x;
1506             if(physDev->dc_rect.top + y + extents.bottom < h)
1507                 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1508             else
1509                 image_h = h - image_y;
1510
1511             if(image_w <= 0 || image_h <= 0) goto no_image;
1512
1513             X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1514             image = XGetImage(gdi_display, physDev->drawable,
1515                               image_x, image_y, image_w, image_h,
1516                               AllPlanes, ZPixmap);
1517             X11DRV_check_error();
1518
1519             TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1520                   gdi_display, (int)physDev->drawable, image_x, image_y,
1521                   image_w, image_h, AllPlanes, ZPixmap,
1522                   physDev->depth, image);
1523             if(!image) {
1524                 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1525                                            physDev->depth);
1526                 GC gc;
1527                 XGCValues gcv;
1528
1529                 gcv.graphics_exposures = False;
1530                 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1531                 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1532                           image_w, image_h, 0, 0);
1533                 XFreeGC(gdi_display, gc);
1534                 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1535                 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1536                                   ZPixmap);
1537                 X11DRV_check_error();
1538                 XFreePixmap(gdi_display, xpm);
1539             }
1540             if(!image) goto no_image;
1541
1542             image->red_mask = visual->red_mask;
1543             image->green_mask = visual->green_mask;
1544             image->blue_mask = visual->blue_mask;
1545
1546             offset = xoff = yoff = 0;
1547             for(idx = 0; idx < count; idx++) {
1548                 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1549                                 yoff + image_off_y - extents.top,
1550                                 formatEntry->bitmaps[wstr[idx]],
1551                                 &formatEntry->gis[wstr[idx]],
1552                                 physDev->textPixel);
1553                 if(lpDx) {
1554                     offset += lpDx[idx];
1555                     xoff = offset * cosEsc;
1556                     yoff = offset * -sinEsc;
1557                 } else {
1558                     xoff += formatEntry->gis[wstr[idx]].xOff;
1559                     yoff += formatEntry->gis[wstr[idx]].yOff;
1560                 }
1561             }
1562             XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1563                       image_x, image_y, image_w, image_h);
1564             XDestroyImage(image);
1565         }
1566     no_image:
1567         wine_tsx11_unlock();
1568     }
1569     LeaveCriticalSection(&xrender_cs);
1570
1571     if (flags & ETO_CLIPPED)
1572     {
1573         /* restore the device region */
1574         X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1575         DeleteObject( saved_region );
1576     }
1577
1578     retv = TRUE;
1579
1580 done_unlock:
1581     X11DRV_UnlockDIBSection( physDev, TRUE );
1582     return retv;
1583 }
1584
1585 /******************************************************************************
1586  * AlphaBlend         (x11drv.@)
1587  */
1588 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1589                              X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1590                              BLENDFUNCTION blendfn)
1591 {
1592     XRenderPictureAttributes pa;
1593     XRenderPictFormat *src_format;
1594     XRenderPictFormat argb32_templ = {
1595         0,                          /* id */
1596         PictTypeDirect,             /* type */
1597         32,                         /* depth */
1598         {                           /* direct */
1599             16,                     /* direct.red */
1600             0xff,                   /* direct.redMask */
1601             8,                      /* direct.green */
1602             0xff,                   /* direct.greenMask */
1603             0,                      /* direct.blue */
1604             0xff,                   /* direct.blueMask */
1605             24,                     /* direct.alpha */
1606             0xff,                   /* direct.alphaMask */
1607         },
1608         0,                          /* colormap */
1609     };
1610     unsigned long argb32_templ_mask = 
1611         PictFormatType |
1612         PictFormatDepth |
1613         PictFormatRed |
1614         PictFormatRedMask |
1615         PictFormatGreen |
1616         PictFormatGreenMask |
1617         PictFormatBlue |
1618         PictFormatBlueMask |
1619         PictFormatAlpha |
1620         PictFormatAlphaMask;
1621
1622     Picture dst_pict, src_pict;
1623     Pixmap xpm;
1624     DIBSECTION dib;
1625     XImage *image;
1626     GC gc;
1627     XGCValues gcv;
1628     DWORD *dstbits, *data;
1629     int y, y2;
1630     POINT pts[2];
1631     BOOL top_down = FALSE;
1632     RGNDATA *rgndata;
1633     enum drawable_depth_type dst_depth_type = (devDst->depth == 1) ? mono_drawable : color_drawable;
1634     int repeat_src;
1635
1636     if(!X11DRV_XRender_Installed) {
1637         FIXME("Unable to AlphaBlend without Xrender\n");
1638         return FALSE;
1639     }
1640     pts[0].x = xDst;
1641     pts[0].y = yDst;
1642     pts[1].x = xDst + widthDst;
1643     pts[1].y = yDst + heightDst;
1644     LPtoDP(devDst->hdc, pts, 2);
1645     xDst      = pts[0].x;
1646     yDst      = pts[0].y;
1647     widthDst  = pts[1].x - pts[0].x;
1648     heightDst = pts[1].y - pts[0].y;
1649
1650     pts[0].x = xSrc;
1651     pts[0].y = ySrc;
1652     pts[1].x = xSrc + widthSrc;
1653     pts[1].y = ySrc + heightSrc;
1654     LPtoDP(devSrc->hdc, pts, 2);
1655     xSrc      = pts[0].x;
1656     ySrc      = pts[0].y;
1657     widthSrc  = pts[1].x - pts[0].x;
1658     heightSrc = pts[1].y - pts[0].y;
1659     if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1660
1661     /* If the source is a 1x1 bitmap, tiling is equivalent to stretching, but
1662         tiling is much faster. Therefore, we do no stretching in this case. */
1663     repeat_src = widthSrc == 1 && heightSrc == 1;
1664
1665 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1666     if((widthDst != widthSrc || heightDst != heightSrc) && !repeat_src)
1667 #else
1668     if(!pXRenderSetPictureTransform && !repeat_src)
1669 #endif
1670     {
1671         FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1672         return FALSE;
1673     }
1674
1675     if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1676     {
1677         static BOOL out = FALSE;
1678         if (!out)
1679         {
1680             FIXME("not a dibsection\n");
1681             out = TRUE;
1682         }
1683         return FALSE;
1684     }
1685
1686     if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1687         || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1688     {
1689         WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1690         SetLastError(ERROR_INVALID_PARAMETER);
1691         return FALSE;
1692     }
1693
1694     if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1695         FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1696
1697     if(dib.dsBm.bmBitsPixel != 32) {
1698         FIXME("not a 32 bpp dibsection\n");
1699         return FALSE;
1700     }
1701     dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1702
1703     if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1704         top_down = TRUE;
1705         dstbits += widthSrc * (heightSrc - 1);
1706         y2 = ySrc;
1707         y = y2 + heightSrc - 1;
1708     }
1709     else
1710     {
1711         y = dib.dsBmih.biHeight - ySrc - 1;
1712         y2 = y - heightSrc + 1;
1713     }
1714
1715     if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1716     {
1717         for(; y >= y2; y--)
1718         {
1719             memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1720                    widthSrc * 4);
1721             dstbits += (top_down ? -1 : 1) * widthSrc;
1722         }
1723     }
1724     else
1725     {
1726         DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1727         int x;
1728
1729         for(; y >= y2; y--)
1730         {
1731             DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1732             for (x = 0; x < widthSrc; x++)
1733             {
1734                 DWORD argb = *srcbits++;
1735                 argb = (argb & 0xffffff) | source_alpha;
1736                 *dstbits++ = argb;
1737             }
1738             if (top_down)  /* we traversed the row forward so we should go back by two rows */
1739                 dstbits -= 2 * widthSrc;
1740         }
1741
1742     }
1743
1744     rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1745
1746     wine_tsx11_lock();
1747     image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1748                          (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1749
1750     /*
1751       Avoid using XRenderFindStandardFormat as older libraries don't have it
1752       src_format = pXRenderFindStandardFormat(gdi_display, PictStandardARGB32);
1753     */
1754     src_format = pXRenderFindFormat(gdi_display, argb32_templ_mask, &argb32_templ, 0);
1755
1756     TRACE("src_format %p\n", src_format);
1757
1758     pa.subwindow_mode = IncludeInferiors;
1759     pa.repeat = repeat_src ? RepeatNormal : RepeatNone;
1760
1761     /* FIXME use devDst->xrender->pict ? */
1762     dst_pict = pXRenderCreatePicture(gdi_display,
1763                                      devDst->drawable,
1764                                      pict_formats[dst_depth_type],
1765                                      CPSubwindowMode, &pa);
1766     TRACE("dst_pict %08lx\n", dst_pict);
1767     TRACE("src_drawable = %08lx\n", devSrc->drawable);
1768     xpm = XCreatePixmap(gdi_display,
1769                         root_window,
1770                         widthSrc, heightSrc, 32);
1771     gcv.graphics_exposures = False;
1772     gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1773     TRACE("xpm = %08lx\n", xpm);
1774     XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1775
1776     src_pict = pXRenderCreatePicture(gdi_display,
1777                                      xpm, src_format,
1778                                      CPSubwindowMode|CPRepeat, &pa);
1779     TRACE("src_pict %08lx\n", src_pict);
1780
1781     if (rgndata)
1782     {
1783         pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1784                                           devDst->dc_rect.left, devDst->dc_rect.top,
1785                                           (XRectangle *)rgndata->Buffer, 
1786                                           rgndata->rdh.nCount );
1787         HeapFree( GetProcessHeap(), 0, rgndata );
1788     }
1789
1790 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1791     if(!repeat_src && (widthDst != widthSrc || heightDst != heightSrc)) {
1792         double xscale = widthSrc/(double)widthDst;
1793         double yscale = heightSrc/(double)heightDst;
1794         XTransform xform = {{
1795             { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(0) },
1796             { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(0) },
1797             { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1798         }};
1799         pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1800     }
1801 #endif
1802     pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1803                       0, 0, 0, 0,
1804                       xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1805
1806
1807     pXRenderFreePicture(gdi_display, src_pict);
1808     XFreePixmap(gdi_display, xpm);
1809     XFreeGC(gdi_display, gc);
1810     pXRenderFreePicture(gdi_display, dst_pict);
1811     image->data = NULL;
1812     XDestroyImage(image);
1813
1814     wine_tsx11_unlock();
1815     HeapFree(GetProcessHeap(), 0, data);
1816     return TRUE;
1817 }
1818
1819 #else /* SONAME_LIBXRENDER */
1820
1821 void X11DRV_XRender_Init(void)
1822 {
1823     TRACE("XRender support not compiled in.\n");
1824     return;
1825 }
1826
1827 void X11DRV_XRender_Finalize(void)
1828 {
1829 }
1830
1831 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1832 {
1833   assert(0);
1834   return FALSE;
1835 }
1836
1837 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1838 {
1839   assert(0);
1840   return;
1841 }
1842
1843 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1844                                 const RECT *lprect, LPCWSTR wstr, UINT count,
1845                                 const INT *lpDx )
1846 {
1847   assert(0);
1848   return FALSE;
1849 }
1850
1851 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1852 {
1853   assert(0);
1854   return;
1855 }
1856
1857 /******************************************************************************
1858  * AlphaBlend         (x11drv.@)
1859  */
1860 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1861                        X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1862                        BLENDFUNCTION blendfn)
1863 {
1864   FIXME("not supported - XRENDER headers were missing at compile time\n");
1865   return FALSE;
1866 }
1867
1868 #endif /* SONAME_LIBXRENDER */