winex11: Add helper function for copying brushes.
[wine/hacks.git] / dlls / winex11.drv / xrender.c
blobefa8d02abc440e03e133c0c03a5d89c0ae96b131
1 /*
2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
6 * Some parts also:
7 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
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.
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.
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
23 #include "config.h"
24 #include "wine/port.h"
26 #include <assert.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
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"
39 int using_client_side_fonts = FALSE;
41 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
43 #ifdef SONAME_LIBXRENDER
45 static BOOL X11DRV_XRender_Installed = FALSE;
47 #include <X11/Xlib.h>
48 #include <X11/extensions/Xrender.h>
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
57 #define MAX_FORMATS 10
58 typedef enum wine_xrformat
60 WXR_FORMAT_MONO,
61 WXR_FORMAT_GRAY,
62 WXR_FORMAT_X1R5G5B5,
63 WXR_FORMAT_X1B5G5R5,
64 WXR_FORMAT_R5G6B5,
65 WXR_FORMAT_B5G6R5,
66 WXR_FORMAT_R8G8B8,
67 WXR_FORMAT_B8G8R8,
68 WXR_FORMAT_A8R8G8B8,
69 WXR_FORMAT_X8R8G8B8,
70 } WXRFormat;
72 typedef struct wine_xrender_format_template
74 WXRFormat wxr_format;
75 unsigned int depth;
76 unsigned int alpha;
77 unsigned int alphaMask;
78 unsigned int red;
79 unsigned int redMask;
80 unsigned int green;
81 unsigned int greenMask;
82 unsigned int blue;
83 unsigned int blueMask;
84 } WineXRenderFormatTemplate;
86 static const WineXRenderFormatTemplate wxr_formats_template[] =
88 /* Format depth alpha mask red mask green mask blue mask*/
89 {WXR_FORMAT_MONO, 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
90 {WXR_FORMAT_GRAY, 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
91 {WXR_FORMAT_X1R5G5B5, 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
92 {WXR_FORMAT_X1B5G5R5, 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
93 {WXR_FORMAT_R5G6B5, 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
94 {WXR_FORMAT_B5G6R5, 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
95 {WXR_FORMAT_R8G8B8, 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
96 {WXR_FORMAT_B8G8R8, 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
97 {WXR_FORMAT_A8R8G8B8, 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
98 {WXR_FORMAT_X8R8G8B8, 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff }
101 typedef struct wine_xrender_format
103 WXRFormat format;
104 XRenderPictFormat *pict_format;
105 } WineXRenderFormat;
107 static WineXRenderFormat wxr_formats[MAX_FORMATS];
108 static int WineXRenderFormatsListSize = 0;
109 static WineXRenderFormat *default_format = NULL;
111 typedef struct
113 LOGFONTW lf;
114 XFORM xform;
115 SIZE devsize; /* size in device coords */
116 DWORD hash;
117 } LFANDSIZE;
119 #define INITIAL_REALIZED_BUF_SIZE 128
121 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
123 typedef struct
125 GlyphSet glyphset;
126 WineXRenderFormat *font_format;
127 int nrealized;
128 BOOL *realized;
129 void **bitmaps;
130 XGlyphInfo *gis;
131 } gsCacheEntryFormat;
133 typedef struct
135 LFANDSIZE lfsz;
136 AA_Type aa_default;
137 gsCacheEntryFormat * format[AA_MAXVALUE];
138 INT count;
139 INT next;
140 } gsCacheEntry;
142 struct tagXRENDERINFO
144 int cache_index;
145 Picture pict;
149 static gsCacheEntry *glyphsetCache = NULL;
150 static DWORD glyphsetCacheSize = 0;
151 static INT lastfree = -1;
152 static INT mru = -1;
154 #define INIT_CACHE_SIZE 10
156 static int antialias = 1;
158 static void *xrender_handle;
160 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
161 MAKE_FUNCPTR(XRenderAddGlyphs)
162 MAKE_FUNCPTR(XRenderComposite)
163 MAKE_FUNCPTR(XRenderCompositeString8)
164 MAKE_FUNCPTR(XRenderCompositeString16)
165 MAKE_FUNCPTR(XRenderCompositeString32)
166 MAKE_FUNCPTR(XRenderCompositeText16)
167 MAKE_FUNCPTR(XRenderCreateGlyphSet)
168 MAKE_FUNCPTR(XRenderCreatePicture)
169 MAKE_FUNCPTR(XRenderFillRectangle)
170 MAKE_FUNCPTR(XRenderFindFormat)
171 MAKE_FUNCPTR(XRenderFindVisualFormat)
172 MAKE_FUNCPTR(XRenderFreeGlyphSet)
173 MAKE_FUNCPTR(XRenderFreePicture)
174 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
175 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
176 MAKE_FUNCPTR(XRenderSetPictureTransform)
177 #endif
178 MAKE_FUNCPTR(XRenderQueryExtension)
179 #undef MAKE_FUNCPTR
181 static CRITICAL_SECTION xrender_cs;
182 static CRITICAL_SECTION_DEBUG critsect_debug =
184 0, 0, &xrender_cs,
185 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
186 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
188 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
190 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
191 ( ( (ULONG)_x4 << 24 ) | \
192 ( (ULONG)_x3 << 16 ) | \
193 ( (ULONG)_x2 << 8 ) | \
194 (ULONG)_x1 )
196 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
198 #define GASP_GRIDFIT 0x01
199 #define GASP_DOGRAY 0x02
201 #ifdef WORDS_BIGENDIAN
202 #define get_be_word(x) (x)
203 #define NATIVE_BYTE_ORDER MSBFirst
204 #else
205 #define get_be_word(x) RtlUshortByteSwap(x)
206 #define NATIVE_BYTE_ORDER LSBFirst
207 #endif
209 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
211 templ->id = 0;
212 templ->type = PictTypeDirect;
213 templ->depth = fmt->depth;
214 templ->direct.alpha = fmt->alpha;
215 templ->direct.alphaMask = fmt->alphaMask;
216 templ->direct.red = fmt->red;
217 templ->direct.redMask = fmt->redMask;
218 templ->direct.green = fmt->green;
219 templ->direct.greenMask = fmt->greenMask;
220 templ->direct.blue = fmt->blue;
221 templ->direct.blueMask = fmt->blueMask;
222 templ->colormap = 0;
224 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
226 return TRUE;
229 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
231 if(fmt->depth != screen_depth)
232 return FALSE;
233 if( (fmt->redMask << fmt->red) != visual->red_mask)
234 return FALSE;
235 if( (fmt->greenMask << fmt->green) != visual->green_mask)
236 return FALSE;
237 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
238 return FALSE;
240 /* We never select a default ARGB visual */
241 if(fmt->alphaMask)
242 return FALSE;
244 return TRUE;
247 static int load_xrender_formats(void)
249 unsigned int i;
250 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
252 XRenderPictFormat templ, *pict_format;
254 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
256 wine_tsx11_lock();
257 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
258 if(!pict_format)
260 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
261 if (visual->class == DirectColor)
263 XVisualInfo info;
264 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
265 screen_depth, TrueColor, &info ))
267 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
268 if (pict_format) visual = info.visual;
272 wine_tsx11_unlock();
274 if(pict_format)
276 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
277 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
278 default_format = &wxr_formats[WineXRenderFormatsListSize];
279 WineXRenderFormatsListSize++;
280 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
283 else
285 unsigned long mask = 0;
286 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
288 wine_tsx11_lock();
289 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
290 wine_tsx11_unlock();
292 if(pict_format)
294 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
295 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
296 WineXRenderFormatsListSize++;
297 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
301 return WineXRenderFormatsListSize;
304 /***********************************************************************
305 * X11DRV_XRender_Init
307 * Let's see if our XServer has the extension available
310 void X11DRV_XRender_Init(void)
312 int event_base, i;
314 if (client_side_with_render &&
315 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
316 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
317 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
320 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
321 LOAD_FUNCPTR(XRenderAddGlyphs)
322 LOAD_FUNCPTR(XRenderComposite)
323 LOAD_FUNCPTR(XRenderCompositeString8)
324 LOAD_FUNCPTR(XRenderCompositeString16)
325 LOAD_FUNCPTR(XRenderCompositeString32)
326 LOAD_FUNCPTR(XRenderCompositeText16)
327 LOAD_FUNCPTR(XRenderCreateGlyphSet)
328 LOAD_FUNCPTR(XRenderCreatePicture)
329 LOAD_FUNCPTR(XRenderFillRectangle)
330 LOAD_FUNCPTR(XRenderFindFormat)
331 LOAD_FUNCPTR(XRenderFindVisualFormat)
332 LOAD_FUNCPTR(XRenderFreeGlyphSet)
333 LOAD_FUNCPTR(XRenderFreePicture)
334 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
335 LOAD_FUNCPTR(XRenderQueryExtension)
336 #undef LOAD_FUNCPTR
337 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
338 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
339 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
340 #undef LOAD_OPTIONAL_FUNCPTR
341 #endif
343 wine_tsx11_lock();
344 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
345 wine_tsx11_unlock();
346 if(X11DRV_XRender_Installed) {
347 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
348 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
350 wine_tsx11_unlock();
351 WINE_MESSAGE(
352 "Wine has detected that you probably have a buggy version\n"
353 "of libXrender.so . Because of this client side font rendering\n"
354 "will be disabled. Please upgrade this library.\n");
355 X11DRV_XRender_Installed = FALSE;
356 return;
359 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
360 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
361 X11DRV_XRender_Installed = FALSE;
366 sym_not_found:
367 if(X11DRV_XRender_Installed || client_side_with_core)
369 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
370 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
372 glyphsetCacheSize = INIT_CACHE_SIZE;
373 lastfree = 0;
374 for(i = 0; i < INIT_CACHE_SIZE; i++) {
375 glyphsetCache[i].next = i + 1;
376 glyphsetCache[i].count = -1;
378 glyphsetCache[i-1].next = -1;
379 using_client_side_fonts = 1;
381 if(!X11DRV_XRender_Installed) {
382 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
383 if(screen_depth <= 8 || !client_side_antialias_with_core)
384 antialias = 0;
385 } else {
386 if(screen_depth <= 8 || !client_side_antialias_with_render)
387 antialias = 0;
390 else TRACE("Using X11 core fonts\n");
393 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
394 static void get_xrender_color(WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
396 XRenderPictFormat *pf = wxr_format->pict_format;
398 if(pf->direct.redMask)
399 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
400 else
401 dst_color->red = 0;
403 if(pf->direct.greenMask)
404 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
405 else
406 dst_color->green = 0;
408 if(pf->direct.blueMask)
409 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
410 else
411 dst_color->blue = 0;
413 dst_color->alpha = 0xffff;
416 static WineXRenderFormat *get_xrender_format(WXRFormat format)
418 int i;
419 for(i=0; i<WineXRenderFormatsListSize; i++)
421 if(wxr_formats[i].format == format)
423 TRACE("Returning wxr_format=%#x\n", format);
424 return &wxr_formats[i];
427 return NULL;
430 static WineXRenderFormat *get_xrender_format_from_color_shifts(int depth, ColorShifts *shifts)
432 int redMask, greenMask, blueMask;
433 unsigned int i;
435 if(depth == 1)
436 return get_xrender_format(WXR_FORMAT_MONO);
438 /* physDevs of a depth <=8, don't have color_shifts set and XRender can't handle those except for 1-bit */
439 if(!shifts)
440 return default_format;
442 redMask = shifts->physicalRed.max << shifts->physicalRed.shift;
443 greenMask = shifts->physicalGreen.max << shifts->physicalGreen.shift;
444 blueMask = shifts->physicalBlue.max << shifts->physicalBlue.shift;
446 /* Try to locate a format which matches the specification of the dibsection. */
447 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
449 if( depth == wxr_formats_template[i].depth &&
450 redMask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
451 greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
452 blueMask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
455 /* When we reach this stage the format was found in our template table but this doesn't mean that
456 * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
458 return get_xrender_format(wxr_formats_template[i].wxr_format);
462 /* This should not happen because when we reach 'shifts' must have been set and we only allows shifts which are backed by X */
463 ERR("No XRender format found!\n");
464 return NULL;
467 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
469 if(p1->hash != p2->hash) return TRUE;
470 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
471 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
472 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
473 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
476 #if 0
477 static void walk_cache(void)
479 int i;
481 EnterCriticalSection(&xrender_cs);
482 for(i=mru; i >= 0; i = glyphsetCache[i].next)
483 TRACE("item %d\n", i);
484 LeaveCriticalSection(&xrender_cs);
486 #endif
488 static int LookupEntry(LFANDSIZE *plfsz)
490 int i, prev_i = -1;
492 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
493 TRACE("%d\n", i);
494 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
495 i = -1;
496 break;
499 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
500 glyphsetCache[i].count++;
501 if(prev_i >= 0) {
502 glyphsetCache[prev_i].next = glyphsetCache[i].next;
503 glyphsetCache[i].next = mru;
504 mru = i;
506 TRACE("found font in cache %d\n", i);
507 return i;
509 prev_i = i;
511 TRACE("font not in cache\n");
512 return -1;
515 static void FreeEntry(int entry)
517 int i, format;
519 for(format = 0; format < AA_MAXVALUE; format++) {
520 gsCacheEntryFormat * formatEntry;
522 if( !glyphsetCache[entry].format[format] )
523 continue;
525 formatEntry = glyphsetCache[entry].format[format];
527 if(formatEntry->glyphset) {
528 wine_tsx11_lock();
529 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
530 wine_tsx11_unlock();
531 formatEntry->glyphset = 0;
533 if(formatEntry->nrealized) {
534 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
535 formatEntry->realized = NULL;
536 if(formatEntry->bitmaps) {
537 for(i = 0; i < formatEntry->nrealized; i++)
538 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
539 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
540 formatEntry->bitmaps = NULL;
542 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
543 formatEntry->gis = NULL;
544 formatEntry->nrealized = 0;
547 HeapFree(GetProcessHeap(), 0, formatEntry);
548 glyphsetCache[entry].format[format] = NULL;
552 static int AllocEntry(void)
554 int best = -1, prev_best = -1, i, prev_i = -1;
556 if(lastfree >= 0) {
557 assert(glyphsetCache[lastfree].count == -1);
558 glyphsetCache[lastfree].count = 1;
559 best = lastfree;
560 lastfree = glyphsetCache[lastfree].next;
561 assert(best != mru);
562 glyphsetCache[best].next = mru;
563 mru = best;
565 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
566 return mru;
569 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
570 if(glyphsetCache[i].count == 0) {
571 best = i;
572 prev_best = prev_i;
574 prev_i = i;
577 if(best >= 0) {
578 TRACE("freeing unused glyphset at cache %d\n", best);
579 FreeEntry(best);
580 glyphsetCache[best].count = 1;
581 if(prev_best >= 0) {
582 glyphsetCache[prev_best].next = glyphsetCache[best].next;
583 glyphsetCache[best].next = mru;
584 mru = best;
585 } else {
586 assert(mru == best);
588 return mru;
591 TRACE("Growing cache\n");
593 if (glyphsetCache)
594 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
595 glyphsetCache,
596 (glyphsetCacheSize + INIT_CACHE_SIZE)
597 * sizeof(*glyphsetCache));
598 else
599 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
600 (glyphsetCacheSize + INIT_CACHE_SIZE)
601 * sizeof(*glyphsetCache));
603 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
604 i++) {
605 glyphsetCache[i].next = i + 1;
606 glyphsetCache[i].count = -1;
608 glyphsetCache[i-1].next = -1;
609 glyphsetCacheSize += INIT_CACHE_SIZE;
611 lastfree = glyphsetCache[best].next;
612 glyphsetCache[best].count = 1;
613 glyphsetCache[best].next = mru;
614 mru = best;
615 TRACE("new free cache slot at %d\n", mru);
616 return mru;
619 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
621 DWORD size;
622 WORD *gasp, *buffer;
623 WORD num_recs;
624 DWORD ppem;
625 TEXTMETRICW tm;
627 *flags = 0;
629 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
630 if(size == GDI_ERROR)
631 return FALSE;
633 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
634 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
636 GetTextMetricsW(physDev->hdc, &tm);
637 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
639 gasp++;
640 num_recs = get_be_word(*gasp);
641 gasp++;
642 while(num_recs--)
644 *flags = get_be_word(*(gasp + 1));
645 if(ppem <= get_be_word(*gasp))
646 break;
647 gasp += 2;
649 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
651 HeapFree(GetProcessHeap(), 0, buffer);
652 return TRUE;
655 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
657 AA_Type ret;
658 WORD flags;
659 UINT font_smoothing_type, font_smoothing_orientation;
661 if (X11DRV_XRender_Installed && subpixel &&
662 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
663 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
665 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
666 &font_smoothing_orientation, 0) &&
667 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
669 ret = AA_BGR;
671 else
672 ret = AA_RGB;
673 /*FIXME
674 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
675 But, Wine's subpixel rendering can support the portrait mode.
678 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
679 ret = AA_Grey;
680 else
681 ret = AA_None;
683 return ret;
686 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
688 int ret;
689 int format;
690 gsCacheEntry *entry;
691 static int hinter = -1;
692 static int subpixel = -1;
693 BOOL font_smoothing;
695 if((ret = LookupEntry(plfsz)) != -1) return ret;
697 ret = AllocEntry();
698 entry = glyphsetCache + ret;
699 entry->lfsz = *plfsz;
700 for( format = 0; format < AA_MAXVALUE; format++ ) {
701 assert( !entry->format[format] );
704 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
706 if(hinter == -1 || subpixel == -1)
708 RASTERIZER_STATUS status;
709 GetRasterizerCaps(&status, sizeof(status));
710 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
711 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
714 switch (plfsz->lf.lfQuality)
716 case ANTIALIASED_QUALITY:
717 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
718 break;
719 case CLEARTYPE_QUALITY:
720 case CLEARTYPE_NATURAL_QUALITY:
721 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
722 break;
723 case DEFAULT_QUALITY:
724 case DRAFT_QUALITY:
725 case PROOF_QUALITY:
726 default:
727 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
728 font_smoothing)
730 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
732 else
733 entry->aa_default = AA_None;
734 break;
737 else
738 entry->aa_default = AA_None;
740 return ret;
743 static void dec_ref_cache(int index)
745 assert(index >= 0);
746 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
747 assert(glyphsetCache[index].count > 0);
748 glyphsetCache[index].count--;
751 static void lfsz_calc_hash(LFANDSIZE *plfsz)
753 DWORD hash = 0, *ptr, two_chars;
754 WORD *pwc;
755 int i;
757 hash ^= plfsz->devsize.cx;
758 hash ^= plfsz->devsize.cy;
759 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
760 hash ^= *ptr;
761 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
762 hash ^= *ptr;
763 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
764 two_chars = *ptr;
765 pwc = (WCHAR *)&two_chars;
766 if(!*pwc) break;
767 *pwc = toupperW(*pwc);
768 pwc++;
769 *pwc = toupperW(*pwc);
770 hash ^= two_chars;
771 if(!*pwc) break;
773 plfsz->hash = hash;
774 return;
777 /***********************************************************************
778 * X11DRV_XRender_Finalize
780 void X11DRV_XRender_Finalize(void)
782 int i;
784 EnterCriticalSection(&xrender_cs);
785 for(i = mru; i >= 0; i = glyphsetCache[i].next)
786 FreeEntry(i);
787 LeaveCriticalSection(&xrender_cs);
791 /***********************************************************************
792 * X11DRV_XRender_SelectFont
794 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
796 LFANDSIZE lfsz;
798 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
799 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
800 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
801 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
802 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
803 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
804 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
805 GetWorldTransform( physDev->hdc, &lfsz.xform );
806 lfsz_calc_hash(&lfsz);
808 EnterCriticalSection(&xrender_cs);
809 if(!physDev->xrender) {
810 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
811 sizeof(*physDev->xrender));
812 physDev->xrender->cache_index = -1;
814 else if(physDev->xrender->cache_index != -1)
815 dec_ref_cache(physDev->xrender->cache_index);
816 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
817 LeaveCriticalSection(&xrender_cs);
818 return 0;
821 /***********************************************************************
822 * X11DRV_XRender_DeleteDC
824 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
826 X11DRV_XRender_UpdateDrawable(physDev);
828 EnterCriticalSection(&xrender_cs);
829 if(physDev->xrender->cache_index != -1)
830 dec_ref_cache(physDev->xrender->cache_index);
831 LeaveCriticalSection(&xrender_cs);
833 HeapFree(GetProcessHeap(), 0, physDev->xrender);
834 physDev->xrender = NULL;
835 return;
838 /***********************************************************************
839 * X11DRV_XRender_UpdateDrawable
841 * Deletes the pict and tile when the drawable changes.
843 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
845 wine_tsx11_lock();
847 if(physDev->xrender->pict)
849 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
850 XFlush(gdi_display);
851 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
852 physDev->xrender->pict = 0;
854 wine_tsx11_unlock();
856 return;
859 /************************************************************************
860 * UploadGlyph
862 * Helper to ExtTextOut. Must be called inside xrender_cs
864 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
866 unsigned int buflen;
867 char *buf;
868 Glyph gid;
869 GLYPHMETRICS gm;
870 XGlyphInfo gi;
871 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
872 gsCacheEntryFormat *formatEntry;
873 UINT ggo_format = GGO_GLYPH_INDEX;
874 WXRFormat wxr_format;
875 static const char zero[4];
876 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
878 switch(format) {
879 case AA_Grey:
880 ggo_format |= WINE_GGO_GRAY16_BITMAP;
881 break;
882 case AA_RGB:
883 ggo_format |= WINE_GGO_HRGB_BITMAP;
884 break;
885 case AA_BGR:
886 ggo_format |= WINE_GGO_HBGR_BITMAP;
887 break;
888 case AA_VRGB:
889 ggo_format |= WINE_GGO_VRGB_BITMAP;
890 break;
891 case AA_VBGR:
892 ggo_format |= WINE_GGO_VBGR_BITMAP;
893 break;
895 default:
896 ERR("aa = %d - not implemented\n", format);
897 case AA_None:
898 ggo_format |= GGO_BITMAP;
899 break;
902 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
903 if(buflen == GDI_ERROR) {
904 if(format != AA_None) {
905 format = AA_None;
906 entry->aa_default = AA_None;
907 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
908 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
910 if(buflen == GDI_ERROR) {
911 WARN("GetGlyphOutlineW failed\n");
912 return FALSE;
914 TRACE("Turning off antialiasing for this monochrome font\n");
917 /* If there is nothing for the current type, we create the entry. */
918 if( !entry->format[format] ) {
919 entry->format[format] = HeapAlloc(GetProcessHeap(),
920 HEAP_ZERO_MEMORY,
921 sizeof(gsCacheEntryFormat));
923 formatEntry = entry->format[format];
925 if(formatEntry->nrealized <= glyph) {
926 formatEntry->nrealized = (glyph / 128 + 1) * 128;
928 if (formatEntry->realized)
929 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
930 HEAP_ZERO_MEMORY,
931 formatEntry->realized,
932 formatEntry->nrealized * sizeof(BOOL));
933 else
934 formatEntry->realized = HeapAlloc(GetProcessHeap(),
935 HEAP_ZERO_MEMORY,
936 formatEntry->nrealized * sizeof(BOOL));
938 if(!X11DRV_XRender_Installed) {
939 if (formatEntry->bitmaps)
940 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
941 HEAP_ZERO_MEMORY,
942 formatEntry->bitmaps,
943 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
944 else
945 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
946 HEAP_ZERO_MEMORY,
947 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
949 if (formatEntry->gis)
950 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
951 HEAP_ZERO_MEMORY,
952 formatEntry->gis,
953 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
954 else
955 formatEntry->gis = HeapAlloc(GetProcessHeap(),
956 HEAP_ZERO_MEMORY,
957 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
961 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
962 switch(format) {
963 case AA_Grey:
964 wxr_format = WXR_FORMAT_GRAY;
965 break;
967 case AA_RGB:
968 case AA_BGR:
969 case AA_VRGB:
970 case AA_VBGR:
971 wxr_format = WXR_FORMAT_A8R8G8B8;
972 break;
974 default:
975 ERR("aa = %d - not implemented\n", format);
976 case AA_None:
977 wxr_format = WXR_FORMAT_MONO;
978 break;
981 wine_tsx11_lock();
982 formatEntry->font_format = get_xrender_format(wxr_format);
983 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
984 wine_tsx11_unlock();
988 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
989 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
990 formatEntry->realized[glyph] = TRUE;
992 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
993 buflen,
994 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
995 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
997 gi.width = gm.gmBlackBoxX;
998 gi.height = gm.gmBlackBoxY;
999 gi.x = -gm.gmptGlyphOrigin.x;
1000 gi.y = gm.gmptGlyphOrigin.y;
1001 gi.xOff = gm.gmCellIncX;
1002 gi.yOff = gm.gmCellIncY;
1004 if(TRACE_ON(xrender)) {
1005 int pitch, i, j;
1006 char output[300];
1007 unsigned char *line;
1009 if(format == AA_None) {
1010 pitch = ((gi.width + 31) / 32) * 4;
1011 for(i = 0; i < gi.height; i++) {
1012 line = (unsigned char*) buf + i * pitch;
1013 output[0] = '\0';
1014 for(j = 0; j < pitch * 8; j++) {
1015 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1017 TRACE("%s\n", output);
1019 } else {
1020 static const char blks[] = " .:;!o*#";
1021 char str[2];
1023 str[1] = '\0';
1024 pitch = ((gi.width + 3) / 4) * 4;
1025 for(i = 0; i < gi.height; i++) {
1026 line = (unsigned char*) buf + i * pitch;
1027 output[0] = '\0';
1028 for(j = 0; j < pitch; j++) {
1029 str[0] = blks[line[j] >> 5];
1030 strcat(output, str);
1032 TRACE("%s\n", output);
1038 if(formatEntry->glyphset) {
1039 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1040 unsigned char *byte = (unsigned char*) buf, c;
1041 int i = buflen;
1043 while(i--) {
1044 c = *byte;
1046 /* magic to flip bit order */
1047 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1048 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1049 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1051 *byte++ = c;
1054 else if ( format != AA_Grey &&
1055 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1057 unsigned int i, *data = (unsigned int *)buf;
1058 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1060 gid = glyph;
1063 XRenderCompositeText seems to ignore 0x0 glyphs when
1064 AA_None, which means we lose the advance width of glyphs
1065 like the space. We'll pretend that such glyphs are 1x1
1066 bitmaps.
1069 if(buflen == 0)
1070 gi.width = gi.height = 1;
1072 wine_tsx11_lock();
1073 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1074 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1075 wine_tsx11_unlock();
1076 HeapFree(GetProcessHeap(), 0, buf);
1077 } else {
1078 formatEntry->bitmaps[glyph] = buf;
1081 formatEntry->gis[glyph] = gi;
1083 return TRUE;
1086 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1087 void *bitmap, XGlyphInfo *gi)
1089 unsigned char *srcLine = bitmap, *src;
1090 unsigned char bits, bitsMask;
1091 int width = gi->width;
1092 int stride = ((width + 31) & ~31) >> 3;
1093 int height = gi->height;
1094 int w;
1095 int xspan, lenspan;
1097 TRACE("%d, %d\n", x, y);
1098 x -= gi->x;
1099 y -= gi->y;
1100 while (height--)
1102 src = srcLine;
1103 srcLine += stride;
1104 w = width;
1106 bitsMask = 0x80; /* FreeType is always MSB first */
1107 bits = *src++;
1109 xspan = x;
1110 while (w)
1112 if (bits & bitsMask)
1114 lenspan = 0;
1117 lenspan++;
1118 if (lenspan == w)
1119 break;
1120 bitsMask = bitsMask >> 1;
1121 if (!bitsMask)
1123 bits = *src++;
1124 bitsMask = 0x80;
1126 } while (bits & bitsMask);
1127 XFillRectangle (gdi_display, physDev->drawable,
1128 physDev->gc, xspan, y, lenspan, 1);
1129 xspan += lenspan;
1130 w -= lenspan;
1132 else
1136 w--;
1137 xspan++;
1138 if (!w)
1139 break;
1140 bitsMask = bitsMask >> 1;
1141 if (!bitsMask)
1143 bits = *src++;
1144 bitsMask = 0x80;
1146 } while (!(bits & bitsMask));
1149 y++;
1153 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1154 void *bitmap, XGlyphInfo *gi)
1156 unsigned char *srcLine = bitmap, *src, bits;
1157 int width = gi->width;
1158 int stride = ((width + 3) & ~3);
1159 int height = gi->height;
1160 int w;
1161 int xspan, lenspan;
1163 x -= gi->x;
1164 y -= gi->y;
1165 while (height--)
1167 src = srcLine;
1168 srcLine += stride;
1169 w = width;
1171 bits = *src++;
1172 xspan = x;
1173 while (w)
1175 if (bits >= 0x80)
1177 lenspan = 0;
1180 lenspan++;
1181 if (lenspan == w)
1182 break;
1183 bits = *src++;
1184 } while (bits >= 0x80);
1185 XFillRectangle (gdi_display, physDev->drawable,
1186 physDev->gc, xspan, y, lenspan, 1);
1187 xspan += lenspan;
1188 w -= lenspan;
1190 else
1194 w--;
1195 xspan++;
1196 if (!w)
1197 break;
1198 bits = *src++;
1199 } while (bits < 0x80);
1202 y++;
1207 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1209 int s, l;
1211 s = 0;
1212 while ((mask & 1) == 0)
1214 mask >>= 1;
1215 s++;
1217 l = 0;
1218 while ((mask & 1) == 1)
1220 mask >>= 1;
1221 l++;
1223 *shift = s;
1224 *len = l;
1227 static DWORD GetField (DWORD pixel, int shift, int len)
1229 pixel = pixel & (((1 << (len)) - 1) << shift);
1230 pixel = pixel << (32 - (shift + len)) >> 24;
1231 while (len < 8)
1233 pixel |= (pixel >> len);
1234 len <<= 1;
1236 return pixel;
1240 static DWORD PutField (DWORD pixel, int shift, int len)
1242 shift = shift - (8 - len);
1243 if (len <= 8)
1244 pixel &= (((1 << len) - 1) << (8 - len));
1245 if (shift < 0)
1246 pixel >>= -shift;
1247 else
1248 pixel <<= shift;
1249 return pixel;
1252 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1253 int color)
1255 int r_shift, r_len;
1256 int g_shift, g_len;
1257 int b_shift, b_len;
1258 BYTE *maskLine, *mask, m;
1259 int maskStride;
1260 DWORD pixel;
1261 int width, height;
1262 int w, tx;
1263 BYTE src_r, src_g, src_b;
1265 x -= gi->x;
1266 y -= gi->y;
1267 width = gi->width;
1268 height = gi->height;
1270 maskLine = bitmap;
1271 maskStride = (width + 3) & ~3;
1273 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1274 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1275 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1277 src_r = GetField(color, r_shift, r_len);
1278 src_g = GetField(color, g_shift, g_len);
1279 src_b = GetField(color, b_shift, b_len);
1281 for(; height--; y++)
1283 mask = maskLine;
1284 maskLine += maskStride;
1285 w = width;
1286 tx = x;
1288 if(y < 0) continue;
1289 if(y >= image->height) break;
1291 for(; w--; tx++)
1293 if(tx >= image->width) break;
1295 m = *mask++;
1296 if(tx < 0) continue;
1298 if (m == 0xff)
1299 XPutPixel (image, tx, y, color);
1300 else if (m)
1302 BYTE r, g, b;
1304 pixel = XGetPixel (image, tx, y);
1306 r = GetField(pixel, r_shift, r_len);
1307 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1308 g = GetField(pixel, g_shift, g_len);
1309 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1310 b = GetField(pixel, b_shift, b_len);
1311 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1313 pixel = (PutField (r, r_shift, r_len) |
1314 PutField (g, g_shift, g_len) |
1315 PutField (b, b_shift, b_len));
1316 XPutPixel (image, tx, y, pixel);
1322 /*************************************************************
1323 * get_tile_pict
1325 * Returns an appropriate Picture for tiling the text colour.
1326 * Call and use result within the xrender_cs
1328 static Picture get_tile_pict(WineXRenderFormat *wxr_format, int text_pixel)
1330 static struct
1332 Pixmap xpm;
1333 Picture pict;
1334 int current_color;
1335 } tiles[MAX_FORMATS], *tile;
1336 XRenderColor col;
1338 tile = &tiles[wxr_format->format];
1340 if(!tile->xpm)
1342 XRenderPictureAttributes pa;
1344 wine_tsx11_lock();
1345 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1347 pa.repeat = RepeatNormal;
1348 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1349 wine_tsx11_unlock();
1351 /* init current_color to something different from text_pixel */
1352 tile->current_color = ~text_pixel;
1354 if(wxr_format->format == WXR_FORMAT_MONO)
1356 /* for a 1bpp bitmap we always need a 1 in the tile */
1357 col.red = col.green = col.blue = 0;
1358 col.alpha = 0xffff;
1359 wine_tsx11_lock();
1360 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1361 wine_tsx11_unlock();
1365 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1367 get_xrender_color(wxr_format, text_pixel, &col);
1368 wine_tsx11_lock();
1369 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1370 wine_tsx11_unlock();
1371 tile->current_color = text_pixel;
1373 return tile->pict;
1376 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1378 return 1;
1381 /***********************************************************************
1382 * X11DRV_XRender_ExtTextOut
1384 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1385 const RECT *lprect, LPCWSTR wstr, UINT count,
1386 const INT *lpDx )
1388 RGNDATA *data;
1389 XGCValues xgcval;
1390 gsCacheEntry *entry;
1391 gsCacheEntryFormat *formatEntry;
1392 BOOL retv = FALSE;
1393 HDC hdc = physDev->hdc;
1394 int textPixel, backgroundPixel;
1395 HRGN saved_region = 0;
1396 BOOL disable_antialias = FALSE;
1397 AA_Type aa_type = AA_None;
1398 DIBSECTION bmp;
1399 unsigned int idx;
1400 double cosEsc, sinEsc;
1401 LOGFONTW lf;
1402 WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1403 Picture tile_pict = 0;
1405 /* Do we need to disable antialiasing because of palette mode? */
1406 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1407 TRACE("bitmap is not a DIB\n");
1409 else if (bmp.dsBmih.biBitCount <= 8) {
1410 TRACE("Disabling antialiasing\n");
1411 disable_antialias = TRUE;
1414 xgcval.function = GXcopy;
1415 xgcval.background = physDev->backgroundPixel;
1416 xgcval.fill_style = FillSolid;
1417 wine_tsx11_lock();
1418 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1419 wine_tsx11_unlock();
1421 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1423 if(physDev->depth == 1) {
1424 if((physDev->textPixel & 0xffffff) == 0) {
1425 textPixel = 0;
1426 backgroundPixel = 1;
1427 } else {
1428 textPixel = 1;
1429 backgroundPixel = 0;
1431 } else {
1432 textPixel = physDev->textPixel;
1433 backgroundPixel = physDev->backgroundPixel;
1436 if(flags & ETO_OPAQUE)
1438 wine_tsx11_lock();
1439 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1440 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1441 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1442 lprect->right - lprect->left, lprect->bottom - lprect->top );
1443 wine_tsx11_unlock();
1446 if(count == 0)
1448 retv = TRUE;
1449 goto done_unlock;
1453 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1454 if(lf.lfEscapement != 0) {
1455 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1456 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1457 } else {
1458 cosEsc = 1;
1459 sinEsc = 0;
1462 if (flags & ETO_CLIPPED)
1464 HRGN clip_region;
1466 clip_region = CreateRectRgnIndirect( lprect );
1467 /* make a copy of the current device region */
1468 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1469 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1470 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1471 DeleteObject( clip_region );
1474 if(X11DRV_XRender_Installed) {
1475 if(!physDev->xrender->pict) {
1476 XRenderPictureAttributes pa;
1477 pa.subwindow_mode = IncludeInferiors;
1479 wine_tsx11_lock();
1480 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1481 physDev->drawable, dst_format->pict_format,
1482 CPSubwindowMode, &pa);
1483 wine_tsx11_unlock();
1485 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1486 physDev->xrender->pict, hdc, physDev->drawable);
1487 } else {
1488 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1489 physDev->xrender->pict, hdc, physDev->drawable);
1492 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1494 wine_tsx11_lock();
1495 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1496 physDev->dc_rect.left, physDev->dc_rect.top,
1497 (XRectangle *)data->Buffer, data->rdh.nCount );
1498 wine_tsx11_unlock();
1499 HeapFree( GetProcessHeap(), 0, data );
1503 EnterCriticalSection(&xrender_cs);
1505 entry = glyphsetCache + physDev->xrender->cache_index;
1506 if( disable_antialias == FALSE )
1507 aa_type = entry->aa_default;
1508 formatEntry = entry->format[aa_type];
1510 for(idx = 0; idx < count; idx++) {
1511 if( !formatEntry ) {
1512 UploadGlyph(physDev, wstr[idx], aa_type);
1513 /* re-evaluate antialias since aa_default may have changed */
1514 if( disable_antialias == FALSE )
1515 aa_type = entry->aa_default;
1516 formatEntry = entry->format[aa_type];
1517 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1518 UploadGlyph(physDev, wstr[idx], aa_type);
1521 if (!formatEntry)
1523 WARN("could not upload requested glyphs\n");
1524 LeaveCriticalSection(&xrender_cs);
1525 goto done_unlock;
1528 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1529 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1531 if(X11DRV_XRender_Installed)
1533 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1534 INT offset = 0;
1535 POINT desired, current;
1536 int render_op = PictOpOver;
1538 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1539 So we pass zeros to the function and move to our starting position using the first
1540 element of the elts array. */
1542 desired.x = physDev->dc_rect.left + x;
1543 desired.y = physDev->dc_rect.top + y;
1544 current.x = current.y = 0;
1546 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1548 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1550 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1551 render_op = PictOpOutReverse; /* This gives us 'black' text */
1553 for(idx = 0; idx < count; idx++)
1555 elts[idx].glyphset = formatEntry->glyphset;
1556 elts[idx].chars = wstr + idx;
1557 elts[idx].nchars = 1;
1558 elts[idx].xOff = desired.x - current.x;
1559 elts[idx].yOff = desired.y - current.y;
1561 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1562 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1564 if(!lpDx)
1566 desired.x += formatEntry->gis[wstr[idx]].xOff;
1567 desired.y += formatEntry->gis[wstr[idx]].yOff;
1569 else
1571 offset += lpDx[idx];
1572 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1573 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1576 wine_tsx11_lock();
1577 pXRenderCompositeText16(gdi_display, render_op,
1578 tile_pict,
1579 physDev->xrender->pict,
1580 formatEntry->font_format->pict_format,
1581 0, 0, 0, 0, elts, count);
1582 wine_tsx11_unlock();
1583 HeapFree(GetProcessHeap(), 0, elts);
1584 } else {
1585 INT offset = 0, xoff = 0, yoff = 0;
1586 wine_tsx11_lock();
1587 XSetForeground( gdi_display, physDev->gc, textPixel );
1589 if(aa_type == AA_None || physDev->depth == 1)
1591 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1593 if(aa_type == AA_None)
1594 sharp_glyph_fn = SharpGlyphMono;
1595 else
1596 sharp_glyph_fn = SharpGlyphGray;
1598 for(idx = 0; idx < count; idx++) {
1599 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1600 physDev->dc_rect.top + y + yoff,
1601 formatEntry->bitmaps[wstr[idx]],
1602 &formatEntry->gis[wstr[idx]]);
1603 if(lpDx) {
1604 offset += lpDx[idx];
1605 xoff = offset * cosEsc;
1606 yoff = offset * -sinEsc;
1607 } else {
1608 xoff += formatEntry->gis[wstr[idx]].xOff;
1609 yoff += formatEntry->gis[wstr[idx]].yOff;
1612 } else {
1613 XImage *image;
1614 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1615 RECT extents = {0, 0, 0, 0};
1616 POINT cur = {0, 0};
1617 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1618 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1620 TRACE("drawable %dx%d\n", w, h);
1622 for(idx = 0; idx < count; idx++) {
1623 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1624 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1625 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1626 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1627 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1628 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1629 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1630 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1631 if(lpDx) {
1632 offset += lpDx[idx];
1633 cur.x = offset * cosEsc;
1634 cur.y = offset * -sinEsc;
1635 } else {
1636 cur.x += formatEntry->gis[wstr[idx]].xOff;
1637 cur.y += formatEntry->gis[wstr[idx]].yOff;
1640 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1641 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1643 if(physDev->dc_rect.left + x + extents.left >= 0) {
1644 image_x = physDev->dc_rect.left + x + extents.left;
1645 image_off_x = 0;
1646 } else {
1647 image_x = 0;
1648 image_off_x = physDev->dc_rect.left + x + extents.left;
1650 if(physDev->dc_rect.top + y + extents.top >= 0) {
1651 image_y = physDev->dc_rect.top + y + extents.top;
1652 image_off_y = 0;
1653 } else {
1654 image_y = 0;
1655 image_off_y = physDev->dc_rect.top + y + extents.top;
1657 if(physDev->dc_rect.left + x + extents.right < w)
1658 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1659 else
1660 image_w = w - image_x;
1661 if(physDev->dc_rect.top + y + extents.bottom < h)
1662 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1663 else
1664 image_h = h - image_y;
1666 if(image_w <= 0 || image_h <= 0) goto no_image;
1668 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1669 image = XGetImage(gdi_display, physDev->drawable,
1670 image_x, image_y, image_w, image_h,
1671 AllPlanes, ZPixmap);
1672 X11DRV_check_error();
1674 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1675 gdi_display, (int)physDev->drawable, image_x, image_y,
1676 image_w, image_h, AllPlanes, ZPixmap,
1677 physDev->depth, image);
1678 if(!image) {
1679 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1680 physDev->depth);
1681 GC gc;
1682 XGCValues gcv;
1684 gcv.graphics_exposures = False;
1685 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1686 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1687 image_w, image_h, 0, 0);
1688 XFreeGC(gdi_display, gc);
1689 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1690 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1691 ZPixmap);
1692 X11DRV_check_error();
1693 XFreePixmap(gdi_display, xpm);
1695 if(!image) goto no_image;
1697 image->red_mask = visual->red_mask;
1698 image->green_mask = visual->green_mask;
1699 image->blue_mask = visual->blue_mask;
1701 offset = xoff = yoff = 0;
1702 for(idx = 0; idx < count; idx++) {
1703 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1704 yoff + image_off_y - extents.top,
1705 formatEntry->bitmaps[wstr[idx]],
1706 &formatEntry->gis[wstr[idx]],
1707 physDev->textPixel);
1708 if(lpDx) {
1709 offset += lpDx[idx];
1710 xoff = offset * cosEsc;
1711 yoff = offset * -sinEsc;
1712 } else {
1713 xoff += formatEntry->gis[wstr[idx]].xOff;
1714 yoff += formatEntry->gis[wstr[idx]].yOff;
1717 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1718 image_x, image_y, image_w, image_h);
1719 XDestroyImage(image);
1721 no_image:
1722 wine_tsx11_unlock();
1724 LeaveCriticalSection(&xrender_cs);
1726 if (flags & ETO_CLIPPED)
1728 /* restore the device region */
1729 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1730 DeleteObject( saved_region );
1733 retv = TRUE;
1735 done_unlock:
1736 X11DRV_UnlockDIBSection( physDev, TRUE );
1737 return retv;
1740 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
1741 static void set_xrender_transformation(Picture src_pict, float xscale, float yscale, int xoffset, int yoffset)
1743 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1744 XTransform xform = {{
1745 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
1746 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
1747 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1750 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1751 #endif
1754 /* Helper function for (stretched) blitting using xrender */
1755 static void xrender_blit(Picture src_pict, Picture mask_pict, Picture dst_pict, int x_src, int y_src, float xscale, float yscale, int width, int height)
1757 /* Further down a transformation matrix is used for stretching and mirroring the source data.
1758 * xscale/yscale contain the scaling factors for the width and height. In case of mirroring
1759 * we also need a x- and y-offset because without the pixels will be in the wrong quadrant of the x-y plane.
1761 int x_offset = (xscale<0) ? width : 0;
1762 int y_offset = (yscale<0) ? height : 0;
1764 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1765 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1766 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1767 if(xscale != 1.0 || yscale != 1.0)
1769 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1770 if(mask_pict)
1771 set_xrender_transformation(mask_pict, xscale, yscale, x_offset, y_offset);
1772 else
1773 set_xrender_transformation(src_pict, xscale, yscale, x_src + x_offset, y_src + y_offset);
1775 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, 0, 0, 0, 0, 0, 0, width, height);
1777 else
1779 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1780 if(mask_pict)
1781 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
1782 else
1783 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1785 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, x_src, y_src, 0, 0, 0, 0, width, height);
1789 /******************************************************************************
1790 * AlphaBlend (x11drv.@)
1792 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1793 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1794 BLENDFUNCTION blendfn)
1796 XRenderPictureAttributes pa;
1797 Picture dst_pict, src_pict;
1798 Pixmap xpm;
1799 DIBSECTION dib;
1800 XImage *image;
1801 GC gc;
1802 XGCValues gcv;
1803 DWORD *dstbits, *data;
1804 int y, y2;
1805 POINT pts[2];
1806 BOOL top_down = FALSE;
1807 RGNDATA *rgndata;
1808 WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(devDst->depth, devDst->color_shifts);
1809 WineXRenderFormat *src_format;
1810 int repeat_src;
1812 if(!X11DRV_XRender_Installed) {
1813 FIXME("Unable to AlphaBlend without Xrender\n");
1814 return FALSE;
1816 pts[0].x = xDst;
1817 pts[0].y = yDst;
1818 pts[1].x = xDst + widthDst;
1819 pts[1].y = yDst + heightDst;
1820 LPtoDP(devDst->hdc, pts, 2);
1821 xDst = pts[0].x;
1822 yDst = pts[0].y;
1823 widthDst = pts[1].x - pts[0].x;
1824 heightDst = pts[1].y - pts[0].y;
1826 pts[0].x = xSrc;
1827 pts[0].y = ySrc;
1828 pts[1].x = xSrc + widthSrc;
1829 pts[1].y = ySrc + heightSrc;
1830 LPtoDP(devSrc->hdc, pts, 2);
1831 xSrc = pts[0].x;
1832 ySrc = pts[0].y;
1833 widthSrc = pts[1].x - pts[0].x;
1834 heightSrc = pts[1].y - pts[0].y;
1835 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1837 /* If the source is a 1x1 bitmap, tiling is equivalent to stretching, but
1838 tiling is much faster. Therefore, we do no stretching in this case. */
1839 repeat_src = widthSrc == 1 && heightSrc == 1;
1841 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1842 if((widthDst != widthSrc || heightDst != heightSrc) && !repeat_src)
1843 #else
1844 if(!pXRenderSetPictureTransform && !repeat_src)
1845 #endif
1847 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1848 return FALSE;
1851 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1853 static BOOL out = FALSE;
1854 if (!out)
1856 FIXME("not a dibsection\n");
1857 out = TRUE;
1859 return FALSE;
1862 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1863 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1865 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1866 SetLastError(ERROR_INVALID_PARAMETER);
1867 return FALSE;
1870 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1871 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1873 if(dib.dsBm.bmBitsPixel != 32) {
1874 FIXME("not a 32 bpp dibsection\n");
1875 return FALSE;
1877 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1879 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1880 top_down = TRUE;
1881 dstbits += widthSrc * (heightSrc - 1);
1882 y2 = ySrc;
1883 y = y2 + heightSrc - 1;
1885 else
1887 y = dib.dsBmih.biHeight - ySrc - 1;
1888 y2 = y - heightSrc + 1;
1891 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1893 for(; y >= y2; y--)
1895 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1896 widthSrc * 4);
1897 dstbits += (top_down ? -1 : 1) * widthSrc;
1900 else
1902 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1903 int x;
1905 for(; y >= y2; y--)
1907 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1908 for (x = 0; x < widthSrc; x++)
1910 DWORD argb = *srcbits++;
1911 argb = (argb & 0xffffff) | source_alpha;
1912 *dstbits++ = argb;
1914 if (top_down) /* we traversed the row forward so we should go back by two rows */
1915 dstbits -= 2 * widthSrc;
1920 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1922 wine_tsx11_lock();
1923 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1924 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1926 src_format = get_xrender_format(WXR_FORMAT_A8R8G8B8);
1927 TRACE("src_format %p\n", src_format);
1928 if(!src_format)
1930 WARN("Unable to find a picture format supporting alpha, make sure X is running at 24-bit\n");
1931 return FALSE;
1934 pa.subwindow_mode = IncludeInferiors;
1935 pa.repeat = repeat_src ? RepeatNormal : RepeatNone;
1937 /* FIXME use devDst->xrender->pict ? */
1938 dst_pict = pXRenderCreatePicture(gdi_display,
1939 devDst->drawable,
1940 dst_format->pict_format,
1941 CPSubwindowMode, &pa);
1942 TRACE("dst_pict %08lx\n", dst_pict);
1943 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1944 xpm = XCreatePixmap(gdi_display,
1945 root_window,
1946 widthSrc, heightSrc, 32);
1947 gcv.graphics_exposures = False;
1948 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1949 TRACE("xpm = %08lx\n", xpm);
1950 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1952 src_pict = pXRenderCreatePicture(gdi_display,
1953 xpm, src_format->pict_format,
1954 CPSubwindowMode|CPRepeat, &pa);
1955 TRACE("src_pict %08lx\n", src_pict);
1957 if (rgndata)
1959 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1960 devDst->dc_rect.left, devDst->dc_rect.top,
1961 (XRectangle *)rgndata->Buffer,
1962 rgndata->rdh.nCount );
1963 HeapFree( GetProcessHeap(), 0, rgndata );
1966 /* Make sure we ALWAYS set the transformation matrix even if we don't need to scale. The reason is
1967 * that later on we want to reuse pictures (it can bring a lot of extra performance) and each time
1968 * a different transformation matrix might have been used. */
1969 set_xrender_transformation(src_pict, widthSrc/(double)widthDst, heightSrc/(double)heightDst, 0, 0);
1970 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1971 0, 0, 0, 0,
1972 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1975 pXRenderFreePicture(gdi_display, src_pict);
1976 XFreePixmap(gdi_display, xpm);
1977 XFreeGC(gdi_display, gc);
1978 pXRenderFreePicture(gdi_display, dst_pict);
1979 image->data = NULL;
1980 XDestroyImage(image);
1982 wine_tsx11_unlock();
1983 HeapFree(GetProcessHeap(), 0, data);
1984 return TRUE;
1987 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
1989 /* At depths >1, the depth of physBitmap and physDev might not be the same e.g. the physbitmap might be a 16-bit DIB while the physdev uses 24-bit */
1990 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
1992 wine_tsx11_lock();
1993 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
1995 /* Use XCopyArea when the physBitmap and brush.pixmap have the same depth. */
1996 if(physBitmap->pixmap_depth == 1 || physDev->depth == physBitmap->pixmap_depth)
1998 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
1999 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2001 else /* We meed depth conversion */
2003 WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2004 WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2006 Picture src_pict, dst_pict;
2007 XRenderPictureAttributes pa;
2008 pa.subwindow_mode = IncludeInferiors;
2009 pa.repeat = RepeatNone;
2011 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2012 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2014 xrender_blit(src_pict, 0, dst_pict, 0, 0, 1.0, 1.0, width, height);
2015 pXRenderFreePicture(gdi_display, src_pict);
2016 pXRenderFreePicture(gdi_display, dst_pict);
2018 wine_tsx11_unlock();
2021 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2022 Pixmap pixmap, GC gc,
2023 INT widthSrc, INT heightSrc,
2024 INT widthDst, INT heightDst,
2025 RECT *visRectSrc, RECT *visRectDst )
2027 BOOL stretch = (widthSrc != widthDst) || (heightSrc != heightDst);
2028 int width = visRectDst->right - visRectDst->left;
2029 int height = visRectDst->bottom - visRectDst->top;
2030 int x_src = physDevSrc->dc_rect.left + visRectSrc->left;
2031 int y_src = physDevSrc->dc_rect.top + visRectSrc->top;
2032 WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physDevSrc->depth, physDevSrc->color_shifts);
2033 WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2034 Picture src_pict=0, dst_pict=0, mask_pict=0;
2036 double xscale = widthSrc/(double)widthDst;
2037 double yscale = heightSrc/(double)heightDst;
2039 XRenderPictureAttributes pa;
2040 pa.subwindow_mode = IncludeInferiors;
2041 pa.repeat = RepeatNone;
2043 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n", physDevSrc->depth, widthSrc, heightSrc, x_src, y_src);
2044 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, widthDst, heightDst);
2046 if(!X11DRV_XRender_Installed)
2048 TRACE("Not using XRender since it is not available or disabled\n");
2049 return FALSE;
2052 /* XRender can't handle palettes, so abort */
2053 if(X11DRV_PALETTE_XPixelToPalette)
2054 return FALSE;
2056 /* XRender is of no use in this case */
2057 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2058 return FALSE;
2060 /* Just use traditional X copy when the depths match and we don't need stretching */
2061 if((physDevSrc->depth == physDevDst->depth) && !stretch)
2063 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2064 wine_tsx11_lock();
2065 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2066 wine_tsx11_unlock();
2067 return TRUE;
2070 /* mono -> color */
2071 if(physDevSrc->depth == 1)
2073 XRenderColor col;
2074 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2076 /* We use the source drawable as a mask */
2077 wine_tsx11_lock();
2078 mask_pict = pXRenderCreatePicture(gdi_display, physDevSrc->drawable, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2080 /* Use backgroundPixel as the foreground color */
2081 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2083 /* Create a destination picture and fill it with textPixel color as the background color */
2084 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2085 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2087 xrender_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2089 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2090 if(mask_pict) pXRenderFreePicture(gdi_display, mask_pict);
2091 wine_tsx11_unlock();
2093 else /* color -> color but with different depths */
2095 wine_tsx11_lock();
2096 src_pict = pXRenderCreatePicture(gdi_display,
2097 physDevSrc->drawable, src_format->pict_format,
2098 CPSubwindowMode|CPRepeat, &pa);
2100 dst_pict = pXRenderCreatePicture(gdi_display,
2101 pixmap, dst_format->pict_format,
2102 CPSubwindowMode|CPRepeat, &pa);
2104 xrender_blit(src_pict, 0, dst_pict, x_src, y_src, xscale, yscale, width, height);
2106 if(src_pict) pXRenderFreePicture(gdi_display, src_pict);
2107 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2108 wine_tsx11_unlock();
2110 return TRUE;
2113 #else /* SONAME_LIBXRENDER */
2115 void X11DRV_XRender_Init(void)
2117 TRACE("XRender support not compiled in.\n");
2118 return;
2121 void X11DRV_XRender_Finalize(void)
2125 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2127 assert(0);
2128 return FALSE;
2131 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2133 assert(0);
2134 return;
2137 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2138 const RECT *lprect, LPCWSTR wstr, UINT count,
2139 const INT *lpDx )
2141 assert(0);
2142 return FALSE;
2145 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2147 assert(0);
2148 return;
2151 /******************************************************************************
2152 * AlphaBlend (x11drv.@)
2154 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
2155 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
2156 BLENDFUNCTION blendfn)
2158 FIXME("not supported - XRENDER headers were missing at compile time\n");
2159 return FALSE;
2162 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2164 wine_tsx11_lock();
2165 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2167 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2168 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2169 wine_tsx11_unlock();
2172 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2173 Pixmap pixmap, GC gc,
2174 INT widthSrc, INT heightSrc,
2175 INT widthDst, INT heightDst,
2176 RECT *visRectSrc, RECT *visRectDst )
2178 return FALSE;
2180 #endif /* SONAME_LIBXRENDER */