dmime/tests: Add more GetTrack() tests.
[wine.git] / dlls / winex11.drv / xrender.c
blob6fe19902334d3c11f03c1b31bd3323e1f896cbf0
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
25 #include "config.h"
26 #include "wine/port.h"
28 #include <assert.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <stdlib.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "x11drv.h"
36 #include "winternl.h"
37 #include "wine/library.h"
38 #include "wine/unicode.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
43 #ifdef SONAME_LIBXRENDER
45 WINE_DECLARE_DEBUG_CHANNEL(winediag);
47 #include <X11/Xlib.h>
48 #include <X11/extensions/Xrender.h>
50 #ifndef RepeatNone /* added in 0.10 */
51 #define RepeatNone 0
52 #define RepeatNormal 1
53 #define RepeatPad 2
54 #define RepeatReflect 3
55 #endif
57 enum wxr_format
59 WXR_FORMAT_MONO,
60 WXR_FORMAT_GRAY,
61 WXR_FORMAT_X1R5G5B5,
62 WXR_FORMAT_X1B5G5R5,
63 WXR_FORMAT_R5G6B5,
64 WXR_FORMAT_B5G6R5,
65 WXR_FORMAT_R8G8B8,
66 WXR_FORMAT_B8G8R8,
67 WXR_FORMAT_A8R8G8B8,
68 WXR_FORMAT_B8G8R8A8,
69 WXR_FORMAT_X8R8G8B8,
70 WXR_FORMAT_B8G8R8X8,
71 WXR_FORMAT_ROOT, /* placeholder for the format to use on the root window */
72 WXR_NB_FORMATS,
73 WXR_INVALID_FORMAT = WXR_NB_FORMATS
76 typedef struct wine_xrender_format_template
78 unsigned int depth;
79 unsigned int alpha;
80 unsigned int alphaMask;
81 unsigned int red;
82 unsigned int redMask;
83 unsigned int green;
84 unsigned int greenMask;
85 unsigned int blue;
86 unsigned int blueMask;
87 } WineXRenderFormatTemplate;
89 static const WineXRenderFormatTemplate wxr_formats_template[WXR_NB_FORMATS] =
91 /* Format depth alpha mask red mask green mask blue mask*/
92 /* WXR_FORMAT_MONO */ { 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
93 /* WXR_FORMAT_GRAY */ { 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
94 /* WXR_FORMAT_X1R5G5B5 */ { 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
95 /* WXR_FORMAT_X1B5G5R5 */ { 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
96 /* WXR_FORMAT_R5G6B5 */ { 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
97 /* WXR_FORMAT_B5G6R5 */ { 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
98 /* WXR_FORMAT_R8G8B8 */ { 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
99 /* WXR_FORMAT_B8G8R8 */ { 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
100 /* WXR_FORMAT_A8R8G8B8 */ { 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
101 /* WXR_FORMAT_B8G8R8A8 */ { 32, 0, 0xff, 8, 0xff, 16, 0xff, 24, 0xff },
102 /* WXR_FORMAT_X8R8G8B8 */ { 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
103 /* WXR_FORMAT_B8G8R8X8 */ { 32, 0, 0, 8, 0xff, 16, 0xff, 24, 0xff },
106 static enum wxr_format default_format = WXR_INVALID_FORMAT;
107 static XRenderPictFormat *pict_formats[WXR_NB_FORMATS + 1 /* invalid format */];
109 typedef struct
111 LOGFONTW lf;
112 XFORM xform;
113 SIZE devsize; /* size in device coords */
114 DWORD hash;
115 } LFANDSIZE;
117 #define INITIAL_REALIZED_BUF_SIZE 128
119 enum glyph_type { GLYPH_INDEX, GLYPH_WCHAR, GLYPH_NBTYPES };
121 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
123 typedef struct
125 GlyphSet glyphset;
126 XRenderPictFormat *font_format;
127 int nrealized;
128 BOOL *realized;
129 XGlyphInfo *gis;
130 } gsCacheEntryFormat;
132 typedef struct
134 LFANDSIZE lfsz;
135 gsCacheEntryFormat *format[GLYPH_NBTYPES][AA_MAXVALUE];
136 INT count;
137 INT next;
138 } gsCacheEntry;
140 struct xrender_physdev
142 struct gdi_physdev dev;
143 X11DRV_PDEVICE *x11dev;
144 HRGN region;
145 enum wxr_format format;
146 UINT aa_flags;
147 int cache_index;
148 BOOL update_clip;
149 Picture pict;
150 Picture pict_src;
151 XRenderPictFormat *pict_format;
154 static inline struct xrender_physdev *get_xrender_dev( PHYSDEV dev )
156 return (struct xrender_physdev *)dev;
159 static const struct gdi_dc_funcs xrender_funcs;
161 static gsCacheEntry *glyphsetCache = NULL;
162 static DWORD glyphsetCacheSize = 0;
163 static INT lastfree = -1;
164 static INT mru = -1;
166 #define INIT_CACHE_SIZE 10
168 static void *xrender_handle;
170 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
171 MAKE_FUNCPTR(XRenderAddGlyphs)
172 MAKE_FUNCPTR(XRenderChangePicture)
173 MAKE_FUNCPTR(XRenderComposite)
174 MAKE_FUNCPTR(XRenderCompositeText16)
175 MAKE_FUNCPTR(XRenderCreateGlyphSet)
176 MAKE_FUNCPTR(XRenderCreatePicture)
177 MAKE_FUNCPTR(XRenderFillRectangle)
178 MAKE_FUNCPTR(XRenderFindFormat)
179 MAKE_FUNCPTR(XRenderFindVisualFormat)
180 MAKE_FUNCPTR(XRenderFreeGlyphSet)
181 MAKE_FUNCPTR(XRenderFreePicture)
182 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
183 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
184 MAKE_FUNCPTR(XRenderCreateLinearGradient)
185 #endif
186 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
187 MAKE_FUNCPTR(XRenderSetPictureTransform)
188 #endif
189 MAKE_FUNCPTR(XRenderQueryExtension)
191 #undef MAKE_FUNCPTR
193 static CRITICAL_SECTION xrender_cs;
194 static CRITICAL_SECTION_DEBUG critsect_debug =
196 0, 0, &xrender_cs,
197 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
198 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
200 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
202 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
203 ( ( (ULONG)_x4 << 24 ) | \
204 ( (ULONG)_x3 << 16 ) | \
205 ( (ULONG)_x2 << 8 ) | \
206 (ULONG)_x1 )
208 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
210 #define GASP_GRIDFIT 0x01
211 #define GASP_DOGRAY 0x02
213 #ifdef WORDS_BIGENDIAN
214 #define get_be_word(x) (x)
215 #define NATIVE_BYTE_ORDER MSBFirst
216 #else
217 #define get_be_word(x) RtlUshortByteSwap(x)
218 #define NATIVE_BYTE_ORDER LSBFirst
219 #endif
221 static BOOL has_alpha( enum wxr_format format )
223 return (format == WXR_FORMAT_A8R8G8B8 || format == WXR_FORMAT_B8G8R8A8);
226 static enum wxr_format get_format_without_alpha( enum wxr_format format )
228 switch (format)
230 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
231 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
232 default: return format;
236 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
238 templ->id = 0;
239 templ->type = PictTypeDirect;
240 templ->depth = fmt->depth;
241 templ->direct.alpha = fmt->alpha;
242 templ->direct.alphaMask = fmt->alphaMask;
243 templ->direct.red = fmt->red;
244 templ->direct.redMask = fmt->redMask;
245 templ->direct.green = fmt->green;
246 templ->direct.greenMask = fmt->greenMask;
247 templ->direct.blue = fmt->blue;
248 templ->direct.blueMask = fmt->blueMask;
249 templ->colormap = 0;
251 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
253 return TRUE;
256 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
258 if(fmt->depth != default_visual.depth) return FALSE;
259 if( (fmt->redMask << fmt->red) != default_visual.red_mask) return FALSE;
260 if( (fmt->greenMask << fmt->green) != default_visual.green_mask) return FALSE;
261 if( (fmt->blueMask << fmt->blue) != default_visual.blue_mask) return FALSE;
263 /* We never select a default ARGB visual */
264 if(fmt->alphaMask) return FALSE;
265 return TRUE;
268 static int load_xrender_formats(void)
270 int count = 0;
271 unsigned int i;
273 for (i = 0; i < WXR_NB_FORMATS; i++)
275 XRenderPictFormat templ;
277 if (i == WXR_FORMAT_ROOT)
279 pict_formats[i] = pXRenderFindVisualFormat(gdi_display,
280 DefaultVisual( gdi_display, DefaultScreen(gdi_display) ));
281 TRACE( "Loaded root pict_format with id=%#lx\n", pict_formats[i]->id );
282 continue;
284 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
286 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, default_visual.visual);
287 if (!pict_formats[i])
289 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
290 if (default_visual.class == DirectColor)
292 XVisualInfo info;
293 if (XMatchVisualInfo( gdi_display, default_visual.screen,
294 default_visual.depth, TrueColor, &info ))
296 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, info.visual);
297 if (pict_formats[i]) default_visual = info;
301 if (pict_formats[i]) default_format = i;
303 else
305 unsigned long mask = 0;
306 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
307 pict_formats[i] = pXRenderFindFormat(gdi_display, mask, &templ, 0);
309 if (pict_formats[i])
311 count++;
312 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_formats[i]->id, i);
315 return count;
318 /***********************************************************************
319 * X11DRV_XRender_Init
321 * Let's see if our XServer has the extension available
324 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
326 int event_base, i;
328 if (!client_side_with_render) return NULL;
329 if (!(xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0))) return NULL;
331 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) return NULL
332 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0)
333 LOAD_FUNCPTR(XRenderAddGlyphs);
334 LOAD_FUNCPTR(XRenderChangePicture);
335 LOAD_FUNCPTR(XRenderComposite);
336 LOAD_FUNCPTR(XRenderCompositeText16);
337 LOAD_FUNCPTR(XRenderCreateGlyphSet);
338 LOAD_FUNCPTR(XRenderCreatePicture);
339 LOAD_FUNCPTR(XRenderFillRectangle);
340 LOAD_FUNCPTR(XRenderFindFormat);
341 LOAD_FUNCPTR(XRenderFindVisualFormat);
342 LOAD_FUNCPTR(XRenderFreeGlyphSet);
343 LOAD_FUNCPTR(XRenderFreePicture);
344 LOAD_FUNCPTR(XRenderSetPictureClipRectangles);
345 LOAD_FUNCPTR(XRenderQueryExtension);
346 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
347 LOAD_OPTIONAL_FUNCPTR(XRenderCreateLinearGradient);
348 #endif
349 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
350 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform);
351 #endif
352 #undef LOAD_OPTIONAL_FUNCPTR
353 #undef LOAD_FUNCPTR
355 if (!pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) return NULL;
357 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
358 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
360 ERR_(winediag)("Wine has detected that you probably have a buggy version "
361 "of libXrender. Because of this client side font rendering "
362 "will be disabled. Please upgrade this library.\n");
363 return NULL;
366 if (!default_visual.red_mask || !default_visual.green_mask || !default_visual.blue_mask)
368 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
369 return NULL;
372 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
373 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
375 glyphsetCacheSize = INIT_CACHE_SIZE;
376 lastfree = 0;
377 for(i = 0; i < INIT_CACHE_SIZE; i++) {
378 glyphsetCache[i].next = i + 1;
379 glyphsetCache[i].count = -1;
381 glyphsetCache[i-1].next = -1;
383 return &xrender_funcs;
386 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
387 static void get_xrender_color( struct xrender_physdev *physdev, COLORREF src_color, XRenderColor *dst_color )
389 if (src_color & (1 << 24)) /* PALETTEINDEX */
391 HPALETTE pal = GetCurrentObject( physdev->dev.hdc, OBJ_PAL );
392 PALETTEENTRY pal_ent;
394 if (!GetPaletteEntries( pal, LOWORD(src_color), 1, &pal_ent ))
395 GetPaletteEntries( pal, 0, 1, &pal_ent );
396 dst_color->red = pal_ent.peRed * 257;
397 dst_color->green = pal_ent.peGreen * 257;
398 dst_color->blue = pal_ent.peBlue * 257;
400 else
402 if (src_color >> 16 == 0x10ff) src_color = 0; /* DIBINDEX */
404 dst_color->red = GetRValue( src_color ) * 257;
405 dst_color->green = GetGValue( src_color ) * 257;
406 dst_color->blue = GetBValue( src_color ) * 257;
409 if (physdev->format == WXR_FORMAT_MONO && !dst_color->red && !dst_color->green && !dst_color->blue)
410 dst_color->alpha = 0;
411 else
412 dst_color->alpha = 0xffff;
415 static enum wxr_format get_xrender_format_from_bitmapinfo( const BITMAPINFO *info )
417 if (info->bmiHeader.biPlanes != 1) return WXR_INVALID_FORMAT;
419 switch (info->bmiHeader.biBitCount)
421 case 1:
422 return WXR_FORMAT_MONO;
423 case 4:
424 case 8:
425 break;
426 case 24:
427 if (info->bmiHeader.biCompression != BI_RGB) break;
428 return WXR_FORMAT_R8G8B8;
429 case 16:
430 case 32:
431 if (info->bmiHeader.biCompression == BI_BITFIELDS)
433 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
434 unsigned int i;
436 for (i = 0; i < WXR_NB_FORMATS; i++)
438 if (info->bmiHeader.biBitCount == wxr_formats_template[i].depth &&
439 colors[0] == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
440 colors[1] == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
441 colors[2] == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue))
442 return i;
444 break;
446 if (info->bmiHeader.biCompression != BI_RGB) break;
447 return (info->bmiHeader.biBitCount == 16) ? WXR_FORMAT_X1R5G5B5 : WXR_FORMAT_A8R8G8B8;
449 return WXR_INVALID_FORMAT;
452 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
453 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
455 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
456 XTransform xform = {{
457 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
458 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
459 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
462 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
463 #endif
466 static void update_xrender_clipping( struct xrender_physdev *dev, HRGN rgn )
468 XRenderPictureAttributes pa;
469 RGNDATA *data;
471 if (!rgn)
473 pa.clip_mask = None;
474 pXRenderChangePicture( gdi_display, dev->pict, CPClipMask, &pa );
476 else if ((data = X11DRV_GetRegionData( rgn, 0 )))
478 pXRenderSetPictureClipRectangles( gdi_display, dev->pict,
479 dev->x11dev->dc_rect.left, dev->x11dev->dc_rect.top,
480 (XRectangle *)data->Buffer, data->rdh.nCount );
481 HeapFree( GetProcessHeap(), 0, data );
486 static Picture get_xrender_picture( struct xrender_physdev *dev, HRGN clip_rgn, const RECT *clip_rect )
488 if (!dev->pict && dev->pict_format)
490 XRenderPictureAttributes pa;
492 pa.subwindow_mode = IncludeInferiors;
493 dev->pict = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
494 dev->pict_format, CPSubwindowMode, &pa );
495 TRACE( "Allocing pict=%lx dc=%p drawable=%08lx\n",
496 dev->pict, dev->dev.hdc, dev->x11dev->drawable );
497 dev->update_clip = (dev->region != 0);
500 if (clip_rect)
502 HRGN rgn = CreateRectRgnIndirect( clip_rect );
503 if (clip_rgn) CombineRgn( rgn, rgn, clip_rgn, RGN_AND );
504 if (dev->region) CombineRgn( rgn, rgn, dev->region, RGN_AND );
505 update_xrender_clipping( dev, rgn );
506 DeleteObject( rgn );
508 else if (clip_rgn)
510 if (dev->region)
512 HRGN rgn = CreateRectRgn( 0, 0, 0, 0 );
513 CombineRgn( rgn, clip_rgn, dev->region, RGN_AND );
514 update_xrender_clipping( dev, rgn );
515 DeleteObject( rgn );
517 else update_xrender_clipping( dev, clip_rgn );
519 else if (dev->update_clip) update_xrender_clipping( dev, dev->region );
521 dev->update_clip = (clip_rect || clip_rgn); /* have to update again if we are using a custom region */
522 return dev->pict;
525 static Picture get_xrender_picture_source( struct xrender_physdev *dev, BOOL repeat )
527 if (!dev->pict_src && dev->pict_format)
529 XRenderPictureAttributes pa;
531 pa.subwindow_mode = IncludeInferiors;
532 pa.repeat = repeat ? RepeatNormal : RepeatNone;
533 dev->pict_src = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
534 dev->pict_format, CPSubwindowMode|CPRepeat, &pa );
536 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
537 dev->pict_src, dev->dev.hdc, dev->x11dev->drawable, pa.repeat);
540 return dev->pict_src;
543 static void free_xrender_picture( struct xrender_physdev *dev )
545 if (dev->pict || dev->pict_src)
547 XFlush( gdi_display );
548 if (dev->pict)
550 TRACE("freeing pict = %lx dc = %p\n", dev->pict, dev->dev.hdc);
551 pXRenderFreePicture(gdi_display, dev->pict);
552 dev->pict = 0;
554 if(dev->pict_src)
556 TRACE("freeing pict = %lx dc = %p\n", dev->pict_src, dev->dev.hdc);
557 pXRenderFreePicture(gdi_display, dev->pict_src);
558 dev->pict_src = 0;
563 /* return a mask picture used to force alpha to 0 */
564 static Picture get_no_alpha_mask(void)
566 static Pixmap pixmap;
567 static Picture pict;
569 EnterCriticalSection( &xrender_cs );
570 if (!pict)
572 XRenderPictureAttributes pa;
573 XRenderColor col;
575 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
576 pa.repeat = RepeatNormal;
577 pa.component_alpha = True;
578 pict = pXRenderCreatePicture( gdi_display, pixmap, pict_formats[WXR_FORMAT_A8R8G8B8],
579 CPRepeat|CPComponentAlpha, &pa );
580 col.red = col.green = col.blue = 0xffff;
581 col.alpha = 0;
582 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
584 LeaveCriticalSection( &xrender_cs );
585 return pict;
588 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
590 if(p1->hash != p2->hash) return TRUE;
591 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
592 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
593 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
594 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
597 static int LookupEntry(LFANDSIZE *plfsz)
599 int i, prev_i = -1;
601 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
602 TRACE("%d\n", i);
603 if(glyphsetCache[i].count == -1) break; /* reached free list so stop */
605 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
606 glyphsetCache[i].count++;
607 if(prev_i >= 0) {
608 glyphsetCache[prev_i].next = glyphsetCache[i].next;
609 glyphsetCache[i].next = mru;
610 mru = i;
612 TRACE("found font in cache %d\n", i);
613 return i;
615 prev_i = i;
617 TRACE("font not in cache\n");
618 return -1;
621 static void FreeEntry(int entry)
623 int type, format;
625 for (type = 0; type < GLYPH_NBTYPES; type++)
627 for(format = 0; format < AA_MAXVALUE; format++) {
628 gsCacheEntryFormat * formatEntry;
630 if( !glyphsetCache[entry].format[type][format] )
631 continue;
633 formatEntry = glyphsetCache[entry].format[type][format];
635 if(formatEntry->glyphset) {
636 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
637 formatEntry->glyphset = 0;
639 if(formatEntry->nrealized) {
640 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
641 formatEntry->realized = NULL;
642 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
643 formatEntry->gis = NULL;
644 formatEntry->nrealized = 0;
647 HeapFree(GetProcessHeap(), 0, formatEntry);
648 glyphsetCache[entry].format[type][format] = NULL;
653 static int AllocEntry(void)
655 int best = -1, prev_best = -1, i, prev_i = -1;
657 if(lastfree >= 0) {
658 assert(glyphsetCache[lastfree].count == -1);
659 glyphsetCache[lastfree].count = 1;
660 best = lastfree;
661 lastfree = glyphsetCache[lastfree].next;
662 assert(best != mru);
663 glyphsetCache[best].next = mru;
664 mru = best;
666 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
667 return mru;
670 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
671 if(glyphsetCache[i].count == 0) {
672 best = i;
673 prev_best = prev_i;
675 prev_i = i;
678 if(best >= 0) {
679 TRACE("freeing unused glyphset at cache %d\n", best);
680 FreeEntry(best);
681 glyphsetCache[best].count = 1;
682 if(prev_best >= 0) {
683 glyphsetCache[prev_best].next = glyphsetCache[best].next;
684 glyphsetCache[best].next = mru;
685 mru = best;
686 } else {
687 assert(mru == best);
689 return mru;
692 TRACE("Growing cache\n");
694 if (glyphsetCache)
695 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
696 glyphsetCache,
697 (glyphsetCacheSize + INIT_CACHE_SIZE)
698 * sizeof(*glyphsetCache));
699 else
700 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
701 (glyphsetCacheSize + INIT_CACHE_SIZE)
702 * sizeof(*glyphsetCache));
704 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
705 i++) {
706 glyphsetCache[i].next = i + 1;
707 glyphsetCache[i].count = -1;
709 glyphsetCache[i-1].next = -1;
710 glyphsetCacheSize += INIT_CACHE_SIZE;
712 lastfree = glyphsetCache[best].next;
713 glyphsetCache[best].count = 1;
714 glyphsetCache[best].next = mru;
715 mru = best;
716 TRACE("new free cache slot at %d\n", mru);
717 return mru;
720 static int GetCacheEntry( LFANDSIZE *plfsz )
722 int ret;
723 gsCacheEntry *entry;
725 if((ret = LookupEntry(plfsz)) != -1) return ret;
727 ret = AllocEntry();
728 entry = glyphsetCache + ret;
729 entry->lfsz = *plfsz;
730 return ret;
733 static void dec_ref_cache(int index)
735 assert(index >= 0);
736 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
737 assert(glyphsetCache[index].count > 0);
738 glyphsetCache[index].count--;
741 static void lfsz_calc_hash(LFANDSIZE *plfsz)
743 DWORD hash = 0, *ptr, two_chars;
744 WORD *pwc;
745 unsigned int i;
747 hash ^= plfsz->devsize.cx;
748 hash ^= plfsz->devsize.cy;
749 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
750 hash ^= *ptr;
751 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
752 hash ^= *ptr;
753 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
754 two_chars = *ptr;
755 pwc = (WCHAR *)&two_chars;
756 if(!*pwc) break;
757 *pwc = toupperW(*pwc);
758 pwc++;
759 *pwc = toupperW(*pwc);
760 hash ^= two_chars;
761 if(!*pwc) break;
763 plfsz->hash = hash;
764 return;
767 static AA_Type aa_type_from_flags( UINT aa_flags )
769 switch (aa_flags & 0x7f)
771 case GGO_BITMAP:
772 return AA_None;
773 case WINE_GGO_GRAY16_BITMAP:
774 return AA_Grey;
775 case WINE_GGO_HRGB_BITMAP:
776 return AA_RGB;
777 case WINE_GGO_HBGR_BITMAP:
778 return AA_BGR;
779 case WINE_GGO_VRGB_BITMAP:
780 return AA_VRGB;
781 case WINE_GGO_VBGR_BITMAP:
782 return AA_VBGR;
783 default:
784 FIXME( "unknown flags %x\n", aa_flags );
785 return AA_None;
789 static UINT get_xft_aa_flags( const LOGFONTW *lf )
791 char *value;
792 UINT ret = 0;
794 switch (lf->lfQuality)
796 case NONANTIALIASED_QUALITY:
797 case ANTIALIASED_QUALITY:
798 break;
799 default:
800 if (!(value = XGetDefault( gdi_display, "Xft", "antialias" ))) break;
801 TRACE( "got antialias '%s'\n", value );
802 if (tolower(value[0]) == 'f' || tolower(value[0]) == 'n' ||
803 value[0] == '0' || !_strnicmp( value, "off", -1 ))
805 ret = GGO_BITMAP;
806 break;
808 ret = GGO_GRAY4_BITMAP;
809 /* fall through */
810 case CLEARTYPE_QUALITY:
811 case CLEARTYPE_NATURAL_QUALITY:
812 if (!(value = XGetDefault( gdi_display, "Xft", "rgba" ))) break;
813 TRACE( "got rgba '%s'\n", value );
814 if (!strcmp( value, "rgb" )) ret = WINE_GGO_HRGB_BITMAP;
815 else if (!strcmp( value, "bgr" )) ret = WINE_GGO_HBGR_BITMAP;
816 else if (!strcmp( value, "vrgb" )) ret = WINE_GGO_VRGB_BITMAP;
817 else if (!strcmp( value, "vbgr" )) ret = WINE_GGO_VBGR_BITMAP;
818 else if (!strcmp( value, "none" )) ret = GGO_GRAY4_BITMAP;
819 break;
821 return ret;
824 /**********************************************************************
825 * xrenderdrv_SelectFont
827 static HFONT CDECL xrenderdrv_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
829 LFANDSIZE lfsz;
830 struct xrender_physdev *physdev = get_xrender_dev( dev );
831 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
832 HFONT ret;
834 GetObjectW( hfont, sizeof(lfsz.lf), &lfsz.lf );
835 if (!*aa_flags) *aa_flags = get_xft_aa_flags( &lfsz.lf );
837 ret = next->funcs->pSelectFont( next, hfont, aa_flags );
838 if (!ret) return 0;
840 switch (*aa_flags)
842 case GGO_GRAY2_BITMAP:
843 case GGO_GRAY4_BITMAP:
844 case GGO_GRAY8_BITMAP:
845 physdev->aa_flags = WINE_GGO_GRAY16_BITMAP;
846 break;
847 case 0:
848 physdev->aa_flags = GGO_BITMAP;
849 break;
850 default:
851 physdev->aa_flags = *aa_flags;
852 break;
855 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
856 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
857 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
858 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
859 lfsz.devsize.cx = X11DRV_XWStoDS( dev->hdc, lfsz.lf.lfWidth );
860 lfsz.devsize.cy = X11DRV_YWStoDS( dev->hdc, lfsz.lf.lfHeight );
862 GetTransform( dev->hdc, 0x204, &lfsz.xform );
863 TRACE("font transform %f %f %f %f\n", lfsz.xform.eM11, lfsz.xform.eM12,
864 lfsz.xform.eM21, lfsz.xform.eM22);
866 if (GetGraphicsMode( dev->hdc ) == GM_COMPATIBLE)
868 lfsz.lf.lfOrientation = lfsz.lf.lfEscapement;
869 if (lfsz.xform.eM11 * lfsz.xform.eM22 < 0)
870 lfsz.lf.lfOrientation = -lfsz.lf.lfOrientation;
873 /* Not used fields, would break hashing */
874 lfsz.xform.eDx = lfsz.xform.eDy = 0;
876 lfsz_calc_hash(&lfsz);
878 EnterCriticalSection(&xrender_cs);
879 if (physdev->cache_index != -1)
880 dec_ref_cache( physdev->cache_index );
881 physdev->cache_index = GetCacheEntry( &lfsz );
882 LeaveCriticalSection(&xrender_cs);
883 return ret;
886 static void set_physdev_format( struct xrender_physdev *physdev, enum wxr_format format )
888 if (physdev->x11dev->drawable == DefaultRootWindow( gdi_display ))
889 physdev->format = WXR_FORMAT_ROOT;
890 else
891 physdev->format = format;
893 physdev->pict_format = pict_formats[physdev->format];
896 static BOOL create_xrender_dc( PHYSDEV *pdev, enum wxr_format format )
898 X11DRV_PDEVICE *x11dev = get_x11drv_dev( *pdev );
899 struct xrender_physdev *physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) );
901 if (!physdev) return FALSE;
902 physdev->x11dev = x11dev;
903 physdev->cache_index = -1;
904 set_physdev_format( physdev, format );
905 push_dc_driver( pdev, &physdev->dev, &xrender_funcs );
906 return TRUE;
909 /* store the color mask data in the bitmap info structure */
910 static void set_color_info( XRenderPictFormat *format, BITMAPINFO *info )
912 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
914 info->bmiHeader.biPlanes = 1;
915 info->bmiHeader.biBitCount = pixmap_formats[format->depth]->bits_per_pixel;
916 info->bmiHeader.biCompression = BI_RGB;
917 info->bmiHeader.biClrUsed = 0;
919 switch (info->bmiHeader.biBitCount)
921 case 16:
922 colors[0] = format->direct.redMask << format->direct.red;
923 colors[1] = format->direct.greenMask << format->direct.green;
924 colors[2] = format->direct.blueMask << format->direct.blue;
925 info->bmiHeader.biCompression = BI_BITFIELDS;
926 break;
927 case 32:
928 colors[0] = format->direct.redMask << format->direct.red;
929 colors[1] = format->direct.greenMask << format->direct.green;
930 colors[2] = format->direct.blueMask << format->direct.blue;
931 if (colors[0] != 0xff0000 || colors[1] != 0x00ff00 || colors[2] != 0x0000ff)
932 info->bmiHeader.biCompression = BI_BITFIELDS;
933 break;
938 /**********************************************************************
939 * xrenderdrv_CreateDC
941 static BOOL CDECL xrenderdrv_CreateDC( PHYSDEV *pdev, LPCWSTR driver, LPCWSTR device,
942 LPCWSTR output, const DEVMODEW* initData )
944 return create_xrender_dc( pdev, default_format );
947 /**********************************************************************
948 * xrenderdrv_CreateCompatibleDC
950 static BOOL CDECL xrenderdrv_CreateCompatibleDC( PHYSDEV orig, PHYSDEV *pdev )
952 if (orig) /* chain to x11drv first */
954 orig = GET_NEXT_PHYSDEV( orig, pCreateCompatibleDC );
955 if (!orig->funcs->pCreateCompatibleDC( orig, pdev )) return FALSE;
957 /* otherwise we have been called by x11drv */
959 return create_xrender_dc( pdev, WXR_FORMAT_MONO );
962 /**********************************************************************
963 * xrenderdrv_DeleteDC
965 static BOOL CDECL xrenderdrv_DeleteDC( PHYSDEV dev )
967 struct xrender_physdev *physdev = get_xrender_dev( dev );
969 free_xrender_picture( physdev );
971 EnterCriticalSection( &xrender_cs );
972 if (physdev->cache_index != -1) dec_ref_cache( physdev->cache_index );
973 LeaveCriticalSection( &xrender_cs );
975 HeapFree( GetProcessHeap(), 0, physdev );
976 return TRUE;
979 /**********************************************************************
980 * xrenderdrv_ExtEscape
982 static INT CDECL xrenderdrv_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOID in_data,
983 INT out_count, LPVOID out_data )
985 struct xrender_physdev *physdev = get_xrender_dev( dev );
987 dev = GET_NEXT_PHYSDEV( dev, pExtEscape );
989 if (escape == X11DRV_ESCAPE && in_data && in_count >= sizeof(enum x11drv_escape_codes))
991 if (*(const enum x11drv_escape_codes *)in_data == X11DRV_SET_DRAWABLE)
993 BOOL ret = dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
994 if (ret)
996 free_xrender_picture( physdev );
997 set_physdev_format( physdev, default_format );
999 return ret;
1002 return dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
1005 /***********************************************************************
1006 * xrenderdrv_SetDeviceClipping
1008 static void CDECL xrenderdrv_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
1010 struct xrender_physdev *physdev = get_xrender_dev( dev );
1012 physdev->region = rgn;
1013 physdev->update_clip = TRUE;
1015 dev = GET_NEXT_PHYSDEV( dev, pSetDeviceClipping );
1016 dev->funcs->pSetDeviceClipping( dev, rgn );
1020 /************************************************************************
1021 * UploadGlyph
1023 * Helper to ExtTextOut. Must be called inside xrender_cs
1025 static void UploadGlyph(struct xrender_physdev *physDev, UINT glyph, enum glyph_type type)
1027 unsigned int buflen;
1028 char *buf;
1029 Glyph gid;
1030 GLYPHMETRICS gm;
1031 XGlyphInfo gi;
1032 gsCacheEntry *entry = glyphsetCache + physDev->cache_index;
1033 gsCacheEntryFormat *formatEntry;
1034 UINT ggo_format = physDev->aa_flags;
1035 AA_Type format = aa_type_from_flags( physDev->aa_flags );
1036 enum wxr_format wxr_format;
1037 static const char zero[4];
1038 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1040 if (type == GLYPH_INDEX) ggo_format |= GGO_GLYPH_INDEX;
1041 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1042 if(buflen == GDI_ERROR) {
1043 if(format != AA_None) {
1044 format = AA_None;
1045 physDev->aa_flags = GGO_BITMAP;
1046 ggo_format = (ggo_format & GGO_GLYPH_INDEX) | GGO_BITMAP;
1047 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1049 if(buflen == GDI_ERROR) {
1050 WARN("GetGlyphOutlineW failed using default glyph\n");
1051 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0, ggo_format, &gm, 0, NULL, &identity);
1052 if(buflen == GDI_ERROR) {
1053 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1054 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0x20, ggo_format, &gm, 0, NULL, &identity);
1055 if(buflen == GDI_ERROR) {
1056 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1057 return;
1061 TRACE("Turning off antialiasing for this monochrome font\n");
1064 /* If there is nothing for the current type, we create the entry. */
1065 if( !entry->format[type][format] ) {
1066 entry->format[type][format] = HeapAlloc(GetProcessHeap(),
1067 HEAP_ZERO_MEMORY,
1068 sizeof(gsCacheEntryFormat));
1070 formatEntry = entry->format[type][format];
1072 if(formatEntry->nrealized <= glyph) {
1073 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1075 if (formatEntry->realized)
1076 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1077 HEAP_ZERO_MEMORY,
1078 formatEntry->realized,
1079 formatEntry->nrealized * sizeof(BOOL));
1080 else
1081 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1082 HEAP_ZERO_MEMORY,
1083 formatEntry->nrealized * sizeof(BOOL));
1085 if (formatEntry->gis)
1086 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1087 HEAP_ZERO_MEMORY,
1088 formatEntry->gis,
1089 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1090 else
1091 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1092 HEAP_ZERO_MEMORY,
1093 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1097 if(formatEntry->glyphset == 0) {
1098 switch(format) {
1099 case AA_Grey:
1100 wxr_format = WXR_FORMAT_GRAY;
1101 break;
1103 case AA_RGB:
1104 case AA_BGR:
1105 case AA_VRGB:
1106 case AA_VBGR:
1107 wxr_format = WXR_FORMAT_A8R8G8B8;
1108 break;
1110 default:
1111 ERR("aa = %d - not implemented\n", format);
1112 /* fall through */
1113 case AA_None:
1114 wxr_format = WXR_FORMAT_MONO;
1115 break;
1118 formatEntry->font_format = pict_formats[wxr_format];
1119 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
1123 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1124 if (buflen)
1125 GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1126 else
1127 gm.gmBlackBoxX = gm.gmBlackBoxY = 0; /* empty glyph */
1128 formatEntry->realized[glyph] = TRUE;
1130 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1131 buflen,
1132 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1133 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1135 gi.width = gm.gmBlackBoxX;
1136 gi.height = gm.gmBlackBoxY;
1137 gi.x = -gm.gmptGlyphOrigin.x;
1138 gi.y = gm.gmptGlyphOrigin.y;
1139 gi.xOff = gm.gmCellIncX;
1140 gi.yOff = gm.gmCellIncY;
1142 if(TRACE_ON(xrender)) {
1143 int pitch, i, j;
1144 char output[300];
1145 unsigned char *line;
1147 if(format == AA_None) {
1148 pitch = ((gi.width + 31) / 32) * 4;
1149 for(i = 0; i < gi.height; i++) {
1150 line = (unsigned char*) buf + i * pitch;
1151 output[0] = '\0';
1152 for(j = 0; j < pitch * 8; j++) {
1153 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1155 TRACE("%s\n", output);
1157 } else {
1158 static const char blks[] = " .:;!o*#";
1159 char str[2];
1161 str[1] = '\0';
1162 pitch = ((gi.width + 3) / 4) * 4;
1163 for(i = 0; i < gi.height; i++) {
1164 line = (unsigned char*) buf + i * pitch;
1165 output[0] = '\0';
1166 for(j = 0; j < pitch; j++) {
1167 str[0] = blks[line[j] >> 5];
1168 strcat(output, str);
1170 TRACE("%s\n", output);
1176 if(formatEntry->glyphset) {
1177 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1178 unsigned char *byte = (unsigned char*) buf, c;
1179 int i = buflen;
1181 while(i--) {
1182 c = *byte;
1184 /* magic to flip bit order */
1185 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1186 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1187 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1189 *byte++ = c;
1192 else if ( format != AA_Grey &&
1193 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1195 unsigned int i, *data = (unsigned int *)buf;
1196 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1198 gid = glyph;
1201 XRenderCompositeText seems to ignore 0x0 glyphs when
1202 AA_None, which means we lose the advance width of glyphs
1203 like the space. We'll pretend that such glyphs are 1x1
1204 bitmaps.
1207 if(buflen == 0)
1208 gi.width = gi.height = 1;
1210 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1211 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1214 HeapFree(GetProcessHeap(), 0, buf);
1215 formatEntry->gis[glyph] = gi;
1218 /*************************************************************
1219 * get_tile_pict
1221 * Returns an appropriate Picture for tiling the text colour.
1222 * Call and use result within the xrender_cs
1224 static Picture get_tile_pict( enum wxr_format wxr_format, const XRenderColor *color)
1226 static struct
1228 Pixmap xpm;
1229 Picture pict;
1230 XRenderColor current_color;
1231 } tiles[WXR_NB_FORMATS], *tile;
1233 tile = &tiles[wxr_format];
1235 if(!tile->xpm)
1237 XRenderPictureAttributes pa;
1238 XRenderPictFormat *pict_format = pict_formats[wxr_format];
1240 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_format->depth);
1242 pa.repeat = RepeatNormal;
1243 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_format, CPRepeat, &pa);
1245 /* init current_color to something different from text_pixel */
1246 tile->current_color = *color;
1247 tile->current_color.red ^= 0xffff;
1249 if (wxr_format == WXR_FORMAT_MONO)
1251 /* for a 1bpp bitmap we always need a 1 in the tile */
1252 XRenderColor col;
1253 col.red = col.green = col.blue = 0;
1254 col.alpha = 0xffff;
1255 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1259 if (memcmp( color, &tile->current_color, sizeof(*color) ) && wxr_format != WXR_FORMAT_MONO)
1261 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, color, 0, 0, 1, 1);
1262 tile->current_color = *color;
1264 return tile->pict;
1267 /*************************************************************
1268 * get_mask_pict
1270 * Returns an appropriate Picture for masking with the specified alpha.
1271 * Call and use result within the xrender_cs
1273 static Picture get_mask_pict( int alpha )
1275 static Pixmap pixmap;
1276 static Picture pict;
1277 static int current_alpha;
1279 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1281 if (!pixmap)
1283 XRenderPictureAttributes pa;
1285 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1286 pa.repeat = RepeatNormal;
1287 pict = pXRenderCreatePicture( gdi_display, pixmap,
1288 pict_formats[WXR_FORMAT_A8R8G8B8], CPRepeat, &pa );
1289 current_alpha = -1;
1292 if (alpha != current_alpha)
1294 XRenderColor col;
1295 col.red = col.green = col.blue = 0;
1296 col.alpha = current_alpha = alpha;
1297 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1299 return pict;
1302 /***********************************************************************
1303 * xrenderdrv_ExtTextOut
1305 static BOOL CDECL xrenderdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
1306 const RECT *lprect, LPCWSTR wstr, UINT count, const INT *lpDx )
1308 struct xrender_physdev *physdev = get_xrender_dev( dev );
1309 gsCacheEntry *entry;
1310 gsCacheEntryFormat *formatEntry;
1311 unsigned int idx;
1312 Picture pict, tile_pict = 0;
1313 XGlyphElt16 *elts;
1314 POINT offset, desired, current;
1315 int render_op = PictOpOver;
1316 XRenderColor col;
1317 RECT rect, bounds;
1318 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
1320 get_xrender_color( physdev, GetTextColor( physdev->dev.hdc ), &col );
1321 pict = get_xrender_picture( physdev, 0, (flags & ETO_CLIPPED) ? lprect : NULL );
1323 if(flags & ETO_OPAQUE)
1325 XRenderColor bg;
1327 if (physdev->format == WXR_FORMAT_MONO)
1328 /* use the inverse of the text color */
1329 bg.red = bg.green = bg.blue = bg.alpha = ~col.alpha;
1330 else
1331 get_xrender_color( physdev, GetBkColor( physdev->dev.hdc ), &bg );
1333 set_xrender_transformation( pict, 1, 1, 0, 0 );
1334 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &bg,
1335 physdev->x11dev->dc_rect.left + lprect->left,
1336 physdev->x11dev->dc_rect.top + lprect->top,
1337 lprect->right - lprect->left,
1338 lprect->bottom - lprect->top );
1339 add_device_bounds( physdev->x11dev, lprect );
1342 if(count == 0) return TRUE;
1344 EnterCriticalSection(&xrender_cs);
1346 entry = glyphsetCache + physdev->cache_index;
1347 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1349 for(idx = 0; idx < count; idx++) {
1350 if( !formatEntry ) {
1351 UploadGlyph(physdev, wstr[idx], type);
1352 /* re-evaluate format entry since aa_flags may have changed */
1353 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1354 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1355 UploadGlyph(physdev, wstr[idx], type);
1358 if (!formatEntry)
1360 WARN("could not upload requested glyphs\n");
1361 LeaveCriticalSection(&xrender_cs);
1362 return FALSE;
1365 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1366 physdev->x11dev->dc_rect.left + x, physdev->x11dev->dc_rect.top + y);
1368 elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1370 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1371 So we pass zeros to the function and move to our starting position using the first
1372 element of the elts array. */
1374 desired.x = physdev->x11dev->dc_rect.left + x;
1375 desired.y = physdev->x11dev->dc_rect.top + y;
1376 offset.x = offset.y = 0;
1377 current.x = current.y = 0;
1379 tile_pict = get_tile_pict(physdev->format, &col);
1381 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1383 if (physdev->format == WXR_FORMAT_MONO && col.red == 0 && col.green == 0 && col.blue == 0)
1384 render_op = PictOpOutReverse; /* This gives us 'black' text */
1386 reset_bounds( &bounds );
1387 for(idx = 0; idx < count; idx++)
1389 elts[idx].glyphset = formatEntry->glyphset;
1390 elts[idx].chars = wstr + idx;
1391 elts[idx].nchars = 1;
1392 elts[idx].xOff = desired.x - current.x;
1393 elts[idx].yOff = desired.y - current.y;
1395 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1396 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1398 rect.left = desired.x - physdev->x11dev->dc_rect.left - formatEntry->gis[wstr[idx]].x;
1399 rect.top = desired.y - physdev->x11dev->dc_rect.top - formatEntry->gis[wstr[idx]].y;
1400 rect.right = rect.left + formatEntry->gis[wstr[idx]].width;
1401 rect.bottom = rect.top + formatEntry->gis[wstr[idx]].height;
1402 add_bounds_rect( &bounds, &rect );
1404 if(!lpDx)
1406 desired.x += formatEntry->gis[wstr[idx]].xOff;
1407 desired.y += formatEntry->gis[wstr[idx]].yOff;
1409 else
1411 if(flags & ETO_PDY)
1413 offset.x += lpDx[idx * 2];
1414 offset.y += lpDx[idx * 2 + 1];
1416 else
1417 offset.x += lpDx[idx];
1418 desired.x = physdev->x11dev->dc_rect.left + x + offset.x;
1419 desired.y = physdev->x11dev->dc_rect.top + y + offset.y;
1423 /* Make sure we don't have any transforms set from a previous call */
1424 set_xrender_transformation(pict, 1, 1, 0, 0);
1425 pXRenderCompositeText16(gdi_display, render_op,
1426 tile_pict,
1427 pict,
1428 formatEntry->font_format,
1429 0, 0, 0, 0, elts, count);
1430 HeapFree(GetProcessHeap(), 0, elts);
1432 LeaveCriticalSection(&xrender_cs);
1433 add_device_bounds( physdev->x11dev, &bounds );
1434 return TRUE;
1437 /* multiply the alpha channel of a picture */
1438 static void multiply_alpha( Picture pict, XRenderPictFormat *format, int alpha,
1439 int x, int y, int width, int height )
1441 XRenderPictureAttributes pa;
1442 Pixmap src_pixmap, mask_pixmap;
1443 Picture src_pict, mask_pict;
1444 XRenderColor color;
1446 src_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1447 mask_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1448 pa.repeat = RepeatNormal;
1449 src_pict = pXRenderCreatePicture( gdi_display, src_pixmap, format, CPRepeat, &pa );
1450 pa.component_alpha = True;
1451 mask_pict = pXRenderCreatePicture( gdi_display, mask_pixmap, format, CPRepeat|CPComponentAlpha, &pa );
1452 color.red = color.green = color.blue = color.alpha = 0xffff;
1453 pXRenderFillRectangle( gdi_display, PictOpSrc, src_pict, &color, 0, 0, 1, 1 );
1454 color.alpha = alpha;
1455 pXRenderFillRectangle( gdi_display, PictOpSrc, mask_pict, &color, 0, 0, 1, 1 );
1456 pXRenderComposite( gdi_display, PictOpInReverse, src_pict, mask_pict, pict,
1457 0, 0, 0, 0, x, y, width, height );
1458 pXRenderFreePicture( gdi_display, src_pict );
1459 pXRenderFreePicture( gdi_display, mask_pict );
1460 XFreePixmap( gdi_display, src_pixmap );
1461 XFreePixmap( gdi_display, mask_pixmap );
1464 /* Helper function for (stretched) blitting using xrender */
1465 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1466 int x_src, int y_src, int width_src, int height_src,
1467 int x_dst, int y_dst, int width_dst, int height_dst,
1468 double xscale, double yscale )
1470 int x_offset, y_offset;
1472 if (width_src < 0)
1474 x_src += width_src + 1;
1476 if (height_src < 0)
1478 y_src += height_src + 1;
1480 if (width_dst < 0)
1482 x_dst += width_dst + 1;
1483 width_dst = -width_dst;
1485 if (height_dst < 0)
1487 y_dst += height_dst + 1;
1488 height_dst = -height_dst;
1491 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1492 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1493 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1494 if(xscale != 1.0 || yscale != 1.0)
1496 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1497 * in the wrong quadrant of the x-y plane.
1499 x_offset = (xscale < 0) ? -width_dst : 0;
1500 y_offset = (yscale < 0) ? -height_dst : 0;
1501 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1503 else
1505 x_offset = x_src;
1506 y_offset = y_src;
1507 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1509 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1510 x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst );
1513 /* Helper function for (stretched) mono->color blitting using xrender */
1514 static void xrender_mono_blit( Picture src_pict, Picture dst_pict,
1515 enum wxr_format dst_format, XRenderColor *fg, XRenderColor *bg,
1516 int x_src, int y_src, int width_src, int height_src,
1517 int x_dst, int y_dst, int width_dst, int height_dst,
1518 double xscale, double yscale )
1520 Picture tile_pict;
1521 int x_offset, y_offset;
1522 XRenderColor color;
1524 if (width_src < 0)
1526 x_src += width_src + 1;
1527 width_src = -width_src;
1529 if (height_src < 0)
1531 y_src += height_src + 1;
1532 height_src = -height_src;
1534 if (width_dst < 0)
1536 x_dst += width_dst + 1;
1537 width_dst = -width_dst;
1539 if (height_dst < 0)
1541 y_dst += height_dst + 1;
1542 height_dst = -height_dst;
1545 /* When doing a mono->color blit, the source data is used as mask, and the source picture
1546 * contains a 1x1 picture for tiling. The source data effectively acts as an alpha channel to
1547 * the tile data.
1549 EnterCriticalSection( &xrender_cs );
1550 color = *bg;
1551 color.alpha = 0xffff; /* tile pict needs 100% alpha */
1552 tile_pict = get_tile_pict( dst_format, &color );
1554 pXRenderFillRectangle( gdi_display, PictOpSrc, dst_pict, fg, x_dst, y_dst, width_dst, height_dst );
1556 if (xscale != 1.0 || yscale != 1.0)
1558 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1559 * in the wrong quadrant of the x-y plane.
1561 x_offset = (xscale < 0) ? -width_dst : 0;
1562 y_offset = (yscale < 0) ? -height_dst : 0;
1563 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1565 else
1567 x_offset = x_src;
1568 y_offset = y_src;
1569 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1571 pXRenderComposite(gdi_display, PictOpOver, tile_pict, src_pict, dst_pict,
1572 0, 0, x_offset, y_offset, x_dst, y_dst, width_dst, height_dst );
1573 LeaveCriticalSection( &xrender_cs );
1575 /* force the alpha channel for background pixels, it has been set to 100% by the tile */
1576 if (bg->alpha != 0xffff && (dst_format == WXR_FORMAT_A8R8G8B8 || dst_format == WXR_FORMAT_B8G8R8A8))
1577 multiply_alpha( dst_pict, pict_formats[dst_format], bg->alpha,
1578 x_dst, y_dst, width_dst, height_dst );
1581 /* create a pixmap and render picture for an image */
1582 static DWORD create_image_pixmap( BITMAPINFO *info, const struct gdi_image_bits *bits,
1583 struct bitblt_coords *src, enum wxr_format format,
1584 Pixmap *pixmap, Picture *pict, BOOL *use_repeat )
1586 DWORD ret;
1587 int width = src->visrect.right - src->visrect.left;
1588 int height = src->visrect.bottom - src->visrect.top;
1589 int depth = pict_formats[format]->depth;
1590 struct gdi_image_bits dst_bits;
1591 XRenderPictureAttributes pa;
1592 GC gc;
1593 XImage *image;
1595 image = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0, NULL,
1596 info->bmiHeader.biWidth, height, 32, 0 );
1597 if (!image) return ERROR_OUTOFMEMORY;
1599 ret = copy_image_bits( info, (format == WXR_FORMAT_R8G8B8), image, bits, &dst_bits, src, NULL, ~0u );
1600 if (ret) return ret;
1602 image->data = dst_bits.ptr;
1604 *use_repeat = (width == 1 && height == 1);
1605 pa.repeat = *use_repeat ? RepeatNormal : RepeatNone;
1607 *pixmap = XCreatePixmap( gdi_display, root_window, width, height, depth );
1608 gc = XCreateGC( gdi_display, *pixmap, 0, NULL );
1609 XPutImage( gdi_display, *pixmap, gc, image, src->visrect.left, 0, 0, 0, width, height );
1610 *pict = pXRenderCreatePicture( gdi_display, *pixmap, pict_formats[format], CPRepeat, &pa );
1611 XFreeGC( gdi_display, gc );
1613 /* make coordinates relative to the pixmap */
1614 src->x -= src->visrect.left;
1615 src->y -= src->visrect.top;
1616 OffsetRect( &src->visrect, -src->visrect.left, -src->visrect.top );
1618 image->data = NULL;
1619 XDestroyImage( image );
1620 if (dst_bits.free) dst_bits.free( &dst_bits );
1621 return ret;
1624 static void xrender_stretch_blit( struct xrender_physdev *physdev_src, struct xrender_physdev *physdev_dst,
1625 Drawable drawable, const struct bitblt_coords *src,
1626 const struct bitblt_coords *dst )
1628 int x_dst, y_dst;
1629 Picture src_pict = 0, dst_pict, mask_pict = 0;
1630 double xscale = src->width / (double)dst->width;
1631 double yscale = src->height / (double)dst->height;
1633 if (drawable) /* using an intermediate pixmap */
1635 x_dst = dst->x;
1636 y_dst = dst->y;
1637 dst_pict = pXRenderCreatePicture( gdi_display, drawable, physdev_dst->pict_format, 0, NULL );
1639 else
1641 x_dst = physdev_dst->x11dev->dc_rect.left + dst->x;
1642 y_dst = physdev_dst->x11dev->dc_rect.top + dst->y;
1643 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1646 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1648 /* mono -> color */
1649 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1651 XRenderColor fg, bg;
1653 get_xrender_color( physdev_dst, GetTextColor( physdev_dst->dev.hdc ), &fg );
1654 get_xrender_color( physdev_dst, GetBkColor( physdev_dst->dev.hdc ), &bg );
1655 fg.alpha = bg.alpha = 0;
1657 xrender_mono_blit( src_pict, dst_pict, physdev_dst->format, &fg, &bg,
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 );
1662 else /* color -> color (can be at different depths) or mono -> mono */
1664 if (physdev_dst->pict_format->depth == 32 && physdev_src->pict_format->depth < 32)
1665 mask_pict = get_no_alpha_mask();
1667 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict,
1668 physdev_src->x11dev->dc_rect.left + src->x,
1669 physdev_src->x11dev->dc_rect.top + src->y,
1670 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1673 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1677 static void xrender_put_image( Pixmap src_pixmap, Picture src_pict, Picture mask_pict, HRGN clip,
1678 XRenderPictFormat *dst_format, struct xrender_physdev *physdev,
1679 Drawable drawable, struct bitblt_coords *src,
1680 struct bitblt_coords *dst, BOOL use_repeat )
1682 int x_dst, y_dst;
1683 Picture dst_pict;
1684 double xscale, yscale;
1686 if (drawable) /* using an intermediate pixmap */
1688 RGNDATA *clip_data = NULL;
1690 if (clip) clip_data = X11DRV_GetRegionData( clip, 0 );
1691 x_dst = dst->x;
1692 y_dst = dst->y;
1693 dst_pict = pXRenderCreatePicture( gdi_display, drawable, dst_format, 0, NULL );
1694 if (clip_data)
1695 pXRenderSetPictureClipRectangles( gdi_display, dst_pict, 0, 0,
1696 (XRectangle *)clip_data->Buffer, clip_data->rdh.nCount );
1697 HeapFree( GetProcessHeap(), 0, clip_data );
1699 else
1701 x_dst = physdev->x11dev->dc_rect.left + dst->x;
1702 y_dst = physdev->x11dev->dc_rect.top + dst->y;
1703 dst_pict = get_xrender_picture( physdev, clip, &dst->visrect );
1706 if (!use_repeat)
1708 xscale = src->width / (double)dst->width;
1709 yscale = src->height / (double)dst->height;
1711 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1713 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict, src->x, src->y, src->width, src->height,
1714 x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1716 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1720 /***********************************************************************
1721 * xrenderdrv_StretchBlt
1723 static BOOL CDECL xrenderdrv_StretchBlt( PHYSDEV dst_dev, struct bitblt_coords *dst,
1724 PHYSDEV src_dev, struct bitblt_coords *src, DWORD rop )
1726 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1727 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1728 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
1730 if (src_dev->funcs != dst_dev->funcs)
1732 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pStretchBlt );
1733 return dst_dev->funcs->pStretchBlt( dst_dev, dst, src_dev, src, rop );
1736 /* XRender is of no use for color -> mono */
1737 if (physdev_dst->format == WXR_FORMAT_MONO && physdev_src->format != WXR_FORMAT_MONO)
1738 goto x11drv_fallback;
1740 /* if not stretching, we only need to handle format conversion */
1741 if (!stretch && physdev_dst->format == physdev_src->format) goto x11drv_fallback;
1743 if (rop != SRCCOPY)
1745 GC tmpGC;
1746 Pixmap tmp_pixmap;
1747 struct bitblt_coords tmp;
1749 /* make coordinates relative to tmp pixmap */
1750 tmp = *dst;
1751 tmp.x -= tmp.visrect.left;
1752 tmp.y -= tmp.visrect.top;
1753 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1755 tmpGC = XCreateGC( gdi_display, physdev_dst->x11dev->drawable, 0, NULL );
1756 XSetSubwindowMode( gdi_display, tmpGC, IncludeInferiors );
1757 XSetGraphicsExposures( gdi_display, tmpGC, False );
1758 tmp_pixmap = XCreatePixmap( gdi_display, root_window, tmp.visrect.right - tmp.visrect.left,
1759 tmp.visrect.bottom - tmp.visrect.top, physdev_dst->pict_format->depth );
1761 xrender_stretch_blit( physdev_src, physdev_dst, tmp_pixmap, src, &tmp );
1762 execute_rop( physdev_dst->x11dev, tmp_pixmap, tmpGC, &dst->visrect, rop );
1764 XFreePixmap( gdi_display, tmp_pixmap );
1765 XFreeGC( gdi_display, tmpGC );
1767 else xrender_stretch_blit( physdev_src, physdev_dst, 0, src, dst );
1769 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
1770 return TRUE;
1772 x11drv_fallback:
1773 return X11DRV_StretchBlt( &physdev_dst->x11dev->dev, dst, &physdev_src->x11dev->dev, src, rop );
1777 /***********************************************************************
1778 * xrenderdrv_PutImage
1780 static DWORD CDECL xrenderdrv_PutImage( PHYSDEV dev, HRGN clip, BITMAPINFO *info,
1781 const struct gdi_image_bits *bits, struct bitblt_coords *src,
1782 struct bitblt_coords *dst, DWORD rop )
1784 struct xrender_physdev *physdev = get_xrender_dev( dev );
1785 DWORD ret;
1786 Pixmap tmp_pixmap;
1787 GC gc;
1788 enum wxr_format src_format, dst_format;
1789 XRenderPictFormat *pict_format;
1790 Pixmap src_pixmap;
1791 Picture src_pict, mask_pict = 0;
1792 BOOL use_repeat;
1794 dst_format = physdev->format;
1795 src_format = get_xrender_format_from_bitmapinfo( info );
1796 if (!(pict_format = pict_formats[src_format])) goto update_format;
1798 /* make sure we can create an image with the same bpp */
1799 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1800 goto update_format;
1802 /* mono <-> color conversions not supported */
1803 if ((src_format != dst_format) && (src_format == WXR_FORMAT_MONO || dst_format == WXR_FORMAT_MONO))
1804 goto x11drv_fallback;
1806 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1808 if (!has_alpha( src_format ) && has_alpha( dst_format )) mask_pict = get_no_alpha_mask();
1810 ret = create_image_pixmap( info, bits, src, src_format, &src_pixmap, &src_pict, &use_repeat );
1811 if (!ret)
1813 struct bitblt_coords tmp;
1815 if (rop != SRCCOPY)
1817 BOOL restore_region = add_extra_clipping_region( physdev->x11dev, clip );
1819 /* make coordinates relative to tmp pixmap */
1820 tmp = *dst;
1821 tmp.x -= tmp.visrect.left;
1822 tmp.y -= tmp.visrect.top;
1823 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1825 gc = XCreateGC( gdi_display, physdev->x11dev->drawable, 0, NULL );
1826 XSetSubwindowMode( gdi_display, gc, IncludeInferiors );
1827 XSetGraphicsExposures( gdi_display, gc, False );
1828 tmp_pixmap = XCreatePixmap( gdi_display, root_window,
1829 tmp.visrect.right - tmp.visrect.left,
1830 tmp.visrect.bottom - tmp.visrect.top,
1831 physdev->pict_format->depth );
1833 xrender_put_image( src_pixmap, src_pict, mask_pict, NULL, physdev->pict_format,
1834 NULL, tmp_pixmap, src, &tmp, use_repeat );
1835 execute_rop( physdev->x11dev, tmp_pixmap, gc, &dst->visrect, rop );
1837 XFreePixmap( gdi_display, tmp_pixmap );
1838 XFreeGC( gdi_display, gc );
1839 if (restore_region) restore_clipping_region( physdev->x11dev );
1841 else xrender_put_image( src_pixmap, src_pict, mask_pict, clip,
1842 physdev->pict_format, physdev, 0, src, dst, use_repeat );
1844 add_device_bounds( physdev->x11dev, &dst->visrect );
1846 pXRenderFreePicture( gdi_display, src_pict );
1847 XFreePixmap( gdi_display, src_pixmap );
1849 return ret;
1851 update_format:
1852 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1853 set_color_info( pict_formats[dst_format], info );
1854 return ERROR_BAD_FORMAT;
1856 x11drv_fallback:
1857 dev = GET_NEXT_PHYSDEV( dev, pPutImage );
1858 return dev->funcs->pPutImage( dev, clip, info, bits, src, dst, rop );
1862 /***********************************************************************
1863 * xrenderdrv_BlendImage
1865 static DWORD CDECL xrenderdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
1866 struct bitblt_coords *src, struct bitblt_coords *dst,
1867 BLENDFUNCTION func )
1869 struct xrender_physdev *physdev = get_xrender_dev( dev );
1870 DWORD ret;
1871 enum wxr_format format;
1872 XRenderPictFormat *pict_format;
1873 Picture dst_pict, src_pict, mask_pict;
1874 Pixmap src_pixmap;
1875 BOOL use_repeat;
1877 format = get_xrender_format_from_bitmapinfo( info );
1878 if (!(func.AlphaFormat & AC_SRC_ALPHA))
1879 format = get_format_without_alpha( format );
1880 else if (format != WXR_FORMAT_A8R8G8B8 || info->bmiHeader.biCompression != BI_RGB)
1881 return ERROR_INVALID_PARAMETER;
1883 if (!(pict_format = pict_formats[format])) goto update_format;
1885 /* make sure we can create an image with the same bpp */
1886 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1887 goto update_format;
1889 if (format == WXR_FORMAT_MONO && physdev->format != WXR_FORMAT_MONO)
1890 goto update_format;
1892 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1894 ret = create_image_pixmap( info, bits, src, format, &src_pixmap, &src_pict, &use_repeat );
1895 if (!ret)
1897 double xscale, yscale;
1899 if (!use_repeat)
1901 xscale = src->width / (double)dst->width;
1902 yscale = src->height / (double)dst->height;
1904 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1906 dst_pict = get_xrender_picture( physdev, 0, &dst->visrect );
1908 EnterCriticalSection( &xrender_cs );
1909 mask_pict = get_mask_pict( func.SourceConstantAlpha * 257 );
1911 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1912 src->x, src->y, src->width, src->height,
1913 physdev->x11dev->dc_rect.left + dst->x,
1914 physdev->x11dev->dc_rect.top + dst->y,
1915 dst->width, dst->height, xscale, yscale );
1917 pXRenderFreePicture( gdi_display, src_pict );
1918 XFreePixmap( gdi_display, src_pixmap );
1920 LeaveCriticalSection( &xrender_cs );
1921 add_device_bounds( physdev->x11dev, &dst->visrect );
1923 return ret;
1925 update_format:
1926 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1927 set_color_info( physdev->pict_format, info );
1928 return ERROR_BAD_FORMAT;
1932 /***********************************************************************
1933 * xrenderdrv_AlphaBlend
1935 static BOOL CDECL xrenderdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
1936 PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blendfn )
1938 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1939 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1940 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
1941 XRenderPictureAttributes pa;
1942 Pixmap tmp_pixmap = 0;
1943 double xscale, yscale;
1945 if (src_dev->funcs != dst_dev->funcs)
1947 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pAlphaBlend );
1948 return dst_dev->funcs->pAlphaBlend( dst_dev, dst, src_dev, src, blendfn );
1951 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->format != WXR_FORMAT_A8R8G8B8)
1953 SetLastError( ERROR_INVALID_PARAMETER );
1954 return FALSE;
1957 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1959 xscale = src->width / (double)dst->width;
1960 yscale = src->height / (double)dst->height;
1962 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1964 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1966 /* mono -> color blending needs an intermediate color pixmap */
1967 XRenderColor fg, bg;
1968 int width = src->visrect.right - src->visrect.left;
1969 int height = src->visrect.bottom - src->visrect.top;
1971 /* blending doesn't use the destination DC colors */
1972 fg.red = fg.green = fg.blue = 0;
1973 bg.red = bg.green = bg.blue = 0xffff;
1974 fg.alpha = bg.alpha = 0xffff;
1976 tmp_pixmap = XCreatePixmap( gdi_display, root_window, width, height,
1977 physdev_dst->pict_format->depth );
1978 tmp_pict = pXRenderCreatePicture( gdi_display, tmp_pixmap, physdev_dst->pict_format, 0, NULL );
1980 xrender_mono_blit( src_pict, tmp_pict, physdev_dst->format, &fg, &bg,
1981 src->visrect.left, src->visrect.top, width, height, 0, 0, width, height, 1, 1 );
1983 else if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->pict_format)
1985 /* we need a source picture with no alpha */
1986 enum wxr_format format = get_format_without_alpha( physdev_src->format );
1987 if (format != physdev_src->format)
1989 pa.subwindow_mode = IncludeInferiors;
1990 tmp_pict = pXRenderCreatePicture( gdi_display, physdev_src->x11dev->drawable,
1991 pict_formats[format], CPSubwindowMode, &pa );
1995 if (tmp_pict) src_pict = tmp_pict;
1997 EnterCriticalSection( &xrender_cs );
1998 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2000 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2001 physdev_src->x11dev->dc_rect.left + src->x,
2002 physdev_src->x11dev->dc_rect.top + src->y,
2003 src->width, src->height,
2004 physdev_dst->x11dev->dc_rect.left + dst->x,
2005 physdev_dst->x11dev->dc_rect.top + dst->y,
2006 dst->width, dst->height, xscale, yscale );
2008 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2009 if (tmp_pixmap) XFreePixmap( gdi_display, tmp_pixmap );
2011 LeaveCriticalSection( &xrender_cs );
2012 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
2013 return TRUE;
2016 /***********************************************************************
2017 * xrenderdrv_GradientFill
2019 static BOOL CDECL xrenderdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
2020 void * grad_array, ULONG ngrad, ULONG mode )
2022 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
2023 static const XFixed stops[2] = { 0, 1 << 16 };
2024 struct xrender_physdev *physdev = get_xrender_dev( dev );
2025 XLinearGradient gradient;
2026 XRenderColor colors[2];
2027 Picture src_pict, dst_pict;
2028 unsigned int i;
2029 const GRADIENT_RECT *rect = grad_array;
2030 RECT rc;
2031 POINT pt[2];
2033 if (!pXRenderCreateLinearGradient) goto fallback;
2035 /* <= 16-bpp uses dithering */
2036 if (!physdev->pict_format || physdev->pict_format->depth <= 16) goto fallback;
2038 switch (mode)
2040 case GRADIENT_FILL_RECT_H:
2041 case GRADIENT_FILL_RECT_V:
2042 for (i = 0; i < ngrad; i++, rect++)
2044 const TRIVERTEX *v1 = vert_array + rect->UpperLeft;
2045 const TRIVERTEX *v2 = vert_array + rect->LowerRight;
2047 colors[0].red = v1->Red * 257 / 256;
2048 colors[0].green = v1->Green * 257 / 256;
2049 colors[0].blue = v1->Blue * 257 / 256;
2050 colors[1].red = v2->Red * 257 / 256;
2051 colors[1].green = v2->Green * 257 / 256;
2052 colors[1].blue = v2->Blue * 257 / 256;
2053 /* always ignore alpha since otherwise xrender will want to pre-multiply the colors */
2054 colors[0].alpha = colors[1].alpha = 65535;
2056 pt[0].x = v1->x;
2057 pt[0].y = v1->y;
2058 pt[1].x = v2->x;
2059 pt[1].y = v2->y;
2060 LPtoDP( dev->hdc, pt, 2 );
2061 if (mode == GRADIENT_FILL_RECT_H)
2063 gradient.p1.y = gradient.p2.y = 0;
2064 if (pt[1].x > pt[0].x)
2066 gradient.p1.x = 0;
2067 gradient.p2.x = (pt[1].x - pt[0].x) << 16;
2069 else
2071 gradient.p1.x = (pt[0].x - pt[1].x) << 16;
2072 gradient.p2.x = 0;
2075 else
2077 gradient.p1.x = gradient.p2.x = 0;
2078 if (pt[1].y > pt[0].y)
2080 gradient.p1.y = 0;
2081 gradient.p2.y = (pt[1].y - pt[0].y) << 16;
2083 else
2085 gradient.p1.y = (pt[0].y - pt[1].y) << 16;
2086 gradient.p2.y = 0;
2090 rc.left = min( pt[0].x, pt[1].x );
2091 rc.top = min( pt[0].y, pt[1].y );
2092 rc.right = max( pt[0].x, pt[1].x );
2093 rc.bottom = max( pt[0].y, pt[1].y );
2095 TRACE( "%u gradient %s colors %04x,%04x,%04x,%04x -> %04x,%04x,%04x,%04x\n",
2096 mode, wine_dbgstr_rect( &rc ),
2097 colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha,
2098 colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha );
2100 dst_pict = get_xrender_picture( physdev, 0, NULL );
2102 src_pict = pXRenderCreateLinearGradient( gdi_display, &gradient, stops, colors, 2 );
2103 xrender_blit( PictOpSrc, src_pict, 0, dst_pict,
2104 0, 0, rc.right - rc.left, rc.bottom - rc.top,
2105 physdev->x11dev->dc_rect.left + rc.left,
2106 physdev->x11dev->dc_rect.top + rc.top,
2107 rc.right - rc.left, rc.bottom - rc.top, 1, 1 );
2108 pXRenderFreePicture( gdi_display, src_pict );
2109 add_device_bounds( physdev->x11dev, &rc );
2111 return TRUE;
2114 fallback:
2115 #endif
2116 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
2117 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
2120 /***********************************************************************
2121 * xrenderdrv_SelectBrush
2123 static HBRUSH CDECL xrenderdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2125 struct xrender_physdev *physdev = get_xrender_dev( dev );
2126 Pixmap pixmap;
2127 XVisualInfo vis = default_visual;
2128 XRenderPictFormat *format = physdev->pict_format;
2130 if (!pattern) goto x11drv_fallback;
2131 if (pattern->info->bmiHeader.biBitCount == 1) goto x11drv_fallback;
2132 if (physdev->format == WXR_FORMAT_MONO) goto x11drv_fallback;
2134 vis.depth = format->depth;
2135 vis.red_mask = format->direct.redMask << format->direct.red;
2136 vis.green_mask = format->direct.greenMask << format->direct.green;
2137 vis.blue_mask = format->direct.blueMask << format->direct.blue;
2139 pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, pattern->info,
2140 &pattern->bits, pattern->usage );
2141 if (!pixmap) return 0;
2143 if (physdev->x11dev->brush.pixmap) XFreePixmap( gdi_display, physdev->x11dev->brush.pixmap );
2144 physdev->x11dev->brush.pixmap = pixmap;
2145 physdev->x11dev->brush.fillStyle = FillTiled;
2146 physdev->x11dev->brush.pixel = 0; /* ignored */
2147 physdev->x11dev->brush.style = BS_PATTERN;
2148 return hbrush;
2150 x11drv_fallback:
2151 dev = GET_NEXT_PHYSDEV( dev, pSelectBrush );
2152 return dev->funcs->pSelectBrush( dev, hbrush, pattern );
2156 static const struct gdi_dc_funcs xrender_funcs =
2158 NULL, /* pAbortDoc */
2159 NULL, /* pAbortPath */
2160 xrenderdrv_AlphaBlend, /* pAlphaBlend */
2161 NULL, /* pAngleArc */
2162 NULL, /* pArc */
2163 NULL, /* pArcTo */
2164 NULL, /* pBeginPath */
2165 xrenderdrv_BlendImage, /* pBlendImage */
2166 NULL, /* pChord */
2167 NULL, /* pCloseFigure */
2168 xrenderdrv_CreateCompatibleDC, /* pCreateCompatibleDC */
2169 xrenderdrv_CreateDC, /* pCreateDC */
2170 xrenderdrv_DeleteDC, /* pDeleteDC */
2171 NULL, /* pDeleteObject */
2172 NULL, /* pDeviceCapabilities */
2173 NULL, /* pEllipse */
2174 NULL, /* pEndDoc */
2175 NULL, /* pEndPage */
2176 NULL, /* pEndPath */
2177 NULL, /* pEnumFonts */
2178 NULL, /* pEnumICMProfiles */
2179 NULL, /* pExcludeClipRect */
2180 NULL, /* pExtDeviceMode */
2181 xrenderdrv_ExtEscape, /* pExtEscape */
2182 NULL, /* pExtFloodFill */
2183 NULL, /* pExtSelectClipRgn */
2184 xrenderdrv_ExtTextOut, /* pExtTextOut */
2185 NULL, /* pFillPath */
2186 NULL, /* pFillRgn */
2187 NULL, /* pFlattenPath */
2188 NULL, /* pFontIsLinked */
2189 NULL, /* pFrameRgn */
2190 NULL, /* pGdiComment */
2191 NULL, /* pGetBoundsRect */
2192 NULL, /* pGetCharABCWidths */
2193 NULL, /* pGetCharABCWidthsI */
2194 NULL, /* pGetCharWidth */
2195 NULL, /* pGetCharWidthInfo */
2196 NULL, /* pGetDeviceCaps */
2197 NULL, /* pGetDeviceGammaRamp */
2198 NULL, /* pGetFontData */
2199 NULL, /* pGetFontRealizationInfo */
2200 NULL, /* pGetFontUnicodeRanges */
2201 NULL, /* pGetGlyphIndices */
2202 NULL, /* pGetGlyphOutline */
2203 NULL, /* pGetICMProfile */
2204 NULL, /* pGetImage */
2205 NULL, /* pGetKerningPairs */
2206 NULL, /* pGetNearestColor */
2207 NULL, /* pGetOutlineTextMetrics */
2208 NULL, /* pGetPixel */
2209 NULL, /* pGetSystemPaletteEntries */
2210 NULL, /* pGetTextCharsetInfo */
2211 NULL, /* pGetTextExtentExPoint */
2212 NULL, /* pGetTextExtentExPointI */
2213 NULL, /* pGetTextFace */
2214 NULL, /* pGetTextMetrics */
2215 xrenderdrv_GradientFill, /* pGradientFill */
2216 NULL, /* pIntersectClipRect */
2217 NULL, /* pInvertRgn */
2218 NULL, /* pLineTo */
2219 NULL, /* pModifyWorldTransform */
2220 NULL, /* pMoveTo */
2221 NULL, /* pOffsetClipRgn */
2222 NULL, /* pOffsetViewportOrg */
2223 NULL, /* pOffsetWindowOrg */
2224 NULL, /* pPaintRgn */
2225 NULL, /* pPatBlt */
2226 NULL, /* pPie */
2227 NULL, /* pPolyBezier */
2228 NULL, /* pPolyBezierTo */
2229 NULL, /* pPolyDraw */
2230 NULL, /* pPolyPolygon */
2231 NULL, /* pPolyPolyline */
2232 NULL, /* pPolygon */
2233 NULL, /* pPolyline */
2234 NULL, /* pPolylineTo */
2235 xrenderdrv_PutImage, /* pPutImage */
2236 NULL, /* pRealizeDefaultPalette */
2237 NULL, /* pRealizePalette */
2238 NULL, /* pRectangle */
2239 NULL, /* pResetDC */
2240 NULL, /* pRestoreDC */
2241 NULL, /* pRoundRect */
2242 NULL, /* pSaveDC */
2243 NULL, /* pScaleViewportExt */
2244 NULL, /* pScaleWindowExt */
2245 NULL, /* pSelectBitmap */
2246 xrenderdrv_SelectBrush, /* pSelectBrush */
2247 NULL, /* pSelectClipPath */
2248 xrenderdrv_SelectFont, /* pSelectFont */
2249 NULL, /* pSelectPalette */
2250 NULL, /* pSelectPen */
2251 NULL, /* pSetArcDirection */
2252 NULL, /* pSetBkColor */
2253 NULL, /* pSetBkMode */
2254 NULL, /* pSetBoundsRect */
2255 NULL, /* pSetDCBrushColor */
2256 NULL, /* pSetDCPenColor */
2257 NULL, /* pSetDIBitsToDevice */
2258 xrenderdrv_SetDeviceClipping, /* pSetDeviceClipping */
2259 NULL, /* pSetDeviceGammaRamp */
2260 NULL, /* pSetLayout */
2261 NULL, /* pSetMapMode */
2262 NULL, /* pSetMapperFlags */
2263 NULL, /* pSetPixel */
2264 NULL, /* pSetPolyFillMode */
2265 NULL, /* pSetROP2 */
2266 NULL, /* pSetRelAbs */
2267 NULL, /* pSetStretchBltMode */
2268 NULL, /* pSetTextAlign */
2269 NULL, /* pSetTextCharacterExtra */
2270 NULL, /* pSetTextColor */
2271 NULL, /* pSetTextJustification */
2272 NULL, /* pSetViewportExt */
2273 NULL, /* pSetViewportOrg */
2274 NULL, /* pSetWindowExt */
2275 NULL, /* pSetWindowOrg */
2276 NULL, /* pSetWorldTransform */
2277 NULL, /* pStartDoc */
2278 NULL, /* pStartPage */
2279 xrenderdrv_StretchBlt, /* pStretchBlt */
2280 NULL, /* pStretchDIBits */
2281 NULL, /* pStrokeAndFillPath */
2282 NULL, /* pStrokePath */
2283 NULL, /* pUnrealizePalette */
2284 NULL, /* pWidenPath */
2285 NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */
2286 NULL, /* pD3DKMTSetVidPnSourceOwner */
2287 NULL, /* wine_get_wgl_driver */
2288 NULL, /* wine_get_vulkan_driver */
2289 GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */
2292 #else /* SONAME_LIBXRENDER */
2294 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
2296 TRACE("XRender support not compiled in.\n");
2297 return NULL;
2300 #endif /* SONAME_LIBXRENDER */