wow64: In wow64_NtSetInformationToken forward TokenIntegrityLevel.
[wine.git] / dlls / winex11.drv / xrender.c
bloba59653871fdf597ef1b9054fe0bc84378954e7b4
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 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 (int)lfsz.lf.lfHeight, (int)lfsz.lf.lfWidth, (int)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 xrenderdrv_CreateDC( PHYSDEV *pdev, LPCWSTR device, LPCWSTR output, const DEVMODEW* initData )
934 return create_xrender_dc( pdev, default_format );
937 /**********************************************************************
938 * xrenderdrv_CreateCompatibleDC
940 static BOOL xrenderdrv_CreateCompatibleDC( PHYSDEV orig, PHYSDEV *pdev )
942 if (orig) /* chain to x11drv first */
944 orig = GET_NEXT_PHYSDEV( orig, pCreateCompatibleDC );
945 if (!orig->funcs->pCreateCompatibleDC( orig, pdev )) return FALSE;
947 /* otherwise we have been called by x11drv */
949 return create_xrender_dc( pdev, WXR_FORMAT_MONO );
952 /**********************************************************************
953 * xrenderdrv_DeleteDC
955 static BOOL xrenderdrv_DeleteDC( PHYSDEV dev )
957 struct xrender_physdev *physdev = get_xrender_dev( dev );
959 free_xrender_picture( physdev );
961 pthread_mutex_lock( &xrender_mutex );
962 if (physdev->cache_index != -1) dec_ref_cache( physdev->cache_index );
963 pthread_mutex_unlock( &xrender_mutex );
965 free( physdev );
966 return TRUE;
969 /**********************************************************************
970 * xrenderdrv_ExtEscape
972 static INT xrenderdrv_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOID in_data,
973 INT out_count, LPVOID out_data )
975 struct xrender_physdev *physdev = get_xrender_dev( dev );
977 dev = GET_NEXT_PHYSDEV( dev, pExtEscape );
979 if (escape == X11DRV_ESCAPE && in_data && in_count >= sizeof(enum x11drv_escape_codes))
981 if (*(const enum x11drv_escape_codes *)in_data == X11DRV_SET_DRAWABLE)
983 BOOL ret = dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
984 if (ret)
986 free_xrender_picture( physdev );
987 set_physdev_format( physdev, default_format );
989 return ret;
992 return dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
995 /***********************************************************************
996 * xrenderdrv_SetDeviceClipping
998 static void xrenderdrv_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
1000 struct xrender_physdev *physdev = get_xrender_dev( dev );
1002 physdev->region = rgn;
1003 physdev->update_clip = TRUE;
1005 dev = GET_NEXT_PHYSDEV( dev, pSetDeviceClipping );
1006 dev->funcs->pSetDeviceClipping( dev, rgn );
1010 /************************************************************************
1011 * UploadGlyph
1013 * Helper to ExtTextOut. Must be called inside xrender_mutex
1015 static void UploadGlyph(struct xrender_physdev *physDev, UINT glyph, enum glyph_type type)
1017 unsigned int buflen;
1018 char *buf;
1019 Glyph gid;
1020 GLYPHMETRICS gm;
1021 XGlyphInfo gi;
1022 gsCacheEntry *entry = glyphsetCache + physDev->cache_index;
1023 gsCacheEntryFormat *formatEntry;
1024 UINT ggo_format = physDev->aa_flags;
1025 AA_Type format = aa_type_from_flags( physDev->aa_flags );
1026 enum wxr_format wxr_format;
1027 static const char zero[4];
1028 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1030 if (type == GLYPH_INDEX) ggo_format |= GGO_GLYPH_INDEX;
1031 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity, FALSE );
1032 if(buflen == GDI_ERROR) {
1033 if(format != AA_None) {
1034 format = AA_None;
1035 physDev->aa_flags = GGO_BITMAP;
1036 ggo_format = (ggo_format & GGO_GLYPH_INDEX) | GGO_BITMAP;
1037 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL,
1038 &identity, FALSE);
1040 if(buflen == GDI_ERROR) {
1041 WARN("GetGlyphOutlineW failed using default glyph\n");
1042 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, 0, ggo_format, &gm, 0, NULL,
1043 &identity, FALSE );
1044 if(buflen == GDI_ERROR) {
1045 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1046 buflen = NtGdiGetGlyphOutline( physDev->dev.hdc, 0x20, ggo_format, &gm, 0, NULL,
1047 &identity, FALSE );
1048 if(buflen == GDI_ERROR) {
1049 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1050 return;
1054 TRACE("Turning off antialiasing for this monochrome font\n");
1057 /* If there is nothing for the current type, we create the entry. */
1058 if( !entry->format[type][format] ) {
1059 entry->format[type][format] = calloc( 1, sizeof(gsCacheEntryFormat) );
1061 formatEntry = entry->format[type][format];
1063 if(formatEntry->nrealized <= glyph) {
1064 size_t new_size = (glyph / 128 + 1) * 128;
1066 formatEntry->realized = realloc( formatEntry->realized, new_size * sizeof(BOOL) );
1067 memset( formatEntry->realized + formatEntry->nrealized, 0,
1068 (new_size - formatEntry->nrealized) * sizeof(BOOL) );
1070 formatEntry->gis = realloc( formatEntry->gis, new_size * sizeof(formatEntry->gis[0]) );
1071 memset( formatEntry->gis + formatEntry->nrealized, 0,
1072 (new_size - formatEntry->nrealized) * sizeof(formatEntry->gis[0]) );
1074 formatEntry->nrealized = new_size;
1078 if(formatEntry->glyphset == 0) {
1079 switch(format) {
1080 case AA_Grey:
1081 wxr_format = WXR_FORMAT_GRAY;
1082 break;
1084 case AA_RGB:
1085 case AA_BGR:
1086 case AA_VRGB:
1087 case AA_VBGR:
1088 wxr_format = WXR_FORMAT_A8R8G8B8;
1089 break;
1091 default:
1092 ERR("aa = %d - not implemented\n", format);
1093 /* fall through */
1094 case AA_None:
1095 wxr_format = WXR_FORMAT_MONO;
1096 break;
1099 formatEntry->font_format = pict_formats[wxr_format];
1100 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
1104 buf = calloc( 1, buflen );
1105 if (buflen)
1106 NtGdiGetGlyphOutline( physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity, FALSE );
1107 else
1108 gm.gmBlackBoxX = gm.gmBlackBoxY = 0; /* empty glyph */
1109 formatEntry->realized[glyph] = TRUE;
1111 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1112 buflen,
1113 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1114 (int)gm.gmptGlyphOrigin.x, (int)gm.gmptGlyphOrigin.y);
1116 gi.width = gm.gmBlackBoxX;
1117 gi.height = gm.gmBlackBoxY;
1118 gi.x = -gm.gmptGlyphOrigin.x;
1119 gi.y = gm.gmptGlyphOrigin.y;
1120 gi.xOff = gm.gmCellIncX;
1121 gi.yOff = gm.gmCellIncY;
1123 if(TRACE_ON(xrender)) {
1124 int pitch, i, j;
1125 char output[300];
1126 unsigned char *line;
1128 if(format == AA_None) {
1129 pitch = ((gi.width + 31) / 32) * 4;
1130 for(i = 0; i < gi.height; i++) {
1131 line = (unsigned char*) buf + i * pitch;
1132 output[0] = '\0';
1133 for(j = 0; j < pitch * 8; j++) {
1134 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1136 TRACE("%s\n", output);
1138 } else {
1139 static const char blks[] = " .:;!o*#";
1140 char str[2];
1142 str[1] = '\0';
1143 pitch = ((gi.width + 3) / 4) * 4;
1144 for(i = 0; i < gi.height; i++) {
1145 line = (unsigned char*) buf + i * pitch;
1146 output[0] = '\0';
1147 for(j = 0; j < pitch; j++) {
1148 str[0] = blks[line[j] >> 5];
1149 strcat(output, str);
1151 TRACE("%s\n", output);
1157 if(formatEntry->glyphset) {
1158 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1159 unsigned char *byte = (unsigned char*) buf, c;
1160 int i = buflen;
1162 while(i--) {
1163 c = *byte;
1165 /* magic to flip bit order */
1166 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1167 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1168 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1170 *byte++ = c;
1173 else if ( format != AA_Grey &&
1174 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1176 unsigned int i, *data = (unsigned int *)buf;
1177 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1179 gid = glyph;
1182 XRenderCompositeText seems to ignore 0x0 glyphs when
1183 AA_None, which means we lose the advance width of glyphs
1184 like the space. We'll pretend that such glyphs are 1x1
1185 bitmaps.
1188 if(buflen == 0)
1189 gi.width = gi.height = 1;
1191 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1192 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1195 free( buf );
1196 formatEntry->gis[glyph] = gi;
1199 /*************************************************************
1200 * get_tile_pict
1202 * Returns an appropriate Picture for tiling the text colour.
1203 * Call and use result within the xrender_mutex
1205 static Picture get_tile_pict( enum wxr_format wxr_format, const XRenderColor *color)
1207 static struct
1209 Pixmap xpm;
1210 Picture pict;
1211 XRenderColor current_color;
1212 } tiles[WXR_NB_FORMATS], *tile;
1214 tile = &tiles[wxr_format];
1216 if(!tile->xpm)
1218 XRenderPictureAttributes pa;
1219 XRenderPictFormat *pict_format = pict_formats[wxr_format];
1221 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_format->depth);
1223 pa.repeat = RepeatNormal;
1224 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_format, CPRepeat, &pa);
1226 /* init current_color to something different from text_pixel */
1227 tile->current_color = *color;
1228 tile->current_color.red ^= 0xffff;
1230 if (wxr_format == WXR_FORMAT_MONO)
1232 /* for a 1bpp bitmap we always need a 1 in the tile */
1233 XRenderColor col;
1234 col.red = col.green = col.blue = 0;
1235 col.alpha = 0xffff;
1236 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1240 if (memcmp( color, &tile->current_color, sizeof(*color) ) && wxr_format != WXR_FORMAT_MONO)
1242 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, color, 0, 0, 1, 1);
1243 tile->current_color = *color;
1245 return tile->pict;
1248 /*************************************************************
1249 * get_mask_pict
1251 * Returns an appropriate Picture for masking with the specified alpha.
1252 * Call and use result within the xrender_mutex
1254 static Picture get_mask_pict( int alpha )
1256 static Pixmap pixmap;
1257 static Picture pict;
1258 static int current_alpha;
1260 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1262 if (!pixmap)
1264 XRenderPictureAttributes pa;
1266 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1267 pa.repeat = RepeatNormal;
1268 pict = pXRenderCreatePicture( gdi_display, pixmap,
1269 pict_formats[WXR_FORMAT_A8R8G8B8], CPRepeat, &pa );
1270 current_alpha = -1;
1273 if (alpha != current_alpha)
1275 XRenderColor col;
1276 col.red = col.green = col.blue = 0;
1277 col.alpha = current_alpha = alpha;
1278 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1280 return pict;
1283 /***********************************************************************
1284 * xrenderdrv_ExtTextOut
1286 static BOOL xrenderdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
1287 const RECT *lprect, LPCWSTR wstr, UINT count, const INT *lpDx )
1289 struct xrender_physdev *physdev = get_xrender_dev( dev );
1290 gsCacheEntry *entry;
1291 gsCacheEntryFormat *formatEntry;
1292 unsigned int idx;
1293 DWORD text_color;
1294 Picture pict, tile_pict = 0;
1295 XGlyphElt16 *elts;
1296 POINT offset, desired, current;
1297 int render_op = PictOpOver;
1298 XRenderColor col;
1299 RECT rect, bounds;
1300 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
1302 NtGdiGetDCDword( physdev->dev.hdc, NtGdiGetTextColor, &text_color );
1303 get_xrender_color( physdev, text_color, &col );
1304 pict = get_xrender_picture( physdev, 0, (flags & ETO_CLIPPED) ? lprect : NULL );
1306 if(flags & ETO_OPAQUE)
1308 XRenderColor bg;
1310 if (physdev->format == WXR_FORMAT_MONO)
1311 /* use the inverse of the text color */
1312 bg.red = bg.green = bg.blue = bg.alpha = ~col.alpha;
1313 else
1315 DWORD bg_color;
1316 NtGdiGetDCDword( physdev->dev.hdc, NtGdiGetBkColor, &bg_color );
1317 get_xrender_color( physdev, bg_color, &bg );
1320 set_xrender_transformation( pict, 1, 1, 0, 0 );
1321 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &bg,
1322 physdev->x11dev->dc_rect.left + lprect->left,
1323 physdev->x11dev->dc_rect.top + lprect->top,
1324 lprect->right - lprect->left,
1325 lprect->bottom - lprect->top );
1326 add_device_bounds( physdev->x11dev, lprect );
1329 if(count == 0) return TRUE;
1331 pthread_mutex_lock( &xrender_mutex );
1333 entry = glyphsetCache + physdev->cache_index;
1334 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1336 for(idx = 0; idx < count; idx++) {
1337 if( !formatEntry ) {
1338 UploadGlyph(physdev, wstr[idx], type);
1339 /* re-evaluate format entry since aa_flags may have changed */
1340 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1341 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1342 UploadGlyph(physdev, wstr[idx], type);
1345 if (!formatEntry)
1347 WARN("could not upload requested glyphs\n");
1348 pthread_mutex_unlock( &xrender_mutex );
1349 return FALSE;
1352 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1353 (int)(physdev->x11dev->dc_rect.left + x), (int)(physdev->x11dev->dc_rect.top + y));
1355 elts = malloc( sizeof(XGlyphElt16) * count );
1357 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1358 So we pass zeros to the function and move to our starting position using the first
1359 element of the elts array. */
1361 desired.x = physdev->x11dev->dc_rect.left + x;
1362 desired.y = physdev->x11dev->dc_rect.top + y;
1363 offset.x = offset.y = 0;
1364 current.x = current.y = 0;
1366 tile_pict = get_tile_pict(physdev->format, &col);
1368 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1370 if (physdev->format == WXR_FORMAT_MONO && col.red == 0 && col.green == 0 && col.blue == 0)
1371 render_op = PictOpOutReverse; /* This gives us 'black' text */
1373 reset_bounds( &bounds );
1374 for(idx = 0; idx < count; idx++)
1376 elts[idx].glyphset = formatEntry->glyphset;
1377 elts[idx].chars = wstr + idx;
1378 elts[idx].nchars = 1;
1379 elts[idx].xOff = desired.x - current.x;
1380 elts[idx].yOff = desired.y - current.y;
1382 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1383 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1385 rect.left = desired.x - physdev->x11dev->dc_rect.left - formatEntry->gis[wstr[idx]].x;
1386 rect.top = desired.y - physdev->x11dev->dc_rect.top - formatEntry->gis[wstr[idx]].y;
1387 rect.right = rect.left + formatEntry->gis[wstr[idx]].width;
1388 rect.bottom = rect.top + formatEntry->gis[wstr[idx]].height;
1389 add_bounds_rect( &bounds, &rect );
1391 if(!lpDx)
1393 desired.x += formatEntry->gis[wstr[idx]].xOff;
1394 desired.y += formatEntry->gis[wstr[idx]].yOff;
1396 else
1398 if(flags & ETO_PDY)
1400 offset.x += lpDx[idx * 2];
1401 offset.y += lpDx[idx * 2 + 1];
1403 else
1404 offset.x += lpDx[idx];
1405 desired.x = physdev->x11dev->dc_rect.left + x + offset.x;
1406 desired.y = physdev->x11dev->dc_rect.top + y + offset.y;
1410 /* Make sure we don't have any transforms set from a previous call */
1411 set_xrender_transformation(pict, 1, 1, 0, 0);
1412 pXRenderCompositeText16(gdi_display, render_op,
1413 tile_pict,
1414 pict,
1415 formatEntry->font_format,
1416 0, 0, 0, 0, elts, count);
1417 free( elts );
1419 pthread_mutex_unlock( &xrender_mutex );
1420 add_device_bounds( physdev->x11dev, &bounds );
1421 return TRUE;
1424 /* multiply the alpha channel of a picture */
1425 static void multiply_alpha( Picture pict, XRenderPictFormat *format, int alpha,
1426 int x, int y, int width, int height )
1428 XRenderPictureAttributes pa;
1429 Pixmap src_pixmap, mask_pixmap;
1430 Picture src_pict, mask_pict;
1431 XRenderColor color;
1433 src_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1434 mask_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1435 pa.repeat = RepeatNormal;
1436 src_pict = pXRenderCreatePicture( gdi_display, src_pixmap, format, CPRepeat, &pa );
1437 pa.component_alpha = True;
1438 mask_pict = pXRenderCreatePicture( gdi_display, mask_pixmap, format, CPRepeat|CPComponentAlpha, &pa );
1439 color.red = color.green = color.blue = color.alpha = 0xffff;
1440 pXRenderFillRectangle( gdi_display, PictOpSrc, src_pict, &color, 0, 0, 1, 1 );
1441 color.alpha = alpha;
1442 pXRenderFillRectangle( gdi_display, PictOpSrc, mask_pict, &color, 0, 0, 1, 1 );
1443 pXRenderComposite( gdi_display, PictOpInReverse, src_pict, mask_pict, pict,
1444 0, 0, 0, 0, x, y, width, height );
1445 pXRenderFreePicture( gdi_display, src_pict );
1446 pXRenderFreePicture( gdi_display, mask_pict );
1447 XFreePixmap( gdi_display, src_pixmap );
1448 XFreePixmap( gdi_display, mask_pixmap );
1451 /* Helper function for (stretched) blitting using xrender */
1452 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1453 int x_src, int y_src, int width_src, int height_src,
1454 int x_dst, int y_dst, int width_dst, int height_dst,
1455 double xscale, double yscale )
1457 int x_offset, y_offset;
1459 if (width_src < 0)
1461 x_src += width_src + 1;
1463 if (height_src < 0)
1465 y_src += height_src + 1;
1467 if (width_dst < 0)
1469 x_dst += width_dst + 1;
1470 width_dst = -width_dst;
1472 if (height_dst < 0)
1474 y_dst += height_dst + 1;
1475 height_dst = -height_dst;
1478 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1479 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1480 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1481 if(xscale != 1.0 || yscale != 1.0)
1483 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1484 * in the wrong quadrant of the x-y plane.
1486 x_offset = (xscale < 0) ? -width_dst : 0;
1487 y_offset = (yscale < 0) ? -height_dst : 0;
1488 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1490 else
1492 x_offset = x_src;
1493 y_offset = y_src;
1494 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1496 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1497 x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst );
1500 /* Helper function for (stretched) mono->color blitting using xrender */
1501 static void xrender_mono_blit( Picture src_pict, Picture dst_pict,
1502 enum wxr_format dst_format, XRenderColor *fg, XRenderColor *bg,
1503 int x_src, int y_src, int width_src, int height_src,
1504 int x_dst, int y_dst, int width_dst, int height_dst,
1505 double xscale, double yscale )
1507 Picture tile_pict;
1508 int x_offset, y_offset;
1509 XRenderColor color;
1511 if (width_src < 0)
1513 x_src += width_src + 1;
1514 width_src = -width_src;
1516 if (height_src < 0)
1518 y_src += height_src + 1;
1519 height_src = -height_src;
1521 if (width_dst < 0)
1523 x_dst += width_dst + 1;
1524 width_dst = -width_dst;
1526 if (height_dst < 0)
1528 y_dst += height_dst + 1;
1529 height_dst = -height_dst;
1532 /* When doing a mono->color blit, the source data is used as mask, and the source picture
1533 * contains a 1x1 picture for tiling. The source data effectively acts as an alpha channel to
1534 * the tile data.
1536 pthread_mutex_lock( &xrender_mutex );
1537 color = *bg;
1538 color.alpha = 0xffff; /* tile pict needs 100% alpha */
1539 tile_pict = get_tile_pict( dst_format, &color );
1541 pXRenderFillRectangle( gdi_display, PictOpSrc, dst_pict, fg, x_dst, y_dst, width_dst, height_dst );
1543 if (xscale != 1.0 || yscale != 1.0)
1545 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1546 * in the wrong quadrant of the x-y plane.
1548 x_offset = (xscale < 0) ? -width_dst : 0;
1549 y_offset = (yscale < 0) ? -height_dst : 0;
1550 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1552 else
1554 x_offset = x_src;
1555 y_offset = y_src;
1556 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1558 pXRenderComposite(gdi_display, PictOpOver, tile_pict, src_pict, dst_pict,
1559 0, 0, x_offset, y_offset, x_dst, y_dst, width_dst, height_dst );
1560 pthread_mutex_unlock( &xrender_mutex );
1562 /* force the alpha channel for background pixels, it has been set to 100% by the tile */
1563 if (bg->alpha != 0xffff && (dst_format == WXR_FORMAT_A8R8G8B8 || dst_format == WXR_FORMAT_B8G8R8A8))
1564 multiply_alpha( dst_pict, pict_formats[dst_format], bg->alpha,
1565 x_dst, y_dst, width_dst, height_dst );
1568 /* create a pixmap and render picture for an image */
1569 static DWORD create_image_pixmap( BITMAPINFO *info, const struct gdi_image_bits *bits,
1570 struct bitblt_coords *src, enum wxr_format format,
1571 Pixmap *pixmap, Picture *pict, BOOL *use_repeat )
1573 DWORD ret;
1574 int width = src->visrect.right - src->visrect.left;
1575 int height = src->visrect.bottom - src->visrect.top;
1576 int depth = pict_formats[format]->depth;
1577 struct gdi_image_bits dst_bits;
1578 XRenderPictureAttributes pa;
1579 GC gc;
1580 XImage *image;
1582 image = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0, NULL,
1583 info->bmiHeader.biWidth, height, 32, 0 );
1584 if (!image) return ERROR_OUTOFMEMORY;
1586 ret = copy_image_bits( info, (format == WXR_FORMAT_R8G8B8), image, bits, &dst_bits, src, NULL, ~0u );
1587 if (ret) return ret;
1589 image->data = dst_bits.ptr;
1591 *use_repeat = (width == 1 && height == 1);
1592 pa.repeat = *use_repeat ? RepeatNormal : RepeatNone;
1594 *pixmap = XCreatePixmap( gdi_display, root_window, width, height, depth );
1595 gc = XCreateGC( gdi_display, *pixmap, 0, NULL );
1596 XPutImage( gdi_display, *pixmap, gc, image, src->visrect.left, 0, 0, 0, width, height );
1597 *pict = pXRenderCreatePicture( gdi_display, *pixmap, pict_formats[format], CPRepeat, &pa );
1598 XFreeGC( gdi_display, gc );
1600 /* make coordinates relative to the pixmap */
1601 src->x -= src->visrect.left;
1602 src->y -= src->visrect.top;
1603 OffsetRect( &src->visrect, -src->visrect.left, -src->visrect.top );
1605 image->data = NULL;
1606 XDestroyImage( image );
1607 if (dst_bits.free) dst_bits.free( &dst_bits );
1608 return ret;
1611 static void xrender_stretch_blit( struct xrender_physdev *physdev_src, struct xrender_physdev *physdev_dst,
1612 Drawable drawable, const struct bitblt_coords *src,
1613 const struct bitblt_coords *dst )
1615 int x_dst, y_dst;
1616 Picture src_pict = 0, dst_pict, mask_pict = 0;
1617 double xscale = src->width / (double)dst->width;
1618 double yscale = src->height / (double)dst->height;
1620 if (drawable) /* using an intermediate pixmap */
1622 x_dst = dst->x;
1623 y_dst = dst->y;
1624 dst_pict = pXRenderCreatePicture( gdi_display, drawable, physdev_dst->pict_format, 0, NULL );
1626 else
1628 x_dst = physdev_dst->x11dev->dc_rect.left + dst->x;
1629 y_dst = physdev_dst->x11dev->dc_rect.top + dst->y;
1630 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1633 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1635 /* mono -> color */
1636 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1638 DWORD text_color, bg_color;
1639 XRenderColor fg, bg;
1641 NtGdiGetDCDword( physdev_dst->dev.hdc, NtGdiGetTextColor, &text_color );
1642 NtGdiGetDCDword( physdev_dst->dev.hdc, NtGdiGetBkColor, &bg_color );
1643 get_xrender_color( physdev_dst, text_color, &fg );
1644 get_xrender_color( physdev_dst, bg_color, &bg );
1645 fg.alpha = bg.alpha = 0;
1647 xrender_mono_blit( src_pict, dst_pict, physdev_dst->format, &fg, &bg,
1648 physdev_src->x11dev->dc_rect.left + src->x,
1649 physdev_src->x11dev->dc_rect.top + src->y,
1650 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1652 else /* color -> color (can be at different depths) or mono -> mono */
1654 if (physdev_dst->pict_format->depth == 32 && physdev_src->pict_format->depth < 32)
1655 mask_pict = get_no_alpha_mask();
1657 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict,
1658 physdev_src->x11dev->dc_rect.left + src->x,
1659 physdev_src->x11dev->dc_rect.top + src->y,
1660 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1663 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1667 static void xrender_put_image( Pixmap src_pixmap, Picture src_pict, Picture mask_pict, HRGN clip,
1668 XRenderPictFormat *dst_format, struct xrender_physdev *physdev,
1669 Drawable drawable, struct bitblt_coords *src,
1670 struct bitblt_coords *dst, BOOL use_repeat )
1672 int x_dst, y_dst;
1673 Picture dst_pict;
1674 double xscale, yscale;
1676 if (drawable) /* using an intermediate pixmap */
1678 RGNDATA *clip_data = NULL;
1680 if (clip) clip_data = X11DRV_GetRegionData( clip, 0 );
1681 x_dst = dst->x;
1682 y_dst = dst->y;
1683 dst_pict = pXRenderCreatePicture( gdi_display, drawable, dst_format, 0, NULL );
1684 if (clip_data)
1685 pXRenderSetPictureClipRectangles( gdi_display, dst_pict, 0, 0,
1686 (XRectangle *)clip_data->Buffer, clip_data->rdh.nCount );
1687 free( clip_data );
1689 else
1691 x_dst = physdev->x11dev->dc_rect.left + dst->x;
1692 y_dst = physdev->x11dev->dc_rect.top + dst->y;
1693 dst_pict = get_xrender_picture( physdev, clip, &dst->visrect );
1696 if (!use_repeat)
1698 xscale = src->width / (double)dst->width;
1699 yscale = src->height / (double)dst->height;
1701 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1703 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict, src->x, src->y, src->width, src->height,
1704 x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1706 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1710 /***********************************************************************
1711 * xrenderdrv_StretchBlt
1713 static BOOL xrenderdrv_StretchBlt( PHYSDEV dst_dev, struct bitblt_coords *dst,
1714 PHYSDEV src_dev, struct bitblt_coords *src, DWORD rop )
1716 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1717 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1718 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
1720 if (src_dev->funcs != dst_dev->funcs)
1722 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pStretchBlt );
1723 return dst_dev->funcs->pStretchBlt( dst_dev, dst, src_dev, src, rop );
1726 /* XRender is of no use for color -> mono */
1727 if (physdev_dst->format == WXR_FORMAT_MONO && physdev_src->format != WXR_FORMAT_MONO)
1728 goto x11drv_fallback;
1730 /* if not stretching, we only need to handle format conversion */
1731 if (!stretch && physdev_dst->format == physdev_src->format) goto x11drv_fallback;
1733 if (rop != SRCCOPY)
1735 GC tmpGC;
1736 Pixmap tmp_pixmap;
1737 struct bitblt_coords tmp;
1739 /* make coordinates relative to tmp pixmap */
1740 tmp = *dst;
1741 tmp.x -= tmp.visrect.left;
1742 tmp.y -= tmp.visrect.top;
1743 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1745 tmpGC = XCreateGC( gdi_display, physdev_dst->x11dev->drawable, 0, NULL );
1746 XSetSubwindowMode( gdi_display, tmpGC, IncludeInferiors );
1747 XSetGraphicsExposures( gdi_display, tmpGC, False );
1748 tmp_pixmap = XCreatePixmap( gdi_display, root_window, tmp.visrect.right - tmp.visrect.left,
1749 tmp.visrect.bottom - tmp.visrect.top, physdev_dst->pict_format->depth );
1751 xrender_stretch_blit( physdev_src, physdev_dst, tmp_pixmap, src, &tmp );
1752 execute_rop( physdev_dst->x11dev, tmp_pixmap, tmpGC, &dst->visrect, rop );
1754 XFreePixmap( gdi_display, tmp_pixmap );
1755 XFreeGC( gdi_display, tmpGC );
1757 else xrender_stretch_blit( physdev_src, physdev_dst, 0, src, dst );
1759 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
1760 return TRUE;
1762 x11drv_fallback:
1763 return X11DRV_StretchBlt( &physdev_dst->x11dev->dev, dst, &physdev_src->x11dev->dev, src, rop );
1767 /***********************************************************************
1768 * xrenderdrv_PutImage
1770 static DWORD xrenderdrv_PutImage( PHYSDEV dev, HRGN clip, BITMAPINFO *info,
1771 const struct gdi_image_bits *bits, struct bitblt_coords *src,
1772 struct bitblt_coords *dst, DWORD rop )
1774 struct xrender_physdev *physdev = get_xrender_dev( dev );
1775 DWORD ret;
1776 Pixmap tmp_pixmap;
1777 GC gc;
1778 enum wxr_format src_format, dst_format;
1779 XRenderPictFormat *pict_format;
1780 Pixmap src_pixmap;
1781 Picture src_pict, mask_pict = 0;
1782 BOOL use_repeat;
1784 dst_format = physdev->format;
1785 src_format = get_xrender_format_from_bitmapinfo( info );
1786 if (!(pict_format = pict_formats[src_format])) goto update_format;
1788 /* make sure we can create an image with the same bpp */
1789 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1790 goto update_format;
1792 /* mono <-> color conversions not supported */
1793 if ((src_format != dst_format) && (src_format == WXR_FORMAT_MONO || dst_format == WXR_FORMAT_MONO))
1794 goto x11drv_fallback;
1796 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1798 if (!has_alpha( src_format ) && has_alpha( dst_format )) mask_pict = get_no_alpha_mask();
1800 ret = create_image_pixmap( info, bits, src, src_format, &src_pixmap, &src_pict, &use_repeat );
1801 if (!ret)
1803 struct bitblt_coords tmp;
1805 if (rop != SRCCOPY)
1807 BOOL restore_region = add_extra_clipping_region( physdev->x11dev, clip );
1809 /* make coordinates relative to tmp pixmap */
1810 tmp = *dst;
1811 tmp.x -= tmp.visrect.left;
1812 tmp.y -= tmp.visrect.top;
1813 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1815 gc = XCreateGC( gdi_display, physdev->x11dev->drawable, 0, NULL );
1816 XSetSubwindowMode( gdi_display, gc, IncludeInferiors );
1817 XSetGraphicsExposures( gdi_display, gc, False );
1818 tmp_pixmap = XCreatePixmap( gdi_display, root_window,
1819 tmp.visrect.right - tmp.visrect.left,
1820 tmp.visrect.bottom - tmp.visrect.top,
1821 physdev->pict_format->depth );
1823 xrender_put_image( src_pixmap, src_pict, mask_pict, NULL, physdev->pict_format,
1824 NULL, tmp_pixmap, src, &tmp, use_repeat );
1825 execute_rop( physdev->x11dev, tmp_pixmap, gc, &dst->visrect, rop );
1827 XFreePixmap( gdi_display, tmp_pixmap );
1828 XFreeGC( gdi_display, gc );
1829 if (restore_region) restore_clipping_region( physdev->x11dev );
1831 else xrender_put_image( src_pixmap, src_pict, mask_pict, clip,
1832 physdev->pict_format, physdev, 0, src, dst, use_repeat );
1834 add_device_bounds( physdev->x11dev, &dst->visrect );
1836 pXRenderFreePicture( gdi_display, src_pict );
1837 XFreePixmap( gdi_display, src_pixmap );
1839 return ret;
1841 update_format:
1842 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1843 set_color_info( pict_formats[dst_format], info );
1844 return ERROR_BAD_FORMAT;
1846 x11drv_fallback:
1847 dev = GET_NEXT_PHYSDEV( dev, pPutImage );
1848 return dev->funcs->pPutImage( dev, clip, info, bits, src, dst, rop );
1852 /***********************************************************************
1853 * xrenderdrv_BlendImage
1855 static DWORD xrenderdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
1856 struct bitblt_coords *src, struct bitblt_coords *dst,
1857 BLENDFUNCTION func )
1859 struct xrender_physdev *physdev = get_xrender_dev( dev );
1860 DWORD ret;
1861 enum wxr_format format;
1862 XRenderPictFormat *pict_format;
1863 Picture dst_pict, src_pict, mask_pict;
1864 Pixmap src_pixmap;
1865 BOOL use_repeat;
1867 format = get_xrender_format_from_bitmapinfo( info );
1868 if (!(func.AlphaFormat & AC_SRC_ALPHA))
1869 format = get_format_without_alpha( format );
1870 else if (format != WXR_FORMAT_A8R8G8B8 || info->bmiHeader.biCompression != BI_RGB)
1871 return ERROR_INVALID_PARAMETER;
1873 if (!(pict_format = pict_formats[format])) goto update_format;
1875 /* make sure we can create an image with the same bpp */
1876 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1877 goto update_format;
1879 if (format == WXR_FORMAT_MONO && physdev->format != WXR_FORMAT_MONO)
1880 goto update_format;
1882 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1884 ret = create_image_pixmap( info, bits, src, format, &src_pixmap, &src_pict, &use_repeat );
1885 if (!ret)
1887 double xscale, yscale;
1889 if (!use_repeat)
1891 xscale = src->width / (double)dst->width;
1892 yscale = src->height / (double)dst->height;
1894 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1896 dst_pict = get_xrender_picture( physdev, 0, &dst->visrect );
1898 pthread_mutex_lock( &xrender_mutex );
1899 mask_pict = get_mask_pict( func.SourceConstantAlpha * 257 );
1901 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1902 src->x, src->y, src->width, src->height,
1903 physdev->x11dev->dc_rect.left + dst->x,
1904 physdev->x11dev->dc_rect.top + dst->y,
1905 dst->width, dst->height, xscale, yscale );
1907 pXRenderFreePicture( gdi_display, src_pict );
1908 XFreePixmap( gdi_display, src_pixmap );
1910 pthread_mutex_unlock( &xrender_mutex );
1911 add_device_bounds( physdev->x11dev, &dst->visrect );
1913 return ret;
1915 update_format:
1916 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1917 set_color_info( physdev->pict_format, info );
1918 return ERROR_BAD_FORMAT;
1922 /***********************************************************************
1923 * xrenderdrv_AlphaBlend
1925 static BOOL xrenderdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
1926 PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blendfn )
1928 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1929 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1930 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
1931 XRenderPictureAttributes pa;
1932 Pixmap tmp_pixmap = 0;
1933 double xscale, yscale;
1935 if (src_dev->funcs != dst_dev->funcs)
1937 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pAlphaBlend );
1938 return dst_dev->funcs->pAlphaBlend( dst_dev, dst, src_dev, src, blendfn );
1941 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->format != WXR_FORMAT_A8R8G8B8)
1943 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1944 return FALSE;
1947 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1949 xscale = src->width / (double)dst->width;
1950 yscale = src->height / (double)dst->height;
1952 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1954 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1956 /* mono -> color blending needs an intermediate color pixmap */
1957 XRenderColor fg, bg;
1958 int width = src->visrect.right - src->visrect.left;
1959 int height = src->visrect.bottom - src->visrect.top;
1961 /* blending doesn't use the destination DC colors */
1962 fg.red = fg.green = fg.blue = 0;
1963 bg.red = bg.green = bg.blue = 0xffff;
1964 fg.alpha = bg.alpha = 0xffff;
1966 tmp_pixmap = XCreatePixmap( gdi_display, root_window, width, height,
1967 physdev_dst->pict_format->depth );
1968 tmp_pict = pXRenderCreatePicture( gdi_display, tmp_pixmap, physdev_dst->pict_format, 0, NULL );
1970 xrender_mono_blit( src_pict, tmp_pict, physdev_dst->format, &fg, &bg,
1971 src->visrect.left, src->visrect.top, width, height, 0, 0, width, height, 1, 1 );
1973 else if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->pict_format)
1975 /* we need a source picture with no alpha */
1976 enum wxr_format format = get_format_without_alpha( physdev_src->format );
1977 if (format != physdev_src->format)
1979 pa.subwindow_mode = IncludeInferiors;
1980 tmp_pict = pXRenderCreatePicture( gdi_display, physdev_src->x11dev->drawable,
1981 pict_formats[format], CPSubwindowMode, &pa );
1985 if (tmp_pict) src_pict = tmp_pict;
1987 pthread_mutex_lock( &xrender_mutex );
1988 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
1990 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1991 physdev_src->x11dev->dc_rect.left + src->x,
1992 physdev_src->x11dev->dc_rect.top + src->y,
1993 src->width, src->height,
1994 physdev_dst->x11dev->dc_rect.left + dst->x,
1995 physdev_dst->x11dev->dc_rect.top + dst->y,
1996 dst->width, dst->height, xscale, yscale );
1998 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
1999 if (tmp_pixmap) XFreePixmap( gdi_display, tmp_pixmap );
2001 pthread_mutex_unlock( &xrender_mutex );
2002 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
2003 return TRUE;
2006 /***********************************************************************
2007 * xrenderdrv_GradientFill
2009 static BOOL xrenderdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
2010 void *grad_array, ULONG ngrad, ULONG mode )
2012 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
2013 static const XFixed stops[2] = { 0, 1 << 16 };
2014 struct xrender_physdev *physdev = get_xrender_dev( dev );
2015 XLinearGradient gradient;
2016 XRenderColor colors[2];
2017 Picture src_pict, dst_pict;
2018 unsigned int i;
2019 const GRADIENT_RECT *rect = grad_array;
2020 RECT rc;
2021 POINT pt[2];
2023 if (!pXRenderCreateLinearGradient) goto fallback;
2025 /* <= 16-bpp uses dithering */
2026 if (!physdev->pict_format || physdev->pict_format->depth <= 16) goto fallback;
2028 switch (mode)
2030 case GRADIENT_FILL_RECT_H:
2031 case GRADIENT_FILL_RECT_V:
2032 for (i = 0; i < ngrad; i++, rect++)
2034 const TRIVERTEX *v1 = vert_array + rect->UpperLeft;
2035 const TRIVERTEX *v2 = vert_array + rect->LowerRight;
2037 colors[0].red = v1->Red * 257 / 256;
2038 colors[0].green = v1->Green * 257 / 256;
2039 colors[0].blue = v1->Blue * 257 / 256;
2040 colors[1].red = v2->Red * 257 / 256;
2041 colors[1].green = v2->Green * 257 / 256;
2042 colors[1].blue = v2->Blue * 257 / 256;
2043 /* always ignore alpha since otherwise xrender will want to pre-multiply the colors */
2044 colors[0].alpha = colors[1].alpha = 65535;
2046 pt[0].x = v1->x;
2047 pt[0].y = v1->y;
2048 pt[1].x = v2->x;
2049 pt[1].y = v2->y;
2050 lp_to_dp( dev->hdc, pt, 2 );
2051 if (mode == GRADIENT_FILL_RECT_H)
2053 gradient.p1.y = gradient.p2.y = 0;
2054 if (pt[1].x > pt[0].x)
2056 gradient.p1.x = 0;
2057 gradient.p2.x = (pt[1].x - pt[0].x) << 16;
2059 else
2061 gradient.p1.x = (pt[0].x - pt[1].x) << 16;
2062 gradient.p2.x = 0;
2065 else
2067 gradient.p1.x = gradient.p2.x = 0;
2068 if (pt[1].y > pt[0].y)
2070 gradient.p1.y = 0;
2071 gradient.p2.y = (pt[1].y - pt[0].y) << 16;
2073 else
2075 gradient.p1.y = (pt[0].y - pt[1].y) << 16;
2076 gradient.p2.y = 0;
2080 rc.left = min( pt[0].x, pt[1].x );
2081 rc.top = min( pt[0].y, pt[1].y );
2082 rc.right = max( pt[0].x, pt[1].x );
2083 rc.bottom = max( pt[0].y, pt[1].y );
2085 TRACE( "%u gradient %s colors %04x,%04x,%04x,%04x -> %04x,%04x,%04x,%04x\n",
2086 (int)mode, wine_dbgstr_rect( &rc ),
2087 colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha,
2088 colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha );
2090 dst_pict = get_xrender_picture( physdev, 0, NULL );
2092 src_pict = pXRenderCreateLinearGradient( gdi_display, &gradient, stops, colors, 2 );
2093 xrender_blit( PictOpSrc, src_pict, 0, dst_pict,
2094 0, 0, rc.right - rc.left, rc.bottom - rc.top,
2095 physdev->x11dev->dc_rect.left + rc.left,
2096 physdev->x11dev->dc_rect.top + rc.top,
2097 rc.right - rc.left, rc.bottom - rc.top, 1, 1 );
2098 pXRenderFreePicture( gdi_display, src_pict );
2099 add_device_bounds( physdev->x11dev, &rc );
2101 return TRUE;
2104 fallback:
2105 #endif
2106 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
2107 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
2110 /***********************************************************************
2111 * xrenderdrv_SelectBrush
2113 static HBRUSH xrenderdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2115 struct xrender_physdev *physdev = get_xrender_dev( dev );
2116 Pixmap pixmap;
2117 XVisualInfo vis = default_visual;
2118 XRenderPictFormat *format = physdev->pict_format;
2120 if (!pattern) goto x11drv_fallback;
2121 if (pattern->info->bmiHeader.biBitCount == 1) goto x11drv_fallback;
2122 if (physdev->format == WXR_FORMAT_MONO) goto x11drv_fallback;
2124 vis.depth = format->depth;
2125 vis.red_mask = format->direct.redMask << format->direct.red;
2126 vis.green_mask = format->direct.greenMask << format->direct.green;
2127 vis.blue_mask = format->direct.blueMask << format->direct.blue;
2129 pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, pattern->info,
2130 &pattern->bits, pattern->usage );
2131 if (!pixmap) return 0;
2133 if (physdev->x11dev->brush.pixmap) XFreePixmap( gdi_display, physdev->x11dev->brush.pixmap );
2134 physdev->x11dev->brush.pixmap = pixmap;
2135 physdev->x11dev->brush.fillStyle = FillTiled;
2136 physdev->x11dev->brush.pixel = 0; /* ignored */
2137 physdev->x11dev->brush.style = BS_PATTERN;
2138 return hbrush;
2140 x11drv_fallback:
2141 dev = GET_NEXT_PHYSDEV( dev, pSelectBrush );
2142 return dev->funcs->pSelectBrush( dev, hbrush, pattern );
2146 static const struct gdi_dc_funcs xrender_funcs =
2148 NULL, /* pAbortDoc */
2149 NULL, /* pAbortPath */
2150 xrenderdrv_AlphaBlend, /* pAlphaBlend */
2151 NULL, /* pAngleArc */
2152 NULL, /* pArc */
2153 NULL, /* pArcTo */
2154 NULL, /* pBeginPath */
2155 xrenderdrv_BlendImage, /* pBlendImage */
2156 NULL, /* pChord */
2157 NULL, /* pCloseFigure */
2158 xrenderdrv_CreateCompatibleDC, /* pCreateCompatibleDC */
2159 xrenderdrv_CreateDC, /* pCreateDC */
2160 xrenderdrv_DeleteDC, /* pDeleteDC */
2161 NULL, /* pDeleteObject */
2162 NULL, /* pEllipse */
2163 NULL, /* pEndDoc */
2164 NULL, /* pEndPage */
2165 NULL, /* pEndPath */
2166 NULL, /* pEnumFonts */
2167 xrenderdrv_ExtEscape, /* pExtEscape */
2168 NULL, /* pExtFloodFill */
2169 xrenderdrv_ExtTextOut, /* pExtTextOut */
2170 NULL, /* pFillPath */
2171 NULL, /* pFillRgn */
2172 NULL, /* pFontIsLinked */
2173 NULL, /* pFrameRgn */
2174 NULL, /* pGetBoundsRect */
2175 NULL, /* pGetCharABCWidths */
2176 NULL, /* pGetCharABCWidthsI */
2177 NULL, /* pGetCharWidth */
2178 NULL, /* pGetCharWidthInfo */
2179 NULL, /* pGetDeviceCaps */
2180 NULL, /* pGetDeviceGammaRamp */
2181 NULL, /* pGetFontData */
2182 NULL, /* pGetFontRealizationInfo */
2183 NULL, /* pGetFontUnicodeRanges */
2184 NULL, /* pGetGlyphIndices */
2185 NULL, /* pGetGlyphOutline */
2186 NULL, /* pGetICMProfile */
2187 NULL, /* pGetImage */
2188 NULL, /* pGetKerningPairs */
2189 NULL, /* pGetNearestColor */
2190 NULL, /* pGetOutlineTextMetrics */
2191 NULL, /* pGetPixel */
2192 NULL, /* pGetSystemPaletteEntries */
2193 NULL, /* pGetTextCharsetInfo */
2194 NULL, /* pGetTextExtentExPoint */
2195 NULL, /* pGetTextExtentExPointI */
2196 NULL, /* pGetTextFace */
2197 NULL, /* pGetTextMetrics */
2198 xrenderdrv_GradientFill, /* pGradientFill */
2199 NULL, /* pInvertRgn */
2200 NULL, /* pLineTo */
2201 NULL, /* pMoveTo */
2202 NULL, /* pPaintRgn */
2203 NULL, /* pPatBlt */
2204 NULL, /* pPie */
2205 NULL, /* pPolyBezier */
2206 NULL, /* pPolyBezierTo */
2207 NULL, /* pPolyDraw */
2208 NULL, /* pPolyPolygon */
2209 NULL, /* pPolyPolyline */
2210 NULL, /* pPolylineTo */
2211 xrenderdrv_PutImage, /* pPutImage */
2212 NULL, /* pRealizeDefaultPalette */
2213 NULL, /* pRealizePalette */
2214 NULL, /* pRectangle */
2215 NULL, /* pResetDC */
2216 NULL, /* pRoundRect */
2217 NULL, /* pSelectBitmap */
2218 xrenderdrv_SelectBrush, /* pSelectBrush */
2219 xrenderdrv_SelectFont, /* pSelectFont */
2220 NULL, /* pSelectPen */
2221 NULL, /* pSetBkColor */
2222 NULL, /* pSetBoundsRect */
2223 NULL, /* pSetDCBrushColor */
2224 NULL, /* pSetDCPenColor */
2225 NULL, /* pSetDIBitsToDevice */
2226 xrenderdrv_SetDeviceClipping, /* pSetDeviceClipping */
2227 NULL, /* pSetDeviceGammaRamp */
2228 NULL, /* pSetPixel */
2229 NULL, /* pSetTextColor */
2230 NULL, /* pStartDoc */
2231 NULL, /* pStartPage */
2232 xrenderdrv_StretchBlt, /* pStretchBlt */
2233 NULL, /* pStretchDIBits */
2234 NULL, /* pStrokeAndFillPath */
2235 NULL, /* pStrokePath */
2236 NULL, /* pUnrealizePalette */
2237 GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */
2240 #else /* SONAME_LIBXRENDER */
2242 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
2244 TRACE("XRender support not compiled in.\n");
2245 return NULL;
2248 #endif /* SONAME_LIBXRENDER */