mfplat/tests: Mark some tests as broken on Win 8 and 10 v1507.
[wine.git] / dlls / winex11.drv / xrender.c
blob585299cba9e53619e6f6457e00ec569fe0f8d24d
1 /*
2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
5 * Copyright 2009 Roderick Colenbrander
6 * Copyright 2011 Alexandre Julliard
8 * Some parts also:
9 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #if 0
27 #pragma makedep unix
28 #endif
30 #include "config.h"
32 #include <assert.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <dlfcn.h>
38 #include "windef.h"
39 #include "winbase.h"
40 #include "x11drv.h"
41 #include "winternl.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
46 #ifdef SONAME_LIBXRENDER
48 WINE_DECLARE_DEBUG_CHANNEL(winediag);
50 #include <X11/Xlib.h>
51 #include <X11/extensions/Xrender.h>
53 #ifndef RepeatNone /* added in 0.10 */
54 #define RepeatNone 0
55 #define RepeatNormal 1
56 #define RepeatPad 2
57 #define RepeatReflect 3
58 #endif
60 enum wxr_format
62 WXR_FORMAT_MONO,
63 WXR_FORMAT_GRAY,
64 WXR_FORMAT_X1R5G5B5,
65 WXR_FORMAT_X1B5G5R5,
66 WXR_FORMAT_R5G6B5,
67 WXR_FORMAT_B5G6R5,
68 WXR_FORMAT_R8G8B8,
69 WXR_FORMAT_B8G8R8,
70 WXR_FORMAT_A8R8G8B8,
71 WXR_FORMAT_B8G8R8A8,
72 WXR_FORMAT_X8R8G8B8,
73 WXR_FORMAT_B8G8R8X8,
74 WXR_FORMAT_ROOT, /* placeholder for the format to use on the root window */
75 WXR_NB_FORMATS,
76 WXR_INVALID_FORMAT = WXR_NB_FORMATS
79 typedef struct wine_xrender_format_template
81 unsigned int depth;
82 unsigned int alpha;
83 unsigned int alphaMask;
84 unsigned int red;
85 unsigned int redMask;
86 unsigned int green;
87 unsigned int greenMask;
88 unsigned int blue;
89 unsigned int blueMask;
90 } WineXRenderFormatTemplate;
92 static const WineXRenderFormatTemplate wxr_formats_template[WXR_NB_FORMATS] =
94 /* Format depth alpha mask red mask green mask blue mask*/
95 /* WXR_FORMAT_MONO */ { 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
96 /* WXR_FORMAT_GRAY */ { 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
97 /* WXR_FORMAT_X1R5G5B5 */ { 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
98 /* WXR_FORMAT_X1B5G5R5 */ { 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
99 /* WXR_FORMAT_R5G6B5 */ { 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
100 /* WXR_FORMAT_B5G6R5 */ { 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
101 /* WXR_FORMAT_R8G8B8 */ { 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
102 /* WXR_FORMAT_B8G8R8 */ { 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
103 /* WXR_FORMAT_A8R8G8B8 */ { 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
104 /* WXR_FORMAT_B8G8R8A8 */ { 32, 0, 0xff, 8, 0xff, 16, 0xff, 24, 0xff },
105 /* WXR_FORMAT_X8R8G8B8 */ { 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
106 /* WXR_FORMAT_B8G8R8X8 */ { 32, 0, 0, 8, 0xff, 16, 0xff, 24, 0xff },
109 static enum wxr_format default_format = WXR_INVALID_FORMAT;
110 static XRenderPictFormat *pict_formats[WXR_NB_FORMATS + 1 /* invalid format */];
112 typedef struct
114 LOGFONTW lf;
115 XFORM xform;
116 SIZE devsize; /* size in device coords */
117 DWORD hash;
118 } LFANDSIZE;
120 #define INITIAL_REALIZED_BUF_SIZE 128
122 enum glyph_type { GLYPH_INDEX, GLYPH_WCHAR, GLYPH_NBTYPES };
124 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
126 typedef struct
128 GlyphSet glyphset;
129 XRenderPictFormat *font_format;
130 int nrealized;
131 BOOL *realized;
132 XGlyphInfo *gis;
133 } gsCacheEntryFormat;
135 typedef struct
137 LFANDSIZE lfsz;
138 gsCacheEntryFormat *format[GLYPH_NBTYPES][AA_MAXVALUE];
139 INT count;
140 INT next;
141 } gsCacheEntry;
143 struct xrender_physdev
145 struct gdi_physdev dev;
146 X11DRV_PDEVICE *x11dev;
147 HRGN region;
148 enum wxr_format format;
149 UINT aa_flags;
150 int cache_index;
151 BOOL update_clip;
152 Picture pict;
153 Picture pict_src;
154 XRenderPictFormat *pict_format;
157 static inline struct xrender_physdev *get_xrender_dev( PHYSDEV dev )
159 return (struct xrender_physdev *)dev;
162 static const struct gdi_dc_funcs xrender_funcs;
164 static gsCacheEntry *glyphsetCache = NULL;
165 static DWORD glyphsetCacheSize = 0;
166 static INT lastfree = -1;
167 static INT mru = -1;
169 #define INIT_CACHE_SIZE 10
171 static void *xrender_handle;
173 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
174 MAKE_FUNCPTR(XRenderAddGlyphs)
175 MAKE_FUNCPTR(XRenderChangePicture)
176 MAKE_FUNCPTR(XRenderComposite)
177 MAKE_FUNCPTR(XRenderCompositeText16)
178 MAKE_FUNCPTR(XRenderCreateGlyphSet)
179 MAKE_FUNCPTR(XRenderCreatePicture)
180 MAKE_FUNCPTR(XRenderFillRectangle)
181 MAKE_FUNCPTR(XRenderFindFormat)
182 MAKE_FUNCPTR(XRenderFindVisualFormat)
183 MAKE_FUNCPTR(XRenderFreeGlyphSet)
184 MAKE_FUNCPTR(XRenderFreePicture)
185 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
186 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
187 MAKE_FUNCPTR(XRenderCreateLinearGradient)
188 #endif
189 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
190 MAKE_FUNCPTR(XRenderSetPictureTransform)
191 #endif
192 MAKE_FUNCPTR(XRenderQueryExtension)
194 #undef MAKE_FUNCPTR
196 static pthread_mutex_t xrender_mutex = PTHREAD_MUTEX_INITIALIZER;
198 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
199 ( ( (ULONG)_x4 << 24 ) | \
200 ( (ULONG)_x3 << 16 ) | \
201 ( (ULONG)_x2 << 8 ) | \
202 (ULONG)_x1 )
204 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
206 #define GASP_GRIDFIT 0x01
207 #define GASP_DOGRAY 0x02
209 #ifdef WORDS_BIGENDIAN
210 #define get_be_word(x) (x)
211 #define NATIVE_BYTE_ORDER MSBFirst
212 #else
213 #define get_be_word(x) RtlUshortByteSwap(x)
214 #define NATIVE_BYTE_ORDER LSBFirst
215 #endif
217 static BOOL has_alpha( enum wxr_format format )
219 return (format == WXR_FORMAT_A8R8G8B8 || format == WXR_FORMAT_B8G8R8A8);
222 static enum wxr_format get_format_without_alpha( enum wxr_format format )
224 switch (format)
226 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
227 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
228 default: return format;
232 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
234 templ->id = 0;
235 templ->type = PictTypeDirect;
236 templ->depth = fmt->depth;
237 templ->direct.alpha = fmt->alpha;
238 templ->direct.alphaMask = fmt->alphaMask;
239 templ->direct.red = fmt->red;
240 templ->direct.redMask = fmt->redMask;
241 templ->direct.green = fmt->green;
242 templ->direct.greenMask = fmt->greenMask;
243 templ->direct.blue = fmt->blue;
244 templ->direct.blueMask = fmt->blueMask;
245 templ->colormap = 0;
247 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
249 return TRUE;
252 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
254 if(fmt->depth != default_visual.depth) return FALSE;
255 if( (fmt->redMask << fmt->red) != default_visual.red_mask) return FALSE;
256 if( (fmt->greenMask << fmt->green) != default_visual.green_mask) return FALSE;
257 if( (fmt->blueMask << fmt->blue) != default_visual.blue_mask) return FALSE;
259 /* We never select a default ARGB visual */
260 if(fmt->alphaMask) return FALSE;
261 return TRUE;
264 static int load_xrender_formats(void)
266 int count = 0;
267 unsigned int i;
269 for (i = 0; i < WXR_NB_FORMATS; i++)
271 XRenderPictFormat templ;
273 if (i == WXR_FORMAT_ROOT)
275 pict_formats[i] = pXRenderFindVisualFormat(gdi_display,
276 DefaultVisual( gdi_display, DefaultScreen(gdi_display) ));
277 TRACE( "Loaded root pict_format with id=%#lx\n", pict_formats[i]->id );
278 continue;
280 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
282 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, default_visual.visual);
283 if (!pict_formats[i])
285 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
286 if (default_visual.class == DirectColor)
288 XVisualInfo info;
289 if (XMatchVisualInfo( gdi_display, default_visual.screen,
290 default_visual.depth, TrueColor, &info ))
292 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, info.visual);
293 if (pict_formats[i]) default_visual = info;
297 if (pict_formats[i]) default_format = i;
299 else
301 unsigned long mask = 0;
302 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
303 pict_formats[i] = pXRenderFindFormat(gdi_display, mask, &templ, 0);
305 if (pict_formats[i])
307 count++;
308 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_formats[i]->id, i);
311 return count;
314 /***********************************************************************
315 * X11DRV_XRender_Init
317 * Let's see if our XServer has the extension available
320 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
322 int event_base, i;
324 if (!client_side_with_render) return NULL;
325 if (!(xrender_handle = dlopen(SONAME_LIBXRENDER, RTLD_NOW))) return NULL;
327 #define LOAD_FUNCPTR(f) if((p##f = dlsym(xrender_handle, #f)) == NULL) return NULL
328 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = dlsym(xrender_handle, #f)
329 LOAD_FUNCPTR(XRenderAddGlyphs);
330 LOAD_FUNCPTR(XRenderChangePicture);
331 LOAD_FUNCPTR(XRenderComposite);
332 LOAD_FUNCPTR(XRenderCompositeText16);
333 LOAD_FUNCPTR(XRenderCreateGlyphSet);
334 LOAD_FUNCPTR(XRenderCreatePicture);
335 LOAD_FUNCPTR(XRenderFillRectangle);
336 LOAD_FUNCPTR(XRenderFindFormat);
337 LOAD_FUNCPTR(XRenderFindVisualFormat);
338 LOAD_FUNCPTR(XRenderFreeGlyphSet);
339 LOAD_FUNCPTR(XRenderFreePicture);
340 LOAD_FUNCPTR(XRenderSetPictureClipRectangles);
341 LOAD_FUNCPTR(XRenderQueryExtension);
342 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
343 LOAD_OPTIONAL_FUNCPTR(XRenderCreateLinearGradient);
344 #endif
345 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
346 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform);
347 #endif
348 #undef LOAD_OPTIONAL_FUNCPTR
349 #undef LOAD_FUNCPTR
351 if (!pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) return NULL;
353 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
354 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
356 ERR_(winediag)("Wine has detected that you probably have a buggy version "
357 "of libXrender. Because of this client side font rendering "
358 "will be disabled. Please upgrade this library.\n");
359 return NULL;
362 if (!default_visual.red_mask || !default_visual.green_mask || !default_visual.blue_mask)
364 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
365 return NULL;
368 glyphsetCache = calloc( sizeof(*glyphsetCache), INIT_CACHE_SIZE );
370 glyphsetCacheSize = INIT_CACHE_SIZE;
371 lastfree = 0;
372 for(i = 0; i < INIT_CACHE_SIZE; i++) {
373 glyphsetCache[i].next = i + 1;
374 glyphsetCache[i].count = -1;
376 glyphsetCache[i-1].next = -1;
378 return &xrender_funcs;
381 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
382 static void get_xrender_color( struct xrender_physdev *physdev, COLORREF src_color, XRenderColor *dst_color )
384 if (src_color & (1 << 24)) /* PALETTEINDEX */
386 HPALETTE pal = NtGdiGetDCObject( physdev->dev.hdc, NTGDI_OBJ_PAL );
387 PALETTEENTRY pal_ent;
389 if (!get_palette_entries( pal, LOWORD(src_color), 1, &pal_ent ))
390 get_palette_entries( pal, 0, 1, &pal_ent );
391 dst_color->red = pal_ent.peRed * 257;
392 dst_color->green = pal_ent.peGreen * 257;
393 dst_color->blue = pal_ent.peBlue * 257;
395 else
397 if (src_color >> 16 == 0x10ff) src_color = 0; /* DIBINDEX */
399 dst_color->red = GetRValue( src_color ) * 257;
400 dst_color->green = GetGValue( src_color ) * 257;
401 dst_color->blue = GetBValue( src_color ) * 257;
404 if (physdev->format == WXR_FORMAT_MONO && !dst_color->red && !dst_color->green && !dst_color->blue)
405 dst_color->alpha = 0;
406 else
407 dst_color->alpha = 0xffff;
410 static enum wxr_format get_xrender_format_from_bitmapinfo( const BITMAPINFO *info )
412 if (info->bmiHeader.biPlanes != 1) return WXR_INVALID_FORMAT;
414 switch (info->bmiHeader.biBitCount)
416 case 1:
417 return WXR_FORMAT_MONO;
418 case 4:
419 case 8:
420 break;
421 case 24:
422 if (info->bmiHeader.biCompression != BI_RGB) break;
423 return WXR_FORMAT_R8G8B8;
424 case 16:
425 case 32:
426 if (info->bmiHeader.biCompression == BI_BITFIELDS)
428 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
429 unsigned int i;
431 for (i = 0; i < WXR_NB_FORMATS; i++)
433 if (info->bmiHeader.biBitCount == wxr_formats_template[i].depth &&
434 colors[0] == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
435 colors[1] == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
436 colors[2] == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue))
437 return i;
439 break;
441 if (info->bmiHeader.biCompression != BI_RGB) break;
442 return (info->bmiHeader.biBitCount == 16) ? WXR_FORMAT_X1R5G5B5 : WXR_FORMAT_A8R8G8B8;
444 return WXR_INVALID_FORMAT;
447 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
448 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
450 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
451 XTransform xform = {{
452 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
453 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
454 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
457 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
458 #endif
461 static void update_xrender_clipping( struct xrender_physdev *dev, HRGN rgn )
463 XRenderPictureAttributes pa;
464 RGNDATA *data;
466 if (!rgn)
468 pa.clip_mask = None;
469 pXRenderChangePicture( gdi_display, dev->pict, CPClipMask, &pa );
471 else if ((data = X11DRV_GetRegionData( rgn, 0 )))
473 pXRenderSetPictureClipRectangles( gdi_display, dev->pict,
474 dev->x11dev->dc_rect.left, dev->x11dev->dc_rect.top,
475 (XRectangle *)data->Buffer, data->rdh.nCount );
476 free( data );
481 static Picture get_xrender_picture( struct xrender_physdev *dev, HRGN clip_rgn, const RECT *clip_rect )
483 if (!dev->pict && dev->pict_format)
485 XRenderPictureAttributes pa;
487 pa.subwindow_mode = IncludeInferiors;
488 dev->pict = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
489 dev->pict_format, CPSubwindowMode, &pa );
490 TRACE( "Allocing pict=%lx dc=%p drawable=%08lx\n",
491 dev->pict, dev->dev.hdc, dev->x11dev->drawable );
492 dev->update_clip = (dev->region != 0);
495 if (clip_rect)
497 HRGN rgn = NtGdiCreateRectRgn( clip_rect->left, clip_rect->top, clip_rect->right, clip_rect->bottom );
498 if (clip_rgn) NtGdiCombineRgn( rgn, rgn, clip_rgn, RGN_AND );
499 if (dev->region) NtGdiCombineRgn( rgn, rgn, dev->region, RGN_AND );
500 update_xrender_clipping( dev, rgn );
501 NtGdiDeleteObjectApp( rgn );
503 else if (clip_rgn)
505 if (dev->region)
507 HRGN rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
508 NtGdiCombineRgn( rgn, clip_rgn, dev->region, RGN_AND );
509 update_xrender_clipping( dev, rgn );
510 NtGdiDeleteObjectApp( rgn );
512 else update_xrender_clipping( dev, clip_rgn );
514 else if (dev->update_clip) update_xrender_clipping( dev, dev->region );
516 dev->update_clip = (clip_rect || clip_rgn); /* have to update again if we are using a custom region */
517 return dev->pict;
520 static Picture get_xrender_picture_source( struct xrender_physdev *dev, BOOL repeat )
522 if (!dev->pict_src && dev->pict_format)
524 XRenderPictureAttributes pa;
526 pa.subwindow_mode = IncludeInferiors;
527 pa.repeat = repeat ? RepeatNormal : RepeatNone;
528 dev->pict_src = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
529 dev->pict_format, CPSubwindowMode|CPRepeat, &pa );
531 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
532 dev->pict_src, dev->dev.hdc, dev->x11dev->drawable, pa.repeat);
535 return dev->pict_src;
538 static void free_xrender_picture( struct xrender_physdev *dev )
540 if (dev->pict || dev->pict_src)
542 XFlush( gdi_display );
543 if (dev->pict)
545 TRACE("freeing pict = %lx dc = %p\n", dev->pict, dev->dev.hdc);
546 pXRenderFreePicture(gdi_display, dev->pict);
547 dev->pict = 0;
549 if(dev->pict_src)
551 TRACE("freeing pict = %lx dc = %p\n", dev->pict_src, dev->dev.hdc);
552 pXRenderFreePicture(gdi_display, dev->pict_src);
553 dev->pict_src = 0;
558 /* return a mask picture used to force alpha to 0 */
559 static Picture get_no_alpha_mask(void)
561 static Pixmap pixmap;
562 static Picture pict;
564 pthread_mutex_lock( &xrender_mutex );
565 if (!pict)
567 XRenderPictureAttributes pa;
568 XRenderColor col;
570 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
571 pa.repeat = RepeatNormal;
572 pa.component_alpha = True;
573 pict = pXRenderCreatePicture( gdi_display, pixmap, pict_formats[WXR_FORMAT_A8R8G8B8],
574 CPRepeat|CPComponentAlpha, &pa );
575 col.red = col.green = col.blue = 0xffff;
576 col.alpha = 0;
577 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
579 pthread_mutex_unlock( &xrender_mutex );
580 return pict;
583 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
585 if(p1->hash != p2->hash) return TRUE;
586 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
587 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
588 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
589 return wcsicmp( p1->lf.lfFaceName, p2->lf.lfFaceName );
592 static int LookupEntry(LFANDSIZE *plfsz)
594 int i, prev_i = -1;
596 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
597 TRACE("%d\n", i);
598 if(glyphsetCache[i].count == -1) break; /* reached free list so stop */
600 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
601 glyphsetCache[i].count++;
602 if(prev_i >= 0) {
603 glyphsetCache[prev_i].next = glyphsetCache[i].next;
604 glyphsetCache[i].next = mru;
605 mru = i;
607 TRACE("found font in cache %d\n", i);
608 return i;
610 prev_i = i;
612 TRACE("font not in cache\n");
613 return -1;
616 static void FreeEntry(int entry)
618 int type, format;
620 for (type = 0; type < GLYPH_NBTYPES; type++)
622 for(format = 0; format < AA_MAXVALUE; format++) {
623 gsCacheEntryFormat * formatEntry;
625 if( !glyphsetCache[entry].format[type][format] )
626 continue;
628 formatEntry = glyphsetCache[entry].format[type][format];
630 if(formatEntry->glyphset) {
631 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
632 formatEntry->glyphset = 0;
634 if(formatEntry->nrealized) {
635 free( formatEntry->realized );
636 formatEntry->realized = NULL;
637 free( formatEntry->gis );
638 formatEntry->gis = NULL;
639 formatEntry->nrealized = 0;
642 free( formatEntry );
643 glyphsetCache[entry].format[type][format] = NULL;
648 static int AllocEntry(void)
650 int best = -1, prev_best = -1, i, prev_i = -1;
652 if(lastfree >= 0) {
653 assert(glyphsetCache[lastfree].count == -1);
654 glyphsetCache[lastfree].count = 1;
655 best = lastfree;
656 lastfree = glyphsetCache[lastfree].next;
657 assert(best != mru);
658 glyphsetCache[best].next = mru;
659 mru = best;
661 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
662 return mru;
665 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
666 if(glyphsetCache[i].count == 0) {
667 best = i;
668 prev_best = prev_i;
670 prev_i = i;
673 if(best >= 0) {
674 TRACE("freeing unused glyphset at cache %d\n", best);
675 FreeEntry(best);
676 glyphsetCache[best].count = 1;
677 if(prev_best >= 0) {
678 glyphsetCache[prev_best].next = glyphsetCache[best].next;
679 glyphsetCache[best].next = mru;
680 mru = best;
681 } else {
682 assert(mru == best);
684 return mru;
687 TRACE("Growing cache\n");
689 glyphsetCache = realloc( glyphsetCache,
690 (glyphsetCacheSize + INIT_CACHE_SIZE) * sizeof(*glyphsetCache) );
692 for (best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE; i++)
694 memset( &glyphsetCache[i], 0, sizeof(glyphsetCache[i]) );
695 glyphsetCache[i].next = i + 1;
696 glyphsetCache[i].count = -1;
698 glyphsetCache[i-1].next = -1;
699 glyphsetCacheSize += INIT_CACHE_SIZE;
701 lastfree = glyphsetCache[best].next;
702 glyphsetCache[best].count = 1;
703 glyphsetCache[best].next = mru;
704 mru = best;
705 TRACE("new free cache slot at %d\n", mru);
706 return mru;
709 static int GetCacheEntry( LFANDSIZE *plfsz )
711 int ret;
712 gsCacheEntry *entry;
714 if((ret = LookupEntry(plfsz)) != -1) return ret;
716 ret = AllocEntry();
717 entry = glyphsetCache + ret;
718 entry->lfsz = *plfsz;
719 return ret;
722 static void dec_ref_cache(int index)
724 assert(index >= 0);
725 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
726 assert(glyphsetCache[index].count > 0);
727 glyphsetCache[index].count--;
730 static void lfsz_calc_hash(LFANDSIZE *plfsz)
732 DWORD hash = 0, *ptr, two_chars;
733 WORD *pwc;
734 unsigned int i;
736 hash ^= plfsz->devsize.cx;
737 hash ^= plfsz->devsize.cy;
738 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
739 hash ^= *ptr;
740 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
741 hash ^= *ptr;
742 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
743 two_chars = *ptr;
744 pwc = (WCHAR *)&two_chars;
745 if(!*pwc) break;
746 *pwc = RtlUpcaseUnicodeChar( *pwc );
747 pwc++;
748 *pwc = RtlUpcaseUnicodeChar( *pwc );
749 hash ^= two_chars;
750 if(!*pwc) break;
752 plfsz->hash = hash;
753 return;
756 static AA_Type aa_type_from_flags( UINT aa_flags )
758 switch (aa_flags & 0x7f)
760 case GGO_BITMAP:
761 return AA_None;
762 case WINE_GGO_GRAY16_BITMAP:
763 return AA_Grey;
764 case WINE_GGO_HRGB_BITMAP:
765 return AA_RGB;
766 case WINE_GGO_HBGR_BITMAP:
767 return AA_BGR;
768 case WINE_GGO_VRGB_BITMAP:
769 return AA_VRGB;
770 case WINE_GGO_VBGR_BITMAP:
771 return AA_VBGR;
772 default:
773 FIXME( "unknown flags %x\n", aa_flags );
774 return AA_None;
778 static UINT get_xft_aa_flags( const LOGFONTW *lf )
780 char *value, *p;
781 UINT ret = 0;
783 switch (lf->lfQuality)
785 case NONANTIALIASED_QUALITY:
786 case ANTIALIASED_QUALITY:
787 break;
788 default:
789 if (!(value = XGetDefault( gdi_display, "Xft", "antialias" ))) break;
790 TRACE( "got antialias '%s'\n", value );
791 for (p = value; *p; p++) if ('A' <= *p && *p <= 'Z') *p += 'a' - 'A'; /* to lower */
792 if (value[0] == 'f' || value[0] == 'n' || value[0] == '0' || !strcmp( value, "off" ))
794 ret = GGO_BITMAP;
795 break;
797 ret = GGO_GRAY4_BITMAP;
798 /* fall through */
799 case CLEARTYPE_QUALITY:
800 case CLEARTYPE_NATURAL_QUALITY:
801 if (!(value = XGetDefault( gdi_display, "Xft", "rgba" ))) break;
802 TRACE( "got rgba '%s'\n", value );
803 if (!strcmp( value, "rgb" )) ret = WINE_GGO_HRGB_BITMAP;
804 else if (!strcmp( value, "bgr" )) ret = WINE_GGO_HBGR_BITMAP;
805 else if (!strcmp( value, "vrgb" )) ret = WINE_GGO_VRGB_BITMAP;
806 else if (!strcmp( value, "vbgr" )) ret = WINE_GGO_VBGR_BITMAP;
807 else if (!strcmp( value, "none" )) ret = GGO_GRAY4_BITMAP;
808 break;
810 return ret;
813 /**********************************************************************
814 * xrenderdrv_SelectFont
816 static HFONT CDECL xrenderdrv_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
818 LFANDSIZE lfsz;
819 struct xrender_physdev *physdev = get_xrender_dev( dev );
820 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
821 DWORD mode;
822 HFONT ret;
824 NtGdiExtGetObjectW( hfont, sizeof(lfsz.lf), &lfsz.lf );
825 if (!*aa_flags) *aa_flags = get_xft_aa_flags( &lfsz.lf );
827 ret = next->funcs->pSelectFont( next, hfont, aa_flags );
828 if (!ret) return 0;
830 switch (*aa_flags)
832 case GGO_GRAY2_BITMAP:
833 case GGO_GRAY4_BITMAP:
834 case GGO_GRAY8_BITMAP:
835 physdev->aa_flags = WINE_GGO_GRAY16_BITMAP;
836 break;
837 case 0:
838 physdev->aa_flags = GGO_BITMAP;
839 break;
840 default:
841 physdev->aa_flags = *aa_flags;
842 break;
845 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
846 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
847 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
848 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
849 lfsz.devsize.cx = X11DRV_XWStoDS( dev->hdc, lfsz.lf.lfWidth );
850 lfsz.devsize.cy = X11DRV_YWStoDS( dev->hdc, lfsz.lf.lfHeight );
852 NtGdiGetTransform( dev->hdc, 0x204, &lfsz.xform );
853 TRACE("font transform %f %f %f %f\n", lfsz.xform.eM11, lfsz.xform.eM12,
854 lfsz.xform.eM21, lfsz.xform.eM22);
856 NtGdiGetDCDword( dev->hdc, NtGdiGetGraphicsMode, &mode );
857 if (mode == GM_COMPATIBLE)
859 lfsz.lf.lfOrientation = lfsz.lf.lfEscapement;
860 if (lfsz.xform.eM11 * lfsz.xform.eM22 < 0)
861 lfsz.lf.lfOrientation = -lfsz.lf.lfOrientation;
864 /* Not used fields, would break hashing */
865 lfsz.xform.eDx = lfsz.xform.eDy = 0;
867 lfsz_calc_hash(&lfsz);
869 pthread_mutex_lock( &xrender_mutex );
870 if (physdev->cache_index != -1)
871 dec_ref_cache( physdev->cache_index );
872 physdev->cache_index = GetCacheEntry( &lfsz );
873 pthread_mutex_unlock( &xrender_mutex );
874 return ret;
877 static void set_physdev_format( struct xrender_physdev *physdev, enum wxr_format format )
879 if (physdev->x11dev->drawable == DefaultRootWindow( gdi_display ))
880 physdev->format = WXR_FORMAT_ROOT;
881 else
882 physdev->format = format;
884 physdev->pict_format = pict_formats[physdev->format];
887 static BOOL create_xrender_dc( PHYSDEV *pdev, enum wxr_format format )
889 X11DRV_PDEVICE *x11dev = get_x11drv_dev( *pdev );
890 struct xrender_physdev *physdev = calloc( 1, sizeof(*physdev) );
892 if (!physdev) return FALSE;
893 physdev->x11dev = x11dev;
894 physdev->cache_index = -1;
895 set_physdev_format( physdev, format );
896 push_dc_driver( pdev, &physdev->dev, &xrender_funcs );
897 return TRUE;
900 /* store the color mask data in the bitmap info structure */
901 static void set_color_info( XRenderPictFormat *format, BITMAPINFO *info )
903 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
905 info->bmiHeader.biPlanes = 1;
906 info->bmiHeader.biBitCount = pixmap_formats[format->depth]->bits_per_pixel;
907 info->bmiHeader.biCompression = BI_RGB;
908 info->bmiHeader.biClrUsed = 0;
910 switch (info->bmiHeader.biBitCount)
912 case 16:
913 colors[0] = format->direct.redMask << format->direct.red;
914 colors[1] = format->direct.greenMask << format->direct.green;
915 colors[2] = format->direct.blueMask << format->direct.blue;
916 info->bmiHeader.biCompression = BI_BITFIELDS;
917 break;
918 case 32:
919 colors[0] = format->direct.redMask << format->direct.red;
920 colors[1] = format->direct.greenMask << format->direct.green;
921 colors[2] = format->direct.blueMask << format->direct.blue;
922 if (colors[0] != 0xff0000 || colors[1] != 0x00ff00 || colors[2] != 0x0000ff)
923 info->bmiHeader.biCompression = BI_BITFIELDS;
924 break;
929 /**********************************************************************
930 * xrenderdrv_CreateDC
932 static BOOL CDECL xrenderdrv_CreateDC( PHYSDEV *pdev, LPCWSTR device, LPCWSTR output,
933 const DEVMODEW* initData )
935 return create_xrender_dc( pdev, default_format );
938 /**********************************************************************
939 * xrenderdrv_CreateCompatibleDC
941 static BOOL CDECL xrenderdrv_CreateCompatibleDC( PHYSDEV orig, PHYSDEV *pdev )
943 if (orig) /* chain to x11drv first */
945 orig = GET_NEXT_PHYSDEV( orig, pCreateCompatibleDC );
946 if (!orig->funcs->pCreateCompatibleDC( orig, pdev )) return FALSE;
948 /* otherwise we have been called by x11drv */
950 return create_xrender_dc( pdev, WXR_FORMAT_MONO );
953 /**********************************************************************
954 * xrenderdrv_DeleteDC
956 static BOOL CDECL xrenderdrv_DeleteDC( PHYSDEV dev )
958 struct xrender_physdev *physdev = get_xrender_dev( dev );
960 free_xrender_picture( physdev );
962 pthread_mutex_lock( &xrender_mutex );
963 if (physdev->cache_index != -1) dec_ref_cache( physdev->cache_index );
964 pthread_mutex_unlock( &xrender_mutex );
966 free( physdev );
967 return TRUE;
970 /**********************************************************************
971 * xrenderdrv_ExtEscape
973 static INT CDECL xrenderdrv_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOID in_data,
974 INT out_count, LPVOID out_data )
976 struct xrender_physdev *physdev = get_xrender_dev( dev );
978 dev = GET_NEXT_PHYSDEV( dev, pExtEscape );
980 if (escape == X11DRV_ESCAPE && in_data && in_count >= sizeof(enum x11drv_escape_codes))
982 if (*(const enum x11drv_escape_codes *)in_data == X11DRV_SET_DRAWABLE)
984 BOOL ret = dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
985 if (ret)
987 free_xrender_picture( physdev );
988 set_physdev_format( physdev, default_format );
990 return ret;
993 return dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
996 /***********************************************************************
997 * xrenderdrv_SetDeviceClipping
999 static void CDECL xrenderdrv_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
1001 struct xrender_physdev *physdev = get_xrender_dev( dev );
1003 physdev->region = rgn;
1004 physdev->update_clip = TRUE;
1006 dev = GET_NEXT_PHYSDEV( dev, pSetDeviceClipping );
1007 dev->funcs->pSetDeviceClipping( dev, rgn );
1011 /************************************************************************
1012 * UploadGlyph
1014 * Helper to ExtTextOut. Must be called inside xrender_mutex
1016 static void UploadGlyph(struct xrender_physdev *physDev, UINT glyph, enum glyph_type type)
1018 unsigned int buflen;
1019 char *buf;
1020 Glyph gid;
1021 GLYPHMETRICS gm;
1022 XGlyphInfo gi;
1023 gsCacheEntry *entry = glyphsetCache + physDev->cache_index;
1024 gsCacheEntryFormat *formatEntry;
1025 UINT ggo_format = physDev->aa_flags;
1026 AA_Type format = aa_type_from_flags( physDev->aa_flags );
1027 enum wxr_format wxr_format;
1028 static const char zero[4];
1029 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1031 if (type == GLYPH_INDEX) ggo_format |= GGO_GLYPH_INDEX;
1032 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity, FALSE );
1033 if(buflen == GDI_ERROR) {
1034 if(format != AA_None) {
1035 format = AA_None;
1036 physDev->aa_flags = GGO_BITMAP;
1037 ggo_format = (ggo_format & GGO_GLYPH_INDEX) | GGO_BITMAP;
1038 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL,
1039 &identity, FALSE);
1041 if(buflen == GDI_ERROR) {
1042 WARN("GetGlyphOutlineW failed using default glyph\n");
1043 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, 0, ggo_format, &gm, 0, NULL,
1044 &identity, FALSE );
1045 if(buflen == GDI_ERROR) {
1046 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1047 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, 0x20, ggo_format, &gm, 0, NULL,
1048 &identity, FALSE );
1049 if(buflen == GDI_ERROR) {
1050 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1051 return;
1055 TRACE("Turning off antialiasing for this monochrome font\n");
1058 /* If there is nothing for the current type, we create the entry. */
1059 if( !entry->format[type][format] ) {
1060 entry->format[type][format] = calloc( 1, sizeof(gsCacheEntryFormat) );
1062 formatEntry = entry->format[type][format];
1064 if(formatEntry->nrealized <= glyph) {
1065 size_t new_size = (glyph / 128 + 1) * 128;
1067 formatEntry->realized = realloc( formatEntry->realized, new_size * sizeof(BOOL) );
1068 memset( formatEntry->realized + formatEntry->nrealized, 0,
1069 (new_size - formatEntry->nrealized) * sizeof(BOOL) );
1071 formatEntry->gis = realloc( formatEntry->gis, new_size * sizeof(formatEntry->gis[0]) );
1072 memset( formatEntry->gis + formatEntry->nrealized, 0,
1073 (new_size - formatEntry->nrealized) * sizeof(formatEntry->gis[0]) );
1075 formatEntry->nrealized = new_size;
1079 if(formatEntry->glyphset == 0) {
1080 switch(format) {
1081 case AA_Grey:
1082 wxr_format = WXR_FORMAT_GRAY;
1083 break;
1085 case AA_RGB:
1086 case AA_BGR:
1087 case AA_VRGB:
1088 case AA_VBGR:
1089 wxr_format = WXR_FORMAT_A8R8G8B8;
1090 break;
1092 default:
1093 ERR("aa = %d - not implemented\n", format);
1094 /* fall through */
1095 case AA_None:
1096 wxr_format = WXR_FORMAT_MONO;
1097 break;
1100 formatEntry->font_format = pict_formats[wxr_format];
1101 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
1105 buf = calloc( 1, buflen );
1106 if (buflen)
1107 NtGdiGetGlyphOutline( physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity, FALSE );
1108 else
1109 gm.gmBlackBoxX = gm.gmBlackBoxY = 0; /* empty glyph */
1110 formatEntry->realized[glyph] = TRUE;
1112 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1113 buflen,
1114 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1115 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1117 gi.width = gm.gmBlackBoxX;
1118 gi.height = gm.gmBlackBoxY;
1119 gi.x = -gm.gmptGlyphOrigin.x;
1120 gi.y = gm.gmptGlyphOrigin.y;
1121 gi.xOff = gm.gmCellIncX;
1122 gi.yOff = gm.gmCellIncY;
1124 if(TRACE_ON(xrender)) {
1125 int pitch, i, j;
1126 char output[300];
1127 unsigned char *line;
1129 if(format == AA_None) {
1130 pitch = ((gi.width + 31) / 32) * 4;
1131 for(i = 0; i < gi.height; i++) {
1132 line = (unsigned char*) buf + i * pitch;
1133 output[0] = '\0';
1134 for(j = 0; j < pitch * 8; j++) {
1135 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1137 TRACE("%s\n", output);
1139 } else {
1140 static const char blks[] = " .:;!o*#";
1141 char str[2];
1143 str[1] = '\0';
1144 pitch = ((gi.width + 3) / 4) * 4;
1145 for(i = 0; i < gi.height; i++) {
1146 line = (unsigned char*) buf + i * pitch;
1147 output[0] = '\0';
1148 for(j = 0; j < pitch; j++) {
1149 str[0] = blks[line[j] >> 5];
1150 strcat(output, str);
1152 TRACE("%s\n", output);
1158 if(formatEntry->glyphset) {
1159 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1160 unsigned char *byte = (unsigned char*) buf, c;
1161 int i = buflen;
1163 while(i--) {
1164 c = *byte;
1166 /* magic to flip bit order */
1167 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1168 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1169 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1171 *byte++ = c;
1174 else if ( format != AA_Grey &&
1175 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1177 unsigned int i, *data = (unsigned int *)buf;
1178 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1180 gid = glyph;
1183 XRenderCompositeText seems to ignore 0x0 glyphs when
1184 AA_None, which means we lose the advance width of glyphs
1185 like the space. We'll pretend that such glyphs are 1x1
1186 bitmaps.
1189 if(buflen == 0)
1190 gi.width = gi.height = 1;
1192 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1193 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1196 free( buf );
1197 formatEntry->gis[glyph] = gi;
1200 /*************************************************************
1201 * get_tile_pict
1203 * Returns an appropriate Picture for tiling the text colour.
1204 * Call and use result within the xrender_mutex
1206 static Picture get_tile_pict( enum wxr_format wxr_format, const XRenderColor *color)
1208 static struct
1210 Pixmap xpm;
1211 Picture pict;
1212 XRenderColor current_color;
1213 } tiles[WXR_NB_FORMATS], *tile;
1215 tile = &tiles[wxr_format];
1217 if(!tile->xpm)
1219 XRenderPictureAttributes pa;
1220 XRenderPictFormat *pict_format = pict_formats[wxr_format];
1222 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_format->depth);
1224 pa.repeat = RepeatNormal;
1225 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_format, CPRepeat, &pa);
1227 /* init current_color to something different from text_pixel */
1228 tile->current_color = *color;
1229 tile->current_color.red ^= 0xffff;
1231 if (wxr_format == WXR_FORMAT_MONO)
1233 /* for a 1bpp bitmap we always need a 1 in the tile */
1234 XRenderColor col;
1235 col.red = col.green = col.blue = 0;
1236 col.alpha = 0xffff;
1237 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1241 if (memcmp( color, &tile->current_color, sizeof(*color) ) && wxr_format != WXR_FORMAT_MONO)
1243 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, color, 0, 0, 1, 1);
1244 tile->current_color = *color;
1246 return tile->pict;
1249 /*************************************************************
1250 * get_mask_pict
1252 * Returns an appropriate Picture for masking with the specified alpha.
1253 * Call and use result within the xrender_mutex
1255 static Picture get_mask_pict( int alpha )
1257 static Pixmap pixmap;
1258 static Picture pict;
1259 static int current_alpha;
1261 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1263 if (!pixmap)
1265 XRenderPictureAttributes pa;
1267 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1268 pa.repeat = RepeatNormal;
1269 pict = pXRenderCreatePicture( gdi_display, pixmap,
1270 pict_formats[WXR_FORMAT_A8R8G8B8], CPRepeat, &pa );
1271 current_alpha = -1;
1274 if (alpha != current_alpha)
1276 XRenderColor col;
1277 col.red = col.green = col.blue = 0;
1278 col.alpha = current_alpha = alpha;
1279 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1281 return pict;
1284 /***********************************************************************
1285 * xrenderdrv_ExtTextOut
1287 static BOOL CDECL xrenderdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
1288 const RECT *lprect, LPCWSTR wstr, UINT count, const INT *lpDx )
1290 struct xrender_physdev *physdev = get_xrender_dev( dev );
1291 gsCacheEntry *entry;
1292 gsCacheEntryFormat *formatEntry;
1293 unsigned int idx;
1294 DWORD text_color;
1295 Picture pict, tile_pict = 0;
1296 XGlyphElt16 *elts;
1297 POINT offset, desired, current;
1298 int render_op = PictOpOver;
1299 XRenderColor col;
1300 RECT rect, bounds;
1301 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
1303 NtGdiGetDCDword( physdev->dev.hdc, NtGdiGetTextColor, &text_color );
1304 get_xrender_color( physdev, text_color, &col );
1305 pict = get_xrender_picture( physdev, 0, (flags & ETO_CLIPPED) ? lprect : NULL );
1307 if(flags & ETO_OPAQUE)
1309 XRenderColor bg;
1311 if (physdev->format == WXR_FORMAT_MONO)
1312 /* use the inverse of the text color */
1313 bg.red = bg.green = bg.blue = bg.alpha = ~col.alpha;
1314 else
1316 DWORD bg_color;
1317 NtGdiGetDCDword( physdev->dev.hdc, NtGdiGetBkColor, &bg_color );
1318 get_xrender_color( physdev, bg_color, &bg );
1321 set_xrender_transformation( pict, 1, 1, 0, 0 );
1322 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &bg,
1323 physdev->x11dev->dc_rect.left + lprect->left,
1324 physdev->x11dev->dc_rect.top + lprect->top,
1325 lprect->right - lprect->left,
1326 lprect->bottom - lprect->top );
1327 add_device_bounds( physdev->x11dev, lprect );
1330 if(count == 0) return TRUE;
1332 pthread_mutex_lock( &xrender_mutex );
1334 entry = glyphsetCache + physdev->cache_index;
1335 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1337 for(idx = 0; idx < count; idx++) {
1338 if( !formatEntry ) {
1339 UploadGlyph(physdev, wstr[idx], type);
1340 /* re-evaluate format entry since aa_flags may have changed */
1341 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1342 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1343 UploadGlyph(physdev, wstr[idx], type);
1346 if (!formatEntry)
1348 WARN("could not upload requested glyphs\n");
1349 pthread_mutex_unlock( &xrender_mutex );
1350 return FALSE;
1353 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1354 physdev->x11dev->dc_rect.left + x, physdev->x11dev->dc_rect.top + y);
1356 elts = malloc( sizeof(XGlyphElt16) * count );
1358 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1359 So we pass zeros to the function and move to our starting position using the first
1360 element of the elts array. */
1362 desired.x = physdev->x11dev->dc_rect.left + x;
1363 desired.y = physdev->x11dev->dc_rect.top + y;
1364 offset.x = offset.y = 0;
1365 current.x = current.y = 0;
1367 tile_pict = get_tile_pict(physdev->format, &col);
1369 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1371 if (physdev->format == WXR_FORMAT_MONO && col.red == 0 && col.green == 0 && col.blue == 0)
1372 render_op = PictOpOutReverse; /* This gives us 'black' text */
1374 reset_bounds( &bounds );
1375 for(idx = 0; idx < count; idx++)
1377 elts[idx].glyphset = formatEntry->glyphset;
1378 elts[idx].chars = wstr + idx;
1379 elts[idx].nchars = 1;
1380 elts[idx].xOff = desired.x - current.x;
1381 elts[idx].yOff = desired.y - current.y;
1383 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1384 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1386 rect.left = desired.x - physdev->x11dev->dc_rect.left - formatEntry->gis[wstr[idx]].x;
1387 rect.top = desired.y - physdev->x11dev->dc_rect.top - formatEntry->gis[wstr[idx]].y;
1388 rect.right = rect.left + formatEntry->gis[wstr[idx]].width;
1389 rect.bottom = rect.top + formatEntry->gis[wstr[idx]].height;
1390 add_bounds_rect( &bounds, &rect );
1392 if(!lpDx)
1394 desired.x += formatEntry->gis[wstr[idx]].xOff;
1395 desired.y += formatEntry->gis[wstr[idx]].yOff;
1397 else
1399 if(flags & ETO_PDY)
1401 offset.x += lpDx[idx * 2];
1402 offset.y += lpDx[idx * 2 + 1];
1404 else
1405 offset.x += lpDx[idx];
1406 desired.x = physdev->x11dev->dc_rect.left + x + offset.x;
1407 desired.y = physdev->x11dev->dc_rect.top + y + offset.y;
1411 /* Make sure we don't have any transforms set from a previous call */
1412 set_xrender_transformation(pict, 1, 1, 0, 0);
1413 pXRenderCompositeText16(gdi_display, render_op,
1414 tile_pict,
1415 pict,
1416 formatEntry->font_format,
1417 0, 0, 0, 0, elts, count);
1418 free( elts );
1420 pthread_mutex_unlock( &xrender_mutex );
1421 add_device_bounds( physdev->x11dev, &bounds );
1422 return TRUE;
1425 /* multiply the alpha channel of a picture */
1426 static void multiply_alpha( Picture pict, XRenderPictFormat *format, int alpha,
1427 int x, int y, int width, int height )
1429 XRenderPictureAttributes pa;
1430 Pixmap src_pixmap, mask_pixmap;
1431 Picture src_pict, mask_pict;
1432 XRenderColor color;
1434 src_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1435 mask_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1436 pa.repeat = RepeatNormal;
1437 src_pict = pXRenderCreatePicture( gdi_display, src_pixmap, format, CPRepeat, &pa );
1438 pa.component_alpha = True;
1439 mask_pict = pXRenderCreatePicture( gdi_display, mask_pixmap, format, CPRepeat|CPComponentAlpha, &pa );
1440 color.red = color.green = color.blue = color.alpha = 0xffff;
1441 pXRenderFillRectangle( gdi_display, PictOpSrc, src_pict, &color, 0, 0, 1, 1 );
1442 color.alpha = alpha;
1443 pXRenderFillRectangle( gdi_display, PictOpSrc, mask_pict, &color, 0, 0, 1, 1 );
1444 pXRenderComposite( gdi_display, PictOpInReverse, src_pict, mask_pict, pict,
1445 0, 0, 0, 0, x, y, width, height );
1446 pXRenderFreePicture( gdi_display, src_pict );
1447 pXRenderFreePicture( gdi_display, mask_pict );
1448 XFreePixmap( gdi_display, src_pixmap );
1449 XFreePixmap( gdi_display, mask_pixmap );
1452 /* Helper function for (stretched) blitting using xrender */
1453 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1454 int x_src, int y_src, int width_src, int height_src,
1455 int x_dst, int y_dst, int width_dst, int height_dst,
1456 double xscale, double yscale )
1458 int x_offset, y_offset;
1460 if (width_src < 0)
1462 x_src += width_src + 1;
1464 if (height_src < 0)
1466 y_src += height_src + 1;
1468 if (width_dst < 0)
1470 x_dst += width_dst + 1;
1471 width_dst = -width_dst;
1473 if (height_dst < 0)
1475 y_dst += height_dst + 1;
1476 height_dst = -height_dst;
1479 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1480 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1481 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1482 if(xscale != 1.0 || yscale != 1.0)
1484 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1485 * in the wrong quadrant of the x-y plane.
1487 x_offset = (xscale < 0) ? -width_dst : 0;
1488 y_offset = (yscale < 0) ? -height_dst : 0;
1489 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1491 else
1493 x_offset = x_src;
1494 y_offset = y_src;
1495 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1497 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1498 x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst );
1501 /* Helper function for (stretched) mono->color blitting using xrender */
1502 static void xrender_mono_blit( Picture src_pict, Picture dst_pict,
1503 enum wxr_format dst_format, XRenderColor *fg, XRenderColor *bg,
1504 int x_src, int y_src, int width_src, int height_src,
1505 int x_dst, int y_dst, int width_dst, int height_dst,
1506 double xscale, double yscale )
1508 Picture tile_pict;
1509 int x_offset, y_offset;
1510 XRenderColor color;
1512 if (width_src < 0)
1514 x_src += width_src + 1;
1515 width_src = -width_src;
1517 if (height_src < 0)
1519 y_src += height_src + 1;
1520 height_src = -height_src;
1522 if (width_dst < 0)
1524 x_dst += width_dst + 1;
1525 width_dst = -width_dst;
1527 if (height_dst < 0)
1529 y_dst += height_dst + 1;
1530 height_dst = -height_dst;
1533 /* When doing a mono->color blit, the source data is used as mask, and the source picture
1534 * contains a 1x1 picture for tiling. The source data effectively acts as an alpha channel to
1535 * the tile data.
1537 pthread_mutex_lock( &xrender_mutex );
1538 color = *bg;
1539 color.alpha = 0xffff; /* tile pict needs 100% alpha */
1540 tile_pict = get_tile_pict( dst_format, &color );
1542 pXRenderFillRectangle( gdi_display, PictOpSrc, dst_pict, fg, x_dst, y_dst, width_dst, height_dst );
1544 if (xscale != 1.0 || yscale != 1.0)
1546 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1547 * in the wrong quadrant of the x-y plane.
1549 x_offset = (xscale < 0) ? -width_dst : 0;
1550 y_offset = (yscale < 0) ? -height_dst : 0;
1551 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1553 else
1555 x_offset = x_src;
1556 y_offset = y_src;
1557 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1559 pXRenderComposite(gdi_display, PictOpOver, tile_pict, src_pict, dst_pict,
1560 0, 0, x_offset, y_offset, x_dst, y_dst, width_dst, height_dst );
1561 pthread_mutex_unlock( &xrender_mutex );
1563 /* force the alpha channel for background pixels, it has been set to 100% by the tile */
1564 if (bg->alpha != 0xffff && (dst_format == WXR_FORMAT_A8R8G8B8 || dst_format == WXR_FORMAT_B8G8R8A8))
1565 multiply_alpha( dst_pict, pict_formats[dst_format], bg->alpha,
1566 x_dst, y_dst, width_dst, height_dst );
1569 /* create a pixmap and render picture for an image */
1570 static DWORD create_image_pixmap( BITMAPINFO *info, const struct gdi_image_bits *bits,
1571 struct bitblt_coords *src, enum wxr_format format,
1572 Pixmap *pixmap, Picture *pict, BOOL *use_repeat )
1574 DWORD ret;
1575 int width = src->visrect.right - src->visrect.left;
1576 int height = src->visrect.bottom - src->visrect.top;
1577 int depth = pict_formats[format]->depth;
1578 struct gdi_image_bits dst_bits;
1579 XRenderPictureAttributes pa;
1580 GC gc;
1581 XImage *image;
1583 image = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0, NULL,
1584 info->bmiHeader.biWidth, height, 32, 0 );
1585 if (!image) return ERROR_OUTOFMEMORY;
1587 ret = copy_image_bits( info, (format == WXR_FORMAT_R8G8B8), image, bits, &dst_bits, src, NULL, ~0u );
1588 if (ret) return ret;
1590 image->data = dst_bits.ptr;
1592 *use_repeat = (width == 1 && height == 1);
1593 pa.repeat = *use_repeat ? RepeatNormal : RepeatNone;
1595 *pixmap = XCreatePixmap( gdi_display, root_window, width, height, depth );
1596 gc = XCreateGC( gdi_display, *pixmap, 0, NULL );
1597 XPutImage( gdi_display, *pixmap, gc, image, src->visrect.left, 0, 0, 0, width, height );
1598 *pict = pXRenderCreatePicture( gdi_display, *pixmap, pict_formats[format], CPRepeat, &pa );
1599 XFreeGC( gdi_display, gc );
1601 /* make coordinates relative to the pixmap */
1602 src->x -= src->visrect.left;
1603 src->y -= src->visrect.top;
1604 OffsetRect( &src->visrect, -src->visrect.left, -src->visrect.top );
1606 image->data = NULL;
1607 XDestroyImage( image );
1608 if (dst_bits.free) dst_bits.free( &dst_bits );
1609 return ret;
1612 static void xrender_stretch_blit( struct xrender_physdev *physdev_src, struct xrender_physdev *physdev_dst,
1613 Drawable drawable, const struct bitblt_coords *src,
1614 const struct bitblt_coords *dst )
1616 int x_dst, y_dst;
1617 Picture src_pict = 0, dst_pict, mask_pict = 0;
1618 double xscale = src->width / (double)dst->width;
1619 double yscale = src->height / (double)dst->height;
1621 if (drawable) /* using an intermediate pixmap */
1623 x_dst = dst->x;
1624 y_dst = dst->y;
1625 dst_pict = pXRenderCreatePicture( gdi_display, drawable, physdev_dst->pict_format, 0, NULL );
1627 else
1629 x_dst = physdev_dst->x11dev->dc_rect.left + dst->x;
1630 y_dst = physdev_dst->x11dev->dc_rect.top + dst->y;
1631 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1634 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1636 /* mono -> color */
1637 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1639 DWORD text_color, bg_color;
1640 XRenderColor fg, bg;
1642 NtGdiGetDCDword( physdev_dst->dev.hdc, NtGdiGetTextColor, &text_color );
1643 NtGdiGetDCDword( physdev_dst->dev.hdc, NtGdiGetBkColor, &bg_color );
1644 get_xrender_color( physdev_dst, text_color, &fg );
1645 get_xrender_color( physdev_dst, bg_color, &bg );
1646 fg.alpha = bg.alpha = 0;
1648 xrender_mono_blit( src_pict, dst_pict, physdev_dst->format, &fg, &bg,
1649 physdev_src->x11dev->dc_rect.left + src->x,
1650 physdev_src->x11dev->dc_rect.top + src->y,
1651 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1653 else /* color -> color (can be at different depths) or mono -> mono */
1655 if (physdev_dst->pict_format->depth == 32 && physdev_src->pict_format->depth < 32)
1656 mask_pict = get_no_alpha_mask();
1658 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict,
1659 physdev_src->x11dev->dc_rect.left + src->x,
1660 physdev_src->x11dev->dc_rect.top + src->y,
1661 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1664 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1668 static void xrender_put_image( Pixmap src_pixmap, Picture src_pict, Picture mask_pict, HRGN clip,
1669 XRenderPictFormat *dst_format, struct xrender_physdev *physdev,
1670 Drawable drawable, struct bitblt_coords *src,
1671 struct bitblt_coords *dst, BOOL use_repeat )
1673 int x_dst, y_dst;
1674 Picture dst_pict;
1675 double xscale, yscale;
1677 if (drawable) /* using an intermediate pixmap */
1679 RGNDATA *clip_data = NULL;
1681 if (clip) clip_data = X11DRV_GetRegionData( clip, 0 );
1682 x_dst = dst->x;
1683 y_dst = dst->y;
1684 dst_pict = pXRenderCreatePicture( gdi_display, drawable, dst_format, 0, NULL );
1685 if (clip_data)
1686 pXRenderSetPictureClipRectangles( gdi_display, dst_pict, 0, 0,
1687 (XRectangle *)clip_data->Buffer, clip_data->rdh.nCount );
1688 free( clip_data );
1690 else
1692 x_dst = physdev->x11dev->dc_rect.left + dst->x;
1693 y_dst = physdev->x11dev->dc_rect.top + dst->y;
1694 dst_pict = get_xrender_picture( physdev, clip, &dst->visrect );
1697 if (!use_repeat)
1699 xscale = src->width / (double)dst->width;
1700 yscale = src->height / (double)dst->height;
1702 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1704 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict, src->x, src->y, src->width, src->height,
1705 x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1707 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1711 /***********************************************************************
1712 * xrenderdrv_StretchBlt
1714 static BOOL CDECL xrenderdrv_StretchBlt( PHYSDEV dst_dev, struct bitblt_coords *dst,
1715 PHYSDEV src_dev, struct bitblt_coords *src, DWORD rop )
1717 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1718 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1719 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
1721 if (src_dev->funcs != dst_dev->funcs)
1723 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pStretchBlt );
1724 return dst_dev->funcs->pStretchBlt( dst_dev, dst, src_dev, src, rop );
1727 /* XRender is of no use for color -> mono */
1728 if (physdev_dst->format == WXR_FORMAT_MONO && physdev_src->format != WXR_FORMAT_MONO)
1729 goto x11drv_fallback;
1731 /* if not stretching, we only need to handle format conversion */
1732 if (!stretch && physdev_dst->format == physdev_src->format) goto x11drv_fallback;
1734 if (rop != SRCCOPY)
1736 GC tmpGC;
1737 Pixmap tmp_pixmap;
1738 struct bitblt_coords tmp;
1740 /* make coordinates relative to tmp pixmap */
1741 tmp = *dst;
1742 tmp.x -= tmp.visrect.left;
1743 tmp.y -= tmp.visrect.top;
1744 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1746 tmpGC = XCreateGC( gdi_display, physdev_dst->x11dev->drawable, 0, NULL );
1747 XSetSubwindowMode( gdi_display, tmpGC, IncludeInferiors );
1748 XSetGraphicsExposures( gdi_display, tmpGC, False );
1749 tmp_pixmap = XCreatePixmap( gdi_display, root_window, tmp.visrect.right - tmp.visrect.left,
1750 tmp.visrect.bottom - tmp.visrect.top, physdev_dst->pict_format->depth );
1752 xrender_stretch_blit( physdev_src, physdev_dst, tmp_pixmap, src, &tmp );
1753 execute_rop( physdev_dst->x11dev, tmp_pixmap, tmpGC, &dst->visrect, rop );
1755 XFreePixmap( gdi_display, tmp_pixmap );
1756 XFreeGC( gdi_display, tmpGC );
1758 else xrender_stretch_blit( physdev_src, physdev_dst, 0, src, dst );
1760 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
1761 return TRUE;
1763 x11drv_fallback:
1764 return X11DRV_StretchBlt( &physdev_dst->x11dev->dev, dst, &physdev_src->x11dev->dev, src, rop );
1768 /***********************************************************************
1769 * xrenderdrv_PutImage
1771 static DWORD CDECL xrenderdrv_PutImage( PHYSDEV dev, HRGN clip, BITMAPINFO *info,
1772 const struct gdi_image_bits *bits, struct bitblt_coords *src,
1773 struct bitblt_coords *dst, DWORD rop )
1775 struct xrender_physdev *physdev = get_xrender_dev( dev );
1776 DWORD ret;
1777 Pixmap tmp_pixmap;
1778 GC gc;
1779 enum wxr_format src_format, dst_format;
1780 XRenderPictFormat *pict_format;
1781 Pixmap src_pixmap;
1782 Picture src_pict, mask_pict = 0;
1783 BOOL use_repeat;
1785 dst_format = physdev->format;
1786 src_format = get_xrender_format_from_bitmapinfo( info );
1787 if (!(pict_format = pict_formats[src_format])) goto update_format;
1789 /* make sure we can create an image with the same bpp */
1790 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1791 goto update_format;
1793 /* mono <-> color conversions not supported */
1794 if ((src_format != dst_format) && (src_format == WXR_FORMAT_MONO || dst_format == WXR_FORMAT_MONO))
1795 goto x11drv_fallback;
1797 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1799 if (!has_alpha( src_format ) && has_alpha( dst_format )) mask_pict = get_no_alpha_mask();
1801 ret = create_image_pixmap( info, bits, src, src_format, &src_pixmap, &src_pict, &use_repeat );
1802 if (!ret)
1804 struct bitblt_coords tmp;
1806 if (rop != SRCCOPY)
1808 BOOL restore_region = add_extra_clipping_region( physdev->x11dev, clip );
1810 /* make coordinates relative to tmp pixmap */
1811 tmp = *dst;
1812 tmp.x -= tmp.visrect.left;
1813 tmp.y -= tmp.visrect.top;
1814 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1816 gc = XCreateGC( gdi_display, physdev->x11dev->drawable, 0, NULL );
1817 XSetSubwindowMode( gdi_display, gc, IncludeInferiors );
1818 XSetGraphicsExposures( gdi_display, gc, False );
1819 tmp_pixmap = XCreatePixmap( gdi_display, root_window,
1820 tmp.visrect.right - tmp.visrect.left,
1821 tmp.visrect.bottom - tmp.visrect.top,
1822 physdev->pict_format->depth );
1824 xrender_put_image( src_pixmap, src_pict, mask_pict, NULL, physdev->pict_format,
1825 NULL, tmp_pixmap, src, &tmp, use_repeat );
1826 execute_rop( physdev->x11dev, tmp_pixmap, gc, &dst->visrect, rop );
1828 XFreePixmap( gdi_display, tmp_pixmap );
1829 XFreeGC( gdi_display, gc );
1830 if (restore_region) restore_clipping_region( physdev->x11dev );
1832 else xrender_put_image( src_pixmap, src_pict, mask_pict, clip,
1833 physdev->pict_format, physdev, 0, src, dst, use_repeat );
1835 add_device_bounds( physdev->x11dev, &dst->visrect );
1837 pXRenderFreePicture( gdi_display, src_pict );
1838 XFreePixmap( gdi_display, src_pixmap );
1840 return ret;
1842 update_format:
1843 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1844 set_color_info( pict_formats[dst_format], info );
1845 return ERROR_BAD_FORMAT;
1847 x11drv_fallback:
1848 dev = GET_NEXT_PHYSDEV( dev, pPutImage );
1849 return dev->funcs->pPutImage( dev, clip, info, bits, src, dst, rop );
1853 /***********************************************************************
1854 * xrenderdrv_BlendImage
1856 static DWORD CDECL xrenderdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
1857 struct bitblt_coords *src, struct bitblt_coords *dst,
1858 BLENDFUNCTION func )
1860 struct xrender_physdev *physdev = get_xrender_dev( dev );
1861 DWORD ret;
1862 enum wxr_format format;
1863 XRenderPictFormat *pict_format;
1864 Picture dst_pict, src_pict, mask_pict;
1865 Pixmap src_pixmap;
1866 BOOL use_repeat;
1868 format = get_xrender_format_from_bitmapinfo( info );
1869 if (!(func.AlphaFormat & AC_SRC_ALPHA))
1870 format = get_format_without_alpha( format );
1871 else if (format != WXR_FORMAT_A8R8G8B8 || info->bmiHeader.biCompression != BI_RGB)
1872 return ERROR_INVALID_PARAMETER;
1874 if (!(pict_format = pict_formats[format])) goto update_format;
1876 /* make sure we can create an image with the same bpp */
1877 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1878 goto update_format;
1880 if (format == WXR_FORMAT_MONO && physdev->format != WXR_FORMAT_MONO)
1881 goto update_format;
1883 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1885 ret = create_image_pixmap( info, bits, src, format, &src_pixmap, &src_pict, &use_repeat );
1886 if (!ret)
1888 double xscale, yscale;
1890 if (!use_repeat)
1892 xscale = src->width / (double)dst->width;
1893 yscale = src->height / (double)dst->height;
1895 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1897 dst_pict = get_xrender_picture( physdev, 0, &dst->visrect );
1899 pthread_mutex_lock( &xrender_mutex );
1900 mask_pict = get_mask_pict( func.SourceConstantAlpha * 257 );
1902 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1903 src->x, src->y, src->width, src->height,
1904 physdev->x11dev->dc_rect.left + dst->x,
1905 physdev->x11dev->dc_rect.top + dst->y,
1906 dst->width, dst->height, xscale, yscale );
1908 pXRenderFreePicture( gdi_display, src_pict );
1909 XFreePixmap( gdi_display, src_pixmap );
1911 pthread_mutex_unlock( &xrender_mutex );
1912 add_device_bounds( physdev->x11dev, &dst->visrect );
1914 return ret;
1916 update_format:
1917 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1918 set_color_info( physdev->pict_format, info );
1919 return ERROR_BAD_FORMAT;
1923 /***********************************************************************
1924 * xrenderdrv_AlphaBlend
1926 static BOOL CDECL xrenderdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
1927 PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blendfn )
1929 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1930 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1931 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
1932 XRenderPictureAttributes pa;
1933 Pixmap tmp_pixmap = 0;
1934 double xscale, yscale;
1936 if (src_dev->funcs != dst_dev->funcs)
1938 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pAlphaBlend );
1939 return dst_dev->funcs->pAlphaBlend( dst_dev, dst, src_dev, src, blendfn );
1942 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->format != WXR_FORMAT_A8R8G8B8)
1944 SetLastError( ERROR_INVALID_PARAMETER );
1945 return FALSE;
1948 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1950 xscale = src->width / (double)dst->width;
1951 yscale = src->height / (double)dst->height;
1953 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1955 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1957 /* mono -> color blending needs an intermediate color pixmap */
1958 XRenderColor fg, bg;
1959 int width = src->visrect.right - src->visrect.left;
1960 int height = src->visrect.bottom - src->visrect.top;
1962 /* blending doesn't use the destination DC colors */
1963 fg.red = fg.green = fg.blue = 0;
1964 bg.red = bg.green = bg.blue = 0xffff;
1965 fg.alpha = bg.alpha = 0xffff;
1967 tmp_pixmap = XCreatePixmap( gdi_display, root_window, width, height,
1968 physdev_dst->pict_format->depth );
1969 tmp_pict = pXRenderCreatePicture( gdi_display, tmp_pixmap, physdev_dst->pict_format, 0, NULL );
1971 xrender_mono_blit( src_pict, tmp_pict, physdev_dst->format, &fg, &bg,
1972 src->visrect.left, src->visrect.top, width, height, 0, 0, width, height, 1, 1 );
1974 else if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->pict_format)
1976 /* we need a source picture with no alpha */
1977 enum wxr_format format = get_format_without_alpha( physdev_src->format );
1978 if (format != physdev_src->format)
1980 pa.subwindow_mode = IncludeInferiors;
1981 tmp_pict = pXRenderCreatePicture( gdi_display, physdev_src->x11dev->drawable,
1982 pict_formats[format], CPSubwindowMode, &pa );
1986 if (tmp_pict) src_pict = tmp_pict;
1988 pthread_mutex_lock( &xrender_mutex );
1989 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
1991 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1992 physdev_src->x11dev->dc_rect.left + src->x,
1993 physdev_src->x11dev->dc_rect.top + src->y,
1994 src->width, src->height,
1995 physdev_dst->x11dev->dc_rect.left + dst->x,
1996 physdev_dst->x11dev->dc_rect.top + dst->y,
1997 dst->width, dst->height, xscale, yscale );
1999 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2000 if (tmp_pixmap) XFreePixmap( gdi_display, tmp_pixmap );
2002 pthread_mutex_unlock( &xrender_mutex );
2003 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
2004 return TRUE;
2007 /***********************************************************************
2008 * xrenderdrv_GradientFill
2010 static BOOL CDECL xrenderdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
2011 void * grad_array, ULONG ngrad, ULONG mode )
2013 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
2014 static const XFixed stops[2] = { 0, 1 << 16 };
2015 struct xrender_physdev *physdev = get_xrender_dev( dev );
2016 XLinearGradient gradient;
2017 XRenderColor colors[2];
2018 Picture src_pict, dst_pict;
2019 unsigned int i;
2020 const GRADIENT_RECT *rect = grad_array;
2021 RECT rc;
2022 POINT pt[2];
2024 if (!pXRenderCreateLinearGradient) goto fallback;
2026 /* <= 16-bpp uses dithering */
2027 if (!physdev->pict_format || physdev->pict_format->depth <= 16) goto fallback;
2029 switch (mode)
2031 case GRADIENT_FILL_RECT_H:
2032 case GRADIENT_FILL_RECT_V:
2033 for (i = 0; i < ngrad; i++, rect++)
2035 const TRIVERTEX *v1 = vert_array + rect->UpperLeft;
2036 const TRIVERTEX *v2 = vert_array + rect->LowerRight;
2038 colors[0].red = v1->Red * 257 / 256;
2039 colors[0].green = v1->Green * 257 / 256;
2040 colors[0].blue = v1->Blue * 257 / 256;
2041 colors[1].red = v2->Red * 257 / 256;
2042 colors[1].green = v2->Green * 257 / 256;
2043 colors[1].blue = v2->Blue * 257 / 256;
2044 /* always ignore alpha since otherwise xrender will want to pre-multiply the colors */
2045 colors[0].alpha = colors[1].alpha = 65535;
2047 pt[0].x = v1->x;
2048 pt[0].y = v1->y;
2049 pt[1].x = v2->x;
2050 pt[1].y = v2->y;
2051 lp_to_dp( dev->hdc, pt, 2 );
2052 if (mode == GRADIENT_FILL_RECT_H)
2054 gradient.p1.y = gradient.p2.y = 0;
2055 if (pt[1].x > pt[0].x)
2057 gradient.p1.x = 0;
2058 gradient.p2.x = (pt[1].x - pt[0].x) << 16;
2060 else
2062 gradient.p1.x = (pt[0].x - pt[1].x) << 16;
2063 gradient.p2.x = 0;
2066 else
2068 gradient.p1.x = gradient.p2.x = 0;
2069 if (pt[1].y > pt[0].y)
2071 gradient.p1.y = 0;
2072 gradient.p2.y = (pt[1].y - pt[0].y) << 16;
2074 else
2076 gradient.p1.y = (pt[0].y - pt[1].y) << 16;
2077 gradient.p2.y = 0;
2081 rc.left = min( pt[0].x, pt[1].x );
2082 rc.top = min( pt[0].y, pt[1].y );
2083 rc.right = max( pt[0].x, pt[1].x );
2084 rc.bottom = max( pt[0].y, pt[1].y );
2086 TRACE( "%u gradient %s colors %04x,%04x,%04x,%04x -> %04x,%04x,%04x,%04x\n",
2087 mode, wine_dbgstr_rect( &rc ),
2088 colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha,
2089 colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha );
2091 dst_pict = get_xrender_picture( physdev, 0, NULL );
2093 src_pict = pXRenderCreateLinearGradient( gdi_display, &gradient, stops, colors, 2 );
2094 xrender_blit( PictOpSrc, src_pict, 0, dst_pict,
2095 0, 0, rc.right - rc.left, rc.bottom - rc.top,
2096 physdev->x11dev->dc_rect.left + rc.left,
2097 physdev->x11dev->dc_rect.top + rc.top,
2098 rc.right - rc.left, rc.bottom - rc.top, 1, 1 );
2099 pXRenderFreePicture( gdi_display, src_pict );
2100 add_device_bounds( physdev->x11dev, &rc );
2102 return TRUE;
2105 fallback:
2106 #endif
2107 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
2108 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
2111 /***********************************************************************
2112 * xrenderdrv_SelectBrush
2114 static HBRUSH CDECL xrenderdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2116 struct xrender_physdev *physdev = get_xrender_dev( dev );
2117 Pixmap pixmap;
2118 XVisualInfo vis = default_visual;
2119 XRenderPictFormat *format = physdev->pict_format;
2121 if (!pattern) goto x11drv_fallback;
2122 if (pattern->info->bmiHeader.biBitCount == 1) goto x11drv_fallback;
2123 if (physdev->format == WXR_FORMAT_MONO) goto x11drv_fallback;
2125 vis.depth = format->depth;
2126 vis.red_mask = format->direct.redMask << format->direct.red;
2127 vis.green_mask = format->direct.greenMask << format->direct.green;
2128 vis.blue_mask = format->direct.blueMask << format->direct.blue;
2130 pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, pattern->info,
2131 &pattern->bits, pattern->usage );
2132 if (!pixmap) return 0;
2134 if (physdev->x11dev->brush.pixmap) XFreePixmap( gdi_display, physdev->x11dev->brush.pixmap );
2135 physdev->x11dev->brush.pixmap = pixmap;
2136 physdev->x11dev->brush.fillStyle = FillTiled;
2137 physdev->x11dev->brush.pixel = 0; /* ignored */
2138 physdev->x11dev->brush.style = BS_PATTERN;
2139 return hbrush;
2141 x11drv_fallback:
2142 dev = GET_NEXT_PHYSDEV( dev, pSelectBrush );
2143 return dev->funcs->pSelectBrush( dev, hbrush, pattern );
2147 static const struct gdi_dc_funcs xrender_funcs =
2149 NULL, /* pAbortDoc */
2150 NULL, /* pAbortPath */
2151 xrenderdrv_AlphaBlend, /* pAlphaBlend */
2152 NULL, /* pAngleArc */
2153 NULL, /* pArc */
2154 NULL, /* pArcTo */
2155 NULL, /* pBeginPath */
2156 xrenderdrv_BlendImage, /* pBlendImage */
2157 NULL, /* pChord */
2158 NULL, /* pCloseFigure */
2159 xrenderdrv_CreateCompatibleDC, /* pCreateCompatibleDC */
2160 xrenderdrv_CreateDC, /* pCreateDC */
2161 xrenderdrv_DeleteDC, /* pDeleteDC */
2162 NULL, /* pDeleteObject */
2163 NULL, /* pEllipse */
2164 NULL, /* pEndDoc */
2165 NULL, /* pEndPage */
2166 NULL, /* pEndPath */
2167 NULL, /* pEnumFonts */
2168 xrenderdrv_ExtEscape, /* pExtEscape */
2169 NULL, /* pExtFloodFill */
2170 xrenderdrv_ExtTextOut, /* pExtTextOut */
2171 NULL, /* pFillPath */
2172 NULL, /* pFillRgn */
2173 NULL, /* pFontIsLinked */
2174 NULL, /* pFrameRgn */
2175 NULL, /* pGetBoundsRect */
2176 NULL, /* pGetCharABCWidths */
2177 NULL, /* pGetCharABCWidthsI */
2178 NULL, /* pGetCharWidth */
2179 NULL, /* pGetCharWidthInfo */
2180 NULL, /* pGetDeviceCaps */
2181 NULL, /* pGetDeviceGammaRamp */
2182 NULL, /* pGetFontData */
2183 NULL, /* pGetFontRealizationInfo */
2184 NULL, /* pGetFontUnicodeRanges */
2185 NULL, /* pGetGlyphIndices */
2186 NULL, /* pGetGlyphOutline */
2187 NULL, /* pGetICMProfile */
2188 NULL, /* pGetImage */
2189 NULL, /* pGetKerningPairs */
2190 NULL, /* pGetNearestColor */
2191 NULL, /* pGetOutlineTextMetrics */
2192 NULL, /* pGetPixel */
2193 NULL, /* pGetSystemPaletteEntries */
2194 NULL, /* pGetTextCharsetInfo */
2195 NULL, /* pGetTextExtentExPoint */
2196 NULL, /* pGetTextExtentExPointI */
2197 NULL, /* pGetTextFace */
2198 NULL, /* pGetTextMetrics */
2199 xrenderdrv_GradientFill, /* pGradientFill */
2200 NULL, /* pInvertRgn */
2201 NULL, /* pLineTo */
2202 NULL, /* pMoveTo */
2203 NULL, /* pPaintRgn */
2204 NULL, /* pPatBlt */
2205 NULL, /* pPie */
2206 NULL, /* pPolyBezier */
2207 NULL, /* pPolyBezierTo */
2208 NULL, /* pPolyDraw */
2209 NULL, /* pPolyPolygon */
2210 NULL, /* pPolyPolyline */
2211 NULL, /* pPolylineTo */
2212 xrenderdrv_PutImage, /* pPutImage */
2213 NULL, /* pRealizeDefaultPalette */
2214 NULL, /* pRealizePalette */
2215 NULL, /* pRectangle */
2216 NULL, /* pResetDC */
2217 NULL, /* pRoundRect */
2218 NULL, /* pSelectBitmap */
2219 xrenderdrv_SelectBrush, /* pSelectBrush */
2220 xrenderdrv_SelectFont, /* pSelectFont */
2221 NULL, /* pSelectPen */
2222 NULL, /* pSetBkColor */
2223 NULL, /* pSetBoundsRect */
2224 NULL, /* pSetDCBrushColor */
2225 NULL, /* pSetDCPenColor */
2226 NULL, /* pSetDIBitsToDevice */
2227 xrenderdrv_SetDeviceClipping, /* pSetDeviceClipping */
2228 NULL, /* pSetDeviceGammaRamp */
2229 NULL, /* pSetPixel */
2230 NULL, /* pSetTextColor */
2231 NULL, /* pStartDoc */
2232 NULL, /* pStartPage */
2233 xrenderdrv_StretchBlt, /* pStretchBlt */
2234 NULL, /* pStretchDIBits */
2235 NULL, /* pStrokeAndFillPath */
2236 NULL, /* pStrokePath */
2237 NULL, /* pUnrealizePalette */
2238 NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */
2239 NULL, /* pD3DKMTCloseAdapter */
2240 NULL, /* pD3DKMTOpenAdapterFromLuid */
2241 NULL, /* pD3DKMTQueryVideoMemoryInfo */
2242 NULL, /* pD3DKMTSetVidPnSourceOwner */
2243 GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */
2246 #else /* SONAME_LIBXRENDER */
2248 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
2250 TRACE("XRender support not compiled in.\n");
2251 return NULL;
2254 #endif /* SONAME_LIBXRENDER */