msvcrt: Add __swprintf_l.
[wine.git] / dlls / winex11.drv / xrender.c
blobd2a1c458d5400b1f4e885df8cb18bb3a5e296fe9
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_NB_FORMATS,
72 WXR_INVALID_FORMAT = WXR_NB_FORMATS
75 typedef struct wine_xrender_format_template
77 unsigned int depth;
78 unsigned int alpha;
79 unsigned int alphaMask;
80 unsigned int red;
81 unsigned int redMask;
82 unsigned int green;
83 unsigned int greenMask;
84 unsigned int blue;
85 unsigned int blueMask;
86 } WineXRenderFormatTemplate;
88 static const WineXRenderFormatTemplate wxr_formats_template[WXR_NB_FORMATS] =
90 /* Format depth alpha mask red mask green mask blue mask*/
91 /* WXR_FORMAT_MONO */ { 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
92 /* WXR_FORMAT_GRAY */ { 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
93 /* WXR_FORMAT_X1R5G5B5 */ { 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
94 /* WXR_FORMAT_X1B5G5R5 */ { 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
95 /* WXR_FORMAT_R5G6B5 */ { 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
96 /* WXR_FORMAT_B5G6R5 */ { 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
97 /* WXR_FORMAT_R8G8B8 */ { 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
98 /* WXR_FORMAT_B8G8R8 */ { 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
99 /* WXR_FORMAT_A8R8G8B8 */ { 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
100 /* WXR_FORMAT_B8G8R8A8 */ { 32, 0, 0xff, 8, 0xff, 16, 0xff, 24, 0xff },
101 /* WXR_FORMAT_X8R8G8B8 */ { 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
102 /* WXR_FORMAT_B8G8R8X8 */ { 32, 0, 0, 8, 0xff, 16, 0xff, 24, 0xff },
105 static enum wxr_format default_format = WXR_INVALID_FORMAT;
106 static XRenderPictFormat *pict_formats[WXR_NB_FORMATS + 1 /* invalid format */];
108 typedef struct
110 LOGFONTW lf;
111 XFORM xform;
112 SIZE devsize; /* size in device coords */
113 DWORD hash;
114 } LFANDSIZE;
116 #define INITIAL_REALIZED_BUF_SIZE 128
118 enum glyph_type { GLYPH_INDEX, GLYPH_WCHAR, GLYPH_NBTYPES };
120 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
122 typedef struct
124 GlyphSet glyphset;
125 XRenderPictFormat *font_format;
126 int nrealized;
127 BOOL *realized;
128 XGlyphInfo *gis;
129 } gsCacheEntryFormat;
131 typedef struct
133 LFANDSIZE lfsz;
134 gsCacheEntryFormat *format[GLYPH_NBTYPES][AA_MAXVALUE];
135 INT count;
136 INT next;
137 } gsCacheEntry;
139 struct xrender_physdev
141 struct gdi_physdev dev;
142 X11DRV_PDEVICE *x11dev;
143 HRGN region;
144 enum wxr_format format;
145 UINT aa_flags;
146 int cache_index;
147 BOOL update_clip;
148 Picture pict;
149 Picture pict_src;
150 XRenderPictFormat *pict_format;
153 static inline struct xrender_physdev *get_xrender_dev( PHYSDEV dev )
155 return (struct xrender_physdev *)dev;
158 static const struct gdi_dc_funcs xrender_funcs;
160 static gsCacheEntry *glyphsetCache = NULL;
161 static DWORD glyphsetCacheSize = 0;
162 static INT lastfree = -1;
163 static INT mru = -1;
165 #define INIT_CACHE_SIZE 10
167 static void *xrender_handle;
169 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
170 MAKE_FUNCPTR(XRenderAddGlyphs)
171 MAKE_FUNCPTR(XRenderChangePicture)
172 MAKE_FUNCPTR(XRenderComposite)
173 MAKE_FUNCPTR(XRenderCompositeText16)
174 MAKE_FUNCPTR(XRenderCreateGlyphSet)
175 MAKE_FUNCPTR(XRenderCreatePicture)
176 MAKE_FUNCPTR(XRenderFillRectangle)
177 MAKE_FUNCPTR(XRenderFindFormat)
178 MAKE_FUNCPTR(XRenderFindVisualFormat)
179 MAKE_FUNCPTR(XRenderFreeGlyphSet)
180 MAKE_FUNCPTR(XRenderFreePicture)
181 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
182 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
183 MAKE_FUNCPTR(XRenderCreateLinearGradient)
184 #endif
185 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
186 MAKE_FUNCPTR(XRenderSetPictureTransform)
187 #endif
188 MAKE_FUNCPTR(XRenderQueryExtension)
190 #undef MAKE_FUNCPTR
192 static CRITICAL_SECTION xrender_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
195 0, 0, &xrender_cs,
196 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
199 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
201 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
202 ( ( (ULONG)_x4 << 24 ) | \
203 ( (ULONG)_x3 << 16 ) | \
204 ( (ULONG)_x2 << 8 ) | \
205 (ULONG)_x1 )
207 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
209 #define GASP_GRIDFIT 0x01
210 #define GASP_DOGRAY 0x02
212 #ifdef WORDS_BIGENDIAN
213 #define get_be_word(x) (x)
214 #define NATIVE_BYTE_ORDER MSBFirst
215 #else
216 #define get_be_word(x) RtlUshortByteSwap(x)
217 #define NATIVE_BYTE_ORDER LSBFirst
218 #endif
220 static BOOL has_alpha( enum wxr_format format )
222 return (format == WXR_FORMAT_A8R8G8B8 || format == WXR_FORMAT_B8G8R8A8);
225 static enum wxr_format get_format_without_alpha( enum wxr_format format )
227 switch (format)
229 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
230 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
231 default: return format;
235 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
237 templ->id = 0;
238 templ->type = PictTypeDirect;
239 templ->depth = fmt->depth;
240 templ->direct.alpha = fmt->alpha;
241 templ->direct.alphaMask = fmt->alphaMask;
242 templ->direct.red = fmt->red;
243 templ->direct.redMask = fmt->redMask;
244 templ->direct.green = fmt->green;
245 templ->direct.greenMask = fmt->greenMask;
246 templ->direct.blue = fmt->blue;
247 templ->direct.blueMask = fmt->blueMask;
248 templ->colormap = 0;
250 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
252 return TRUE;
255 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
257 if(fmt->depth != default_visual.depth) return FALSE;
258 if( (fmt->redMask << fmt->red) != default_visual.red_mask) return FALSE;
259 if( (fmt->greenMask << fmt->green) != default_visual.green_mask) return FALSE;
260 if( (fmt->blueMask << fmt->blue) != default_visual.blue_mask) return FALSE;
262 /* We never select a default ARGB visual */
263 if(fmt->alphaMask) return FALSE;
264 return TRUE;
267 static int load_xrender_formats(void)
269 int count = 0;
270 unsigned int i;
272 for (i = 0; i < WXR_NB_FORMATS; i++)
274 XRenderPictFormat templ;
276 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
278 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, default_visual.visual);
279 if (!pict_formats[i])
281 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
282 if (default_visual.class == DirectColor)
284 XVisualInfo info;
285 if (XMatchVisualInfo( gdi_display, default_visual.screen,
286 default_visual.depth, TrueColor, &info ))
288 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, info.visual);
289 if (pict_formats[i]) default_visual = info;
293 if (pict_formats[i]) default_format = i;
295 else
297 unsigned long mask = 0;
298 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
299 pict_formats[i] = pXRenderFindFormat(gdi_display, mask, &templ, 0);
301 if (pict_formats[i])
303 count++;
304 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_formats[i]->id, i);
307 return count;
310 /***********************************************************************
311 * X11DRV_XRender_Init
313 * Let's see if our XServer has the extension available
316 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
318 int event_base, i;
320 if (!client_side_with_render) return NULL;
321 if (!(xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0))) return NULL;
323 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) return NULL
324 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0)
325 LOAD_FUNCPTR(XRenderAddGlyphs);
326 LOAD_FUNCPTR(XRenderChangePicture);
327 LOAD_FUNCPTR(XRenderComposite);
328 LOAD_FUNCPTR(XRenderCompositeText16);
329 LOAD_FUNCPTR(XRenderCreateGlyphSet);
330 LOAD_FUNCPTR(XRenderCreatePicture);
331 LOAD_FUNCPTR(XRenderFillRectangle);
332 LOAD_FUNCPTR(XRenderFindFormat);
333 LOAD_FUNCPTR(XRenderFindVisualFormat);
334 LOAD_FUNCPTR(XRenderFreeGlyphSet);
335 LOAD_FUNCPTR(XRenderFreePicture);
336 LOAD_FUNCPTR(XRenderSetPictureClipRectangles);
337 LOAD_FUNCPTR(XRenderQueryExtension);
338 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
339 LOAD_OPTIONAL_FUNCPTR(XRenderCreateLinearGradient);
340 #endif
341 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
342 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform);
343 #endif
344 #undef LOAD_OPTIONAL_FUNCPTR
345 #undef LOAD_FUNCPTR
347 if (!pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) return NULL;
349 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
350 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
352 ERR_(winediag)("Wine has detected that you probably have a buggy version "
353 "of libXrender. Because of this client side font rendering "
354 "will be disabled. Please upgrade this library.\n");
355 return NULL;
358 if (!default_visual.red_mask || !default_visual.green_mask || !default_visual.blue_mask)
360 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
361 return NULL;
364 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
365 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
367 glyphsetCacheSize = INIT_CACHE_SIZE;
368 lastfree = 0;
369 for(i = 0; i < INIT_CACHE_SIZE; i++) {
370 glyphsetCache[i].next = i + 1;
371 glyphsetCache[i].count = -1;
373 glyphsetCache[i-1].next = -1;
375 return &xrender_funcs;
378 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
379 static void get_xrender_color( struct xrender_physdev *physdev, COLORREF src_color, XRenderColor *dst_color )
381 if (src_color & (1 << 24)) /* PALETTEINDEX */
383 HPALETTE pal = GetCurrentObject( physdev->dev.hdc, OBJ_PAL );
384 PALETTEENTRY pal_ent;
386 if (!GetPaletteEntries( pal, LOWORD(src_color), 1, &pal_ent ))
387 GetPaletteEntries( pal, 0, 1, &pal_ent );
388 dst_color->red = pal_ent.peRed * 257;
389 dst_color->green = pal_ent.peGreen * 257;
390 dst_color->blue = pal_ent.peBlue * 257;
392 else
394 if (src_color >> 16 == 0x10ff) src_color = 0; /* DIBINDEX */
396 dst_color->red = GetRValue( src_color ) * 257;
397 dst_color->green = GetGValue( src_color ) * 257;
398 dst_color->blue = GetBValue( src_color ) * 257;
401 if (physdev->format == WXR_FORMAT_MONO && !dst_color->red && !dst_color->green && !dst_color->blue)
402 dst_color->alpha = 0;
403 else
404 dst_color->alpha = 0xffff;
407 static enum wxr_format get_xrender_format_from_bitmapinfo( const BITMAPINFO *info )
409 if (info->bmiHeader.biPlanes != 1) return WXR_INVALID_FORMAT;
411 switch (info->bmiHeader.biBitCount)
413 case 1:
414 return WXR_FORMAT_MONO;
415 case 4:
416 case 8:
417 break;
418 case 24:
419 if (info->bmiHeader.biCompression != BI_RGB) break;
420 return WXR_FORMAT_R8G8B8;
421 case 16:
422 case 32:
423 if (info->bmiHeader.biCompression == BI_BITFIELDS)
425 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
426 unsigned int i;
428 for (i = 0; i < WXR_NB_FORMATS; i++)
430 if (info->bmiHeader.biBitCount == wxr_formats_template[i].depth &&
431 colors[0] == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
432 colors[1] == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
433 colors[2] == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue))
434 return i;
436 break;
438 if (info->bmiHeader.biCompression != BI_RGB) break;
439 return (info->bmiHeader.biBitCount == 16) ? WXR_FORMAT_X1R5G5B5 : WXR_FORMAT_A8R8G8B8;
441 return WXR_INVALID_FORMAT;
444 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
445 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
447 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
448 XTransform xform = {{
449 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
450 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
451 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
454 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
455 #endif
458 static void update_xrender_clipping( struct xrender_physdev *dev, HRGN rgn )
460 XRenderPictureAttributes pa;
461 RGNDATA *data;
463 if (!rgn)
465 pa.clip_mask = None;
466 pXRenderChangePicture( gdi_display, dev->pict, CPClipMask, &pa );
468 else if ((data = X11DRV_GetRegionData( rgn, 0 )))
470 pXRenderSetPictureClipRectangles( gdi_display, dev->pict,
471 dev->x11dev->dc_rect.left, dev->x11dev->dc_rect.top,
472 (XRectangle *)data->Buffer, data->rdh.nCount );
473 HeapFree( GetProcessHeap(), 0, data );
478 static Picture get_xrender_picture( struct xrender_physdev *dev, HRGN clip_rgn, const RECT *clip_rect )
480 if (!dev->pict && dev->pict_format)
482 XRenderPictureAttributes pa;
484 pa.subwindow_mode = IncludeInferiors;
485 dev->pict = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
486 dev->pict_format, CPSubwindowMode, &pa );
487 TRACE( "Allocing pict=%lx dc=%p drawable=%08lx\n",
488 dev->pict, dev->dev.hdc, dev->x11dev->drawable );
489 dev->update_clip = (dev->region != 0);
492 if (clip_rect)
494 HRGN rgn = CreateRectRgnIndirect( clip_rect );
495 if (clip_rgn) CombineRgn( rgn, rgn, clip_rgn, RGN_AND );
496 if (dev->region) CombineRgn( rgn, rgn, dev->region, RGN_AND );
497 update_xrender_clipping( dev, rgn );
498 DeleteObject( rgn );
500 else if (clip_rgn)
502 if (dev->region)
504 HRGN rgn = CreateRectRgn( 0, 0, 0, 0 );
505 CombineRgn( rgn, clip_rgn, dev->region, RGN_AND );
506 update_xrender_clipping( dev, rgn );
507 DeleteObject( rgn );
509 else update_xrender_clipping( dev, clip_rgn );
511 else if (dev->update_clip) update_xrender_clipping( dev, dev->region );
513 dev->update_clip = (clip_rect || clip_rgn); /* have to update again if we are using a custom region */
514 return dev->pict;
517 static Picture get_xrender_picture_source( struct xrender_physdev *dev, BOOL repeat )
519 if (!dev->pict_src && dev->pict_format)
521 XRenderPictureAttributes pa;
523 pa.subwindow_mode = IncludeInferiors;
524 pa.repeat = repeat ? RepeatNormal : RepeatNone;
525 dev->pict_src = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
526 dev->pict_format, CPSubwindowMode|CPRepeat, &pa );
528 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
529 dev->pict_src, dev->dev.hdc, dev->x11dev->drawable, pa.repeat);
532 return dev->pict_src;
535 static void free_xrender_picture( struct xrender_physdev *dev )
537 if (dev->pict || dev->pict_src)
539 XFlush( gdi_display );
540 if (dev->pict)
542 TRACE("freeing pict = %lx dc = %p\n", dev->pict, dev->dev.hdc);
543 pXRenderFreePicture(gdi_display, dev->pict);
544 dev->pict = 0;
546 if(dev->pict_src)
548 TRACE("freeing pict = %lx dc = %p\n", dev->pict_src, dev->dev.hdc);
549 pXRenderFreePicture(gdi_display, dev->pict_src);
550 dev->pict_src = 0;
555 /* return a mask picture used to force alpha to 0 */
556 static Picture get_no_alpha_mask(void)
558 static Pixmap pixmap;
559 static Picture pict;
561 EnterCriticalSection( &xrender_cs );
562 if (!pict)
564 XRenderPictureAttributes pa;
565 XRenderColor col;
567 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
568 pa.repeat = RepeatNormal;
569 pa.component_alpha = True;
570 pict = pXRenderCreatePicture( gdi_display, pixmap, pict_formats[WXR_FORMAT_A8R8G8B8],
571 CPRepeat|CPComponentAlpha, &pa );
572 col.red = col.green = col.blue = 0xffff;
573 col.alpha = 0;
574 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
576 LeaveCriticalSection( &xrender_cs );
577 return pict;
580 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
582 if(p1->hash != p2->hash) return TRUE;
583 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
584 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
585 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
586 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
589 #if 0
590 static void walk_cache(void)
592 int i;
594 EnterCriticalSection(&xrender_cs);
595 for(i=mru; i >= 0; i = glyphsetCache[i].next)
596 TRACE("item %d\n", i);
597 LeaveCriticalSection(&xrender_cs);
599 #endif
601 static int LookupEntry(LFANDSIZE *plfsz)
603 int i, prev_i = -1;
605 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
606 TRACE("%d\n", i);
607 if(glyphsetCache[i].count == -1) break; /* reached free list so stop */
609 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
610 glyphsetCache[i].count++;
611 if(prev_i >= 0) {
612 glyphsetCache[prev_i].next = glyphsetCache[i].next;
613 glyphsetCache[i].next = mru;
614 mru = i;
616 TRACE("found font in cache %d\n", i);
617 return i;
619 prev_i = i;
621 TRACE("font not in cache\n");
622 return -1;
625 static void FreeEntry(int entry)
627 int type, format;
629 for (type = 0; type < GLYPH_NBTYPES; type++)
631 for(format = 0; format < AA_MAXVALUE; format++) {
632 gsCacheEntryFormat * formatEntry;
634 if( !glyphsetCache[entry].format[type][format] )
635 continue;
637 formatEntry = glyphsetCache[entry].format[type][format];
639 if(formatEntry->glyphset) {
640 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
641 formatEntry->glyphset = 0;
643 if(formatEntry->nrealized) {
644 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
645 formatEntry->realized = NULL;
646 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
647 formatEntry->gis = NULL;
648 formatEntry->nrealized = 0;
651 HeapFree(GetProcessHeap(), 0, formatEntry);
652 glyphsetCache[entry].format[type][format] = NULL;
657 static int AllocEntry(void)
659 int best = -1, prev_best = -1, i, prev_i = -1;
661 if(lastfree >= 0) {
662 assert(glyphsetCache[lastfree].count == -1);
663 glyphsetCache[lastfree].count = 1;
664 best = lastfree;
665 lastfree = glyphsetCache[lastfree].next;
666 assert(best != mru);
667 glyphsetCache[best].next = mru;
668 mru = best;
670 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
671 return mru;
674 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
675 if(glyphsetCache[i].count == 0) {
676 best = i;
677 prev_best = prev_i;
679 prev_i = i;
682 if(best >= 0) {
683 TRACE("freeing unused glyphset at cache %d\n", best);
684 FreeEntry(best);
685 glyphsetCache[best].count = 1;
686 if(prev_best >= 0) {
687 glyphsetCache[prev_best].next = glyphsetCache[best].next;
688 glyphsetCache[best].next = mru;
689 mru = best;
690 } else {
691 assert(mru == best);
693 return mru;
696 TRACE("Growing cache\n");
698 if (glyphsetCache)
699 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
700 glyphsetCache,
701 (glyphsetCacheSize + INIT_CACHE_SIZE)
702 * sizeof(*glyphsetCache));
703 else
704 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
705 (glyphsetCacheSize + INIT_CACHE_SIZE)
706 * sizeof(*glyphsetCache));
708 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
709 i++) {
710 glyphsetCache[i].next = i + 1;
711 glyphsetCache[i].count = -1;
713 glyphsetCache[i-1].next = -1;
714 glyphsetCacheSize += INIT_CACHE_SIZE;
716 lastfree = glyphsetCache[best].next;
717 glyphsetCache[best].count = 1;
718 glyphsetCache[best].next = mru;
719 mru = best;
720 TRACE("new free cache slot at %d\n", mru);
721 return mru;
724 static int GetCacheEntry( LFANDSIZE *plfsz )
726 int ret;
727 gsCacheEntry *entry;
729 if((ret = LookupEntry(plfsz)) != -1) return ret;
731 ret = AllocEntry();
732 entry = glyphsetCache + ret;
733 entry->lfsz = *plfsz;
734 return ret;
737 static void dec_ref_cache(int index)
739 assert(index >= 0);
740 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
741 assert(glyphsetCache[index].count > 0);
742 glyphsetCache[index].count--;
745 static void lfsz_calc_hash(LFANDSIZE *plfsz)
747 DWORD hash = 0, *ptr, two_chars;
748 WORD *pwc;
749 unsigned int i;
751 hash ^= plfsz->devsize.cx;
752 hash ^= plfsz->devsize.cy;
753 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
754 hash ^= *ptr;
755 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
756 hash ^= *ptr;
757 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
758 two_chars = *ptr;
759 pwc = (WCHAR *)&two_chars;
760 if(!*pwc) break;
761 *pwc = toupperW(*pwc);
762 pwc++;
763 *pwc = toupperW(*pwc);
764 hash ^= two_chars;
765 if(!*pwc) break;
767 plfsz->hash = hash;
768 return;
771 static AA_Type aa_type_from_flags( UINT aa_flags )
773 switch (aa_flags & 0x7f)
775 case GGO_BITMAP:
776 return AA_None;
777 case WINE_GGO_GRAY16_BITMAP:
778 return AA_Grey;
779 case WINE_GGO_HRGB_BITMAP:
780 return AA_RGB;
781 case WINE_GGO_HBGR_BITMAP:
782 return AA_BGR;
783 case WINE_GGO_VRGB_BITMAP:
784 return AA_VRGB;
785 case WINE_GGO_VBGR_BITMAP:
786 return AA_VBGR;
787 default:
788 FIXME( "unknown flags %x\n", aa_flags );
789 return AA_None;
793 static UINT get_xft_aa_flags( const LOGFONTW *lf )
795 char *value;
796 UINT ret = 0;
798 switch (lf->lfQuality)
800 case NONANTIALIASED_QUALITY:
801 case ANTIALIASED_QUALITY:
802 break;
803 default:
804 if (!(value = XGetDefault( gdi_display, "Xft", "antialias" ))) break;
805 TRACE( "got antialias '%s'\n", value );
806 if (tolower(value[0]) == 'f' || tolower(value[0]) == 'n' ||
807 value[0] == '0' || !strcasecmp( value, "off" ))
809 ret = GGO_BITMAP;
810 break;
812 ret = GGO_GRAY4_BITMAP;
813 /* fall through */
814 case CLEARTYPE_QUALITY:
815 case CLEARTYPE_NATURAL_QUALITY:
816 if (!(value = XGetDefault( gdi_display, "Xft", "rgba" ))) break;
817 TRACE( "got rgba '%s'\n", value );
818 if (!strcmp( value, "rgb" )) ret = WINE_GGO_HRGB_BITMAP;
819 else if (!strcmp( value, "bgr" )) ret = WINE_GGO_HBGR_BITMAP;
820 else if (!strcmp( value, "vrgb" )) ret = WINE_GGO_VRGB_BITMAP;
821 else if (!strcmp( value, "vbgr" )) ret = WINE_GGO_VBGR_BITMAP;
822 else if (!strcmp( value, "none" )) ret = GGO_GRAY4_BITMAP;
823 break;
825 return ret;
828 /**********************************************************************
829 * xrenderdrv_SelectFont
831 static HFONT xrenderdrv_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
833 LFANDSIZE lfsz;
834 struct xrender_physdev *physdev = get_xrender_dev( dev );
835 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
836 HFONT ret;
838 GetObjectW( hfont, sizeof(lfsz.lf), &lfsz.lf );
839 if (!*aa_flags) *aa_flags = get_xft_aa_flags( &lfsz.lf );
841 ret = next->funcs->pSelectFont( next, hfont, aa_flags );
842 if (!ret) return 0;
844 switch (*aa_flags)
846 case GGO_GRAY2_BITMAP:
847 case GGO_GRAY4_BITMAP:
848 case GGO_GRAY8_BITMAP:
849 physdev->aa_flags = WINE_GGO_GRAY16_BITMAP;
850 break;
851 case 0:
852 physdev->aa_flags = GGO_BITMAP;
853 break;
854 default:
855 physdev->aa_flags = *aa_flags;
856 break;
859 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
860 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
861 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
862 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
863 lfsz.devsize.cx = X11DRV_XWStoDS( dev->hdc, lfsz.lf.lfWidth );
864 lfsz.devsize.cy = X11DRV_YWStoDS( dev->hdc, lfsz.lf.lfHeight );
866 GetTransform( dev->hdc, 0x204, &lfsz.xform );
867 TRACE("font transform %f %f %f %f\n", lfsz.xform.eM11, lfsz.xform.eM12,
868 lfsz.xform.eM21, lfsz.xform.eM22);
870 if (GetGraphicsMode( dev->hdc ) == GM_COMPATIBLE)
872 lfsz.lf.lfOrientation = lfsz.lf.lfEscapement;
873 if (lfsz.xform.eM11 * lfsz.xform.eM22 < 0)
874 lfsz.lf.lfOrientation = -lfsz.lf.lfOrientation;
877 /* Not used fields, would break hashing */
878 lfsz.xform.eDx = lfsz.xform.eDy = 0;
880 lfsz_calc_hash(&lfsz);
882 EnterCriticalSection(&xrender_cs);
883 if (physdev->cache_index != -1)
884 dec_ref_cache( physdev->cache_index );
885 physdev->cache_index = GetCacheEntry( &lfsz );
886 LeaveCriticalSection(&xrender_cs);
887 return ret;
890 static BOOL create_xrender_dc( PHYSDEV *pdev, enum wxr_format format )
892 X11DRV_PDEVICE *x11dev = get_x11drv_dev( *pdev );
893 struct xrender_physdev *physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) );
895 if (!physdev) return FALSE;
896 physdev->x11dev = x11dev;
897 physdev->cache_index = -1;
898 physdev->format = format;
899 physdev->pict_format = pict_formats[format];
900 push_dc_driver( pdev, &physdev->dev, &xrender_funcs );
901 return TRUE;
904 /* store the color mask data in the bitmap info structure */
905 static void set_color_info( XRenderPictFormat *format, BITMAPINFO *info )
907 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
909 info->bmiHeader.biPlanes = 1;
910 info->bmiHeader.biBitCount = pixmap_formats[format->depth]->bits_per_pixel;
911 info->bmiHeader.biCompression = BI_RGB;
912 info->bmiHeader.biClrUsed = 0;
914 switch (info->bmiHeader.biBitCount)
916 case 16:
917 colors[0] = format->direct.redMask << format->direct.red;
918 colors[1] = format->direct.greenMask << format->direct.green;
919 colors[2] = format->direct.blueMask << format->direct.blue;
920 info->bmiHeader.biCompression = BI_BITFIELDS;
921 break;
922 case 32:
923 colors[0] = format->direct.redMask << format->direct.red;
924 colors[1] = format->direct.greenMask << format->direct.green;
925 colors[2] = format->direct.blueMask << format->direct.blue;
926 if (colors[0] != 0xff0000 || colors[1] != 0x00ff00 || colors[2] != 0x0000ff)
927 info->bmiHeader.biCompression = BI_BITFIELDS;
928 break;
933 /**********************************************************************
934 * xrenderdrv_CreateDC
936 static BOOL xrenderdrv_CreateDC( PHYSDEV *pdev, LPCWSTR driver, LPCWSTR device,
937 LPCWSTR output, const DEVMODEW* initData )
939 return create_xrender_dc( pdev, default_format );
942 /**********************************************************************
943 * xrenderdrv_CreateCompatibleDC
945 static BOOL xrenderdrv_CreateCompatibleDC( PHYSDEV orig, PHYSDEV *pdev )
947 if (orig) /* chain to x11drv first */
949 orig = GET_NEXT_PHYSDEV( orig, pCreateCompatibleDC );
950 if (!orig->funcs->pCreateCompatibleDC( orig, pdev )) return FALSE;
952 /* otherwise we have been called by x11drv */
954 return create_xrender_dc( pdev, WXR_FORMAT_MONO );
957 /**********************************************************************
958 * xrenderdrv_DeleteDC
960 static BOOL xrenderdrv_DeleteDC( PHYSDEV dev )
962 struct xrender_physdev *physdev = get_xrender_dev( dev );
964 free_xrender_picture( physdev );
966 EnterCriticalSection( &xrender_cs );
967 if (physdev->cache_index != -1) dec_ref_cache( physdev->cache_index );
968 LeaveCriticalSection( &xrender_cs );
970 HeapFree( GetProcessHeap(), 0, physdev );
971 return TRUE;
974 /**********************************************************************
975 * xrenderdrv_ExtEscape
977 static INT xrenderdrv_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOID in_data,
978 INT out_count, LPVOID out_data )
980 struct xrender_physdev *physdev = get_xrender_dev( dev );
982 dev = GET_NEXT_PHYSDEV( dev, pExtEscape );
984 if (escape == X11DRV_ESCAPE && in_data && in_count >= sizeof(enum x11drv_escape_codes))
986 if (*(const enum x11drv_escape_codes *)in_data == X11DRV_SET_DRAWABLE)
988 BOOL ret = dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
989 if (ret) free_xrender_picture( physdev ); /* pict format doesn't change, only drawable */
990 return ret;
993 return dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
996 /***********************************************************************
997 * xrenderdrv_SetDeviceClipping
999 static void xrenderdrv_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
1001 struct xrender_physdev *physdev = get_xrender_dev( dev );
1003 physdev->region = rgn;
1004 physdev->update_clip = TRUE;
1006 dev = GET_NEXT_PHYSDEV( dev, pSetDeviceClipping );
1007 dev->funcs->pSetDeviceClipping( dev, rgn );
1011 /************************************************************************
1012 * UploadGlyph
1014 * Helper to ExtTextOut. Must be called inside xrender_cs
1016 static void UploadGlyph(struct xrender_physdev *physDev, UINT glyph, enum glyph_type type)
1018 unsigned int buflen;
1019 char *buf;
1020 Glyph gid;
1021 GLYPHMETRICS gm;
1022 XGlyphInfo gi;
1023 gsCacheEntry *entry = glyphsetCache + physDev->cache_index;
1024 gsCacheEntryFormat *formatEntry;
1025 UINT ggo_format = physDev->aa_flags;
1026 AA_Type format = aa_type_from_flags( physDev->aa_flags );
1027 enum wxr_format wxr_format;
1028 static const char zero[4];
1029 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1031 if (type == GLYPH_INDEX) ggo_format |= GGO_GLYPH_INDEX;
1032 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1033 if(buflen == GDI_ERROR) {
1034 if(format != AA_None) {
1035 format = AA_None;
1036 physDev->aa_flags = GGO_BITMAP;
1037 ggo_format = (ggo_format & GGO_GLYPH_INDEX) | GGO_BITMAP;
1038 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1040 if(buflen == GDI_ERROR) {
1041 WARN("GetGlyphOutlineW failed using default glyph\n");
1042 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0, ggo_format, &gm, 0, NULL, &identity);
1043 if(buflen == GDI_ERROR) {
1044 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1045 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0x20, ggo_format, &gm, 0, NULL, &identity);
1046 if(buflen == GDI_ERROR) {
1047 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1048 return;
1052 TRACE("Turning off antialiasing for this monochrome font\n");
1055 /* If there is nothing for the current type, we create the entry. */
1056 if( !entry->format[type][format] ) {
1057 entry->format[type][format] = HeapAlloc(GetProcessHeap(),
1058 HEAP_ZERO_MEMORY,
1059 sizeof(gsCacheEntryFormat));
1061 formatEntry = entry->format[type][format];
1063 if(formatEntry->nrealized <= glyph) {
1064 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1066 if (formatEntry->realized)
1067 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1068 HEAP_ZERO_MEMORY,
1069 formatEntry->realized,
1070 formatEntry->nrealized * sizeof(BOOL));
1071 else
1072 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1073 HEAP_ZERO_MEMORY,
1074 formatEntry->nrealized * sizeof(BOOL));
1076 if (formatEntry->gis)
1077 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1078 HEAP_ZERO_MEMORY,
1079 formatEntry->gis,
1080 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1081 else
1082 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1083 HEAP_ZERO_MEMORY,
1084 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1088 if(formatEntry->glyphset == 0) {
1089 switch(format) {
1090 case AA_Grey:
1091 wxr_format = WXR_FORMAT_GRAY;
1092 break;
1094 case AA_RGB:
1095 case AA_BGR:
1096 case AA_VRGB:
1097 case AA_VBGR:
1098 wxr_format = WXR_FORMAT_A8R8G8B8;
1099 break;
1101 default:
1102 ERR("aa = %d - not implemented\n", format);
1103 /* fall through */
1104 case AA_None:
1105 wxr_format = WXR_FORMAT_MONO;
1106 break;
1109 formatEntry->font_format = pict_formats[wxr_format];
1110 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
1114 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1115 if (buflen)
1116 GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1117 else
1118 gm.gmBlackBoxX = gm.gmBlackBoxY = 0; /* empty glyph */
1119 formatEntry->realized[glyph] = TRUE;
1121 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1122 buflen,
1123 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1124 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1126 gi.width = gm.gmBlackBoxX;
1127 gi.height = gm.gmBlackBoxY;
1128 gi.x = -gm.gmptGlyphOrigin.x;
1129 gi.y = gm.gmptGlyphOrigin.y;
1130 gi.xOff = gm.gmCellIncX;
1131 gi.yOff = gm.gmCellIncY;
1133 if(TRACE_ON(xrender)) {
1134 int pitch, i, j;
1135 char output[300];
1136 unsigned char *line;
1138 if(format == AA_None) {
1139 pitch = ((gi.width + 31) / 32) * 4;
1140 for(i = 0; i < gi.height; i++) {
1141 line = (unsigned char*) buf + i * pitch;
1142 output[0] = '\0';
1143 for(j = 0; j < pitch * 8; j++) {
1144 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1146 TRACE("%s\n", output);
1148 } else {
1149 static const char blks[] = " .:;!o*#";
1150 char str[2];
1152 str[1] = '\0';
1153 pitch = ((gi.width + 3) / 4) * 4;
1154 for(i = 0; i < gi.height; i++) {
1155 line = (unsigned char*) buf + i * pitch;
1156 output[0] = '\0';
1157 for(j = 0; j < pitch; j++) {
1158 str[0] = blks[line[j] >> 5];
1159 strcat(output, str);
1161 TRACE("%s\n", output);
1167 if(formatEntry->glyphset) {
1168 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1169 unsigned char *byte = (unsigned char*) buf, c;
1170 int i = buflen;
1172 while(i--) {
1173 c = *byte;
1175 /* magic to flip bit order */
1176 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1177 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1178 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1180 *byte++ = c;
1183 else if ( format != AA_Grey &&
1184 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1186 unsigned int i, *data = (unsigned int *)buf;
1187 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1189 gid = glyph;
1192 XRenderCompositeText seems to ignore 0x0 glyphs when
1193 AA_None, which means we lose the advance width of glyphs
1194 like the space. We'll pretend that such glyphs are 1x1
1195 bitmaps.
1198 if(buflen == 0)
1199 gi.width = gi.height = 1;
1201 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1202 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1205 HeapFree(GetProcessHeap(), 0, buf);
1206 formatEntry->gis[glyph] = gi;
1209 /*************************************************************
1210 * get_tile_pict
1212 * Returns an appropriate Picture for tiling the text colour.
1213 * Call and use result within the xrender_cs
1215 static Picture get_tile_pict( enum wxr_format wxr_format, const XRenderColor *color)
1217 static struct
1219 Pixmap xpm;
1220 Picture pict;
1221 XRenderColor current_color;
1222 } tiles[WXR_NB_FORMATS], *tile;
1224 tile = &tiles[wxr_format];
1226 if(!tile->xpm)
1228 XRenderPictureAttributes pa;
1229 XRenderPictFormat *pict_format = pict_formats[wxr_format];
1231 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_format->depth);
1233 pa.repeat = RepeatNormal;
1234 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_format, CPRepeat, &pa);
1236 /* init current_color to something different from text_pixel */
1237 tile->current_color = *color;
1238 tile->current_color.red ^= 0xffff;
1240 if (wxr_format == WXR_FORMAT_MONO)
1242 /* for a 1bpp bitmap we always need a 1 in the tile */
1243 XRenderColor col;
1244 col.red = col.green = col.blue = 0;
1245 col.alpha = 0xffff;
1246 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1250 if (memcmp( color, &tile->current_color, sizeof(*color) ) && wxr_format != WXR_FORMAT_MONO)
1252 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, color, 0, 0, 1, 1);
1253 tile->current_color = *color;
1255 return tile->pict;
1258 /*************************************************************
1259 * get_mask_pict
1261 * Returns an appropriate Picture for masking with the specified alpha.
1262 * Call and use result within the xrender_cs
1264 static Picture get_mask_pict( int alpha )
1266 static Pixmap pixmap;
1267 static Picture pict;
1268 static int current_alpha;
1270 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1272 if (!pixmap)
1274 XRenderPictureAttributes pa;
1276 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1277 pa.repeat = RepeatNormal;
1278 pict = pXRenderCreatePicture( gdi_display, pixmap,
1279 pict_formats[WXR_FORMAT_A8R8G8B8], CPRepeat, &pa );
1280 current_alpha = -1;
1283 if (alpha != current_alpha)
1285 XRenderColor col;
1286 col.red = col.green = col.blue = 0;
1287 col.alpha = current_alpha = alpha;
1288 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1290 return pict;
1293 /***********************************************************************
1294 * xrenderdrv_ExtTextOut
1296 static BOOL xrenderdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
1297 const RECT *lprect, LPCWSTR wstr, UINT count, const INT *lpDx )
1299 struct xrender_physdev *physdev = get_xrender_dev( dev );
1300 gsCacheEntry *entry;
1301 gsCacheEntryFormat *formatEntry;
1302 unsigned int idx;
1303 Picture pict, tile_pict = 0;
1304 XGlyphElt16 *elts;
1305 POINT offset, desired, current;
1306 int render_op = PictOpOver;
1307 XRenderColor col;
1308 RECT rect, bounds;
1309 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
1311 get_xrender_color( physdev, GetTextColor( physdev->dev.hdc ), &col );
1312 pict = get_xrender_picture( physdev, 0, (flags & ETO_CLIPPED) ? lprect : NULL );
1314 if(flags & ETO_OPAQUE)
1316 XRenderColor bg;
1318 if (physdev->format == WXR_FORMAT_MONO)
1319 /* use the inverse of the text color */
1320 bg.red = bg.green = bg.blue = bg.alpha = ~col.alpha;
1321 else
1322 get_xrender_color( physdev, GetBkColor( physdev->dev.hdc ), &bg );
1324 set_xrender_transformation( pict, 1, 1, 0, 0 );
1325 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &bg,
1326 physdev->x11dev->dc_rect.left + lprect->left,
1327 physdev->x11dev->dc_rect.top + lprect->top,
1328 lprect->right - lprect->left,
1329 lprect->bottom - lprect->top );
1330 add_device_bounds( physdev->x11dev, lprect );
1333 if(count == 0) return TRUE;
1335 EnterCriticalSection(&xrender_cs);
1337 entry = glyphsetCache + physdev->cache_index;
1338 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1340 for(idx = 0; idx < count; idx++) {
1341 if( !formatEntry ) {
1342 UploadGlyph(physdev, wstr[idx], type);
1343 /* re-evaluate format entry since aa_flags may have changed */
1344 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1345 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1346 UploadGlyph(physdev, wstr[idx], type);
1349 if (!formatEntry)
1351 WARN("could not upload requested glyphs\n");
1352 LeaveCriticalSection(&xrender_cs);
1353 return FALSE;
1356 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1357 physdev->x11dev->dc_rect.left + x, physdev->x11dev->dc_rect.top + y);
1359 elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1361 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1362 So we pass zeros to the function and move to our starting position using the first
1363 element of the elts array. */
1365 desired.x = physdev->x11dev->dc_rect.left + x;
1366 desired.y = physdev->x11dev->dc_rect.top + y;
1367 offset.x = offset.y = 0;
1368 current.x = current.y = 0;
1370 tile_pict = get_tile_pict(physdev->format, &col);
1372 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1374 if (physdev->format == WXR_FORMAT_MONO && col.red == 0 && col.green == 0 && col.blue == 0)
1375 render_op = PictOpOutReverse; /* This gives us 'black' text */
1377 reset_bounds( &bounds );
1378 for(idx = 0; idx < count; idx++)
1380 elts[idx].glyphset = formatEntry->glyphset;
1381 elts[idx].chars = wstr + idx;
1382 elts[idx].nchars = 1;
1383 elts[idx].xOff = desired.x - current.x;
1384 elts[idx].yOff = desired.y - current.y;
1386 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1387 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1389 rect.left = desired.x - physdev->x11dev->dc_rect.left - formatEntry->gis[wstr[idx]].x;
1390 rect.top = desired.y - physdev->x11dev->dc_rect.top - formatEntry->gis[wstr[idx]].y;
1391 rect.right = rect.left + formatEntry->gis[wstr[idx]].width;
1392 rect.bottom = rect.top + formatEntry->gis[wstr[idx]].height;
1393 add_bounds_rect( &bounds, &rect );
1395 if(!lpDx)
1397 desired.x += formatEntry->gis[wstr[idx]].xOff;
1398 desired.y += formatEntry->gis[wstr[idx]].yOff;
1400 else
1402 if(flags & ETO_PDY)
1404 offset.x += lpDx[idx * 2];
1405 offset.y += lpDx[idx * 2 + 1];
1407 else
1408 offset.x += lpDx[idx];
1409 desired.x = physdev->x11dev->dc_rect.left + x + offset.x;
1410 desired.y = physdev->x11dev->dc_rect.top + y + offset.y;
1414 /* Make sure we don't have any transforms set from a previous call */
1415 set_xrender_transformation(pict, 1, 1, 0, 0);
1416 pXRenderCompositeText16(gdi_display, render_op,
1417 tile_pict,
1418 pict,
1419 formatEntry->font_format,
1420 0, 0, 0, 0, elts, count);
1421 HeapFree(GetProcessHeap(), 0, elts);
1423 LeaveCriticalSection(&xrender_cs);
1424 add_device_bounds( physdev->x11dev, &bounds );
1425 return TRUE;
1428 /* multiply the alpha channel of a picture */
1429 static void multiply_alpha( Picture pict, XRenderPictFormat *format, int alpha,
1430 int x, int y, int width, int height )
1432 XRenderPictureAttributes pa;
1433 Pixmap src_pixmap, mask_pixmap;
1434 Picture src_pict, mask_pict;
1435 XRenderColor color;
1437 src_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1438 mask_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1439 pa.repeat = RepeatNormal;
1440 src_pict = pXRenderCreatePicture( gdi_display, src_pixmap, format, CPRepeat, &pa );
1441 pa.component_alpha = True;
1442 mask_pict = pXRenderCreatePicture( gdi_display, mask_pixmap, format, CPRepeat|CPComponentAlpha, &pa );
1443 color.red = color.green = color.blue = color.alpha = 0xffff;
1444 pXRenderFillRectangle( gdi_display, PictOpSrc, src_pict, &color, 0, 0, 1, 1 );
1445 color.alpha = alpha;
1446 pXRenderFillRectangle( gdi_display, PictOpSrc, mask_pict, &color, 0, 0, 1, 1 );
1447 pXRenderComposite( gdi_display, PictOpInReverse, src_pict, mask_pict, pict,
1448 0, 0, 0, 0, x, y, width, height );
1449 pXRenderFreePicture( gdi_display, src_pict );
1450 pXRenderFreePicture( gdi_display, mask_pict );
1451 XFreePixmap( gdi_display, src_pixmap );
1452 XFreePixmap( gdi_display, mask_pixmap );
1455 /* Helper function for (stretched) blitting using xrender */
1456 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1457 int x_src, int y_src, int width_src, int height_src,
1458 int x_dst, int y_dst, int width_dst, int height_dst,
1459 double xscale, double yscale )
1461 int x_offset, y_offset;
1463 if (width_src < 0)
1465 x_src += width_src + 1;
1466 width_src = -width_src;
1468 if (height_src < 0)
1470 y_src += height_src + 1;
1471 height_src = -height_src;
1473 if (width_dst < 0)
1475 x_dst += width_dst + 1;
1476 width_dst = -width_dst;
1478 if (height_dst < 0)
1480 y_dst += height_dst + 1;
1481 height_dst = -height_dst;
1484 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1485 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1486 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1487 if(xscale != 1.0 || yscale != 1.0)
1489 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1490 * in the wrong quadrant of the x-y plane.
1492 x_offset = (xscale < 0) ? -width_dst : 0;
1493 y_offset = (yscale < 0) ? -height_dst : 0;
1494 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1496 else
1498 x_offset = x_src;
1499 y_offset = y_src;
1500 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1502 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1503 x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst );
1506 /* Helper function for (stretched) mono->color blitting using xrender */
1507 static void xrender_mono_blit( Picture src_pict, Picture dst_pict,
1508 enum wxr_format dst_format, XRenderColor *fg, XRenderColor *bg,
1509 int x_src, int y_src, int width_src, int height_src,
1510 int x_dst, int y_dst, int width_dst, int height_dst,
1511 double xscale, double yscale )
1513 Picture tile_pict;
1514 int x_offset, y_offset;
1515 XRenderColor color;
1517 if (width_src < 0)
1519 x_src += width_src + 1;
1520 width_src = -width_src;
1522 if (height_src < 0)
1524 y_src += height_src + 1;
1525 height_src = -height_src;
1527 if (width_dst < 0)
1529 x_dst += width_dst + 1;
1530 width_dst = -width_dst;
1532 if (height_dst < 0)
1534 y_dst += height_dst + 1;
1535 height_dst = -height_dst;
1538 /* When doing a mono->color blit, the source data is used as mask, and the source picture
1539 * contains a 1x1 picture for tiling. The source data effectively acts as an alpha channel to
1540 * the tile data.
1542 EnterCriticalSection( &xrender_cs );
1543 color = *bg;
1544 color.alpha = 0xffff; /* tile pict needs 100% alpha */
1545 tile_pict = get_tile_pict( dst_format, &color );
1547 pXRenderFillRectangle( gdi_display, PictOpSrc, dst_pict, fg, x_dst, y_dst, width_dst, height_dst );
1549 if (xscale != 1.0 || yscale != 1.0)
1551 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1552 * in the wrong quadrant of the x-y plane.
1554 x_offset = (xscale < 0) ? -width_dst : 0;
1555 y_offset = (yscale < 0) ? -height_dst : 0;
1556 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1558 else
1560 x_offset = x_src;
1561 y_offset = y_src;
1562 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1564 pXRenderComposite(gdi_display, PictOpOver, tile_pict, src_pict, dst_pict,
1565 0, 0, x_offset, y_offset, x_dst, y_dst, width_dst, height_dst );
1566 LeaveCriticalSection( &xrender_cs );
1568 /* force the alpha channel for background pixels, it has been set to 100% by the tile */
1569 if (bg->alpha != 0xffff && (dst_format == WXR_FORMAT_A8R8G8B8 || dst_format == WXR_FORMAT_B8G8R8A8))
1570 multiply_alpha( dst_pict, pict_formats[dst_format], bg->alpha,
1571 x_dst, y_dst, width_dst, height_dst );
1574 /* create a pixmap and render picture for an image */
1575 static DWORD create_image_pixmap( BITMAPINFO *info, const struct gdi_image_bits *bits,
1576 struct bitblt_coords *src, enum wxr_format format,
1577 Pixmap *pixmap, Picture *pict, BOOL *use_repeat )
1579 DWORD ret;
1580 int width = src->visrect.right - src->visrect.left;
1581 int height = src->visrect.bottom - src->visrect.top;
1582 int depth = pict_formats[format]->depth;
1583 struct gdi_image_bits dst_bits;
1584 XRenderPictureAttributes pa;
1585 GC gc;
1586 XImage *image;
1588 image = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0, NULL,
1589 info->bmiHeader.biWidth, height, 32, 0 );
1590 if (!image) return ERROR_OUTOFMEMORY;
1592 ret = copy_image_bits( info, (format == WXR_FORMAT_R8G8B8), image, bits, &dst_bits, src, NULL, ~0u );
1593 if (ret) return ret;
1595 image->data = dst_bits.ptr;
1597 *use_repeat = (width == 1 && height == 1);
1598 pa.repeat = *use_repeat ? RepeatNormal : RepeatNone;
1600 *pixmap = XCreatePixmap( gdi_display, root_window, width, height, depth );
1601 gc = XCreateGC( gdi_display, *pixmap, 0, NULL );
1602 XPutImage( gdi_display, *pixmap, gc, image, src->visrect.left, 0, 0, 0, width, height );
1603 *pict = pXRenderCreatePicture( gdi_display, *pixmap, pict_formats[format], CPRepeat, &pa );
1604 XFreeGC( gdi_display, gc );
1606 /* make coordinates relative to the pixmap */
1607 src->x -= src->visrect.left;
1608 src->y -= src->visrect.top;
1609 OffsetRect( &src->visrect, -src->visrect.left, -src->visrect.top );
1611 image->data = NULL;
1612 XDestroyImage( image );
1613 if (dst_bits.free) dst_bits.free( &dst_bits );
1614 return ret;
1617 static void xrender_stretch_blit( struct xrender_physdev *physdev_src, struct xrender_physdev *physdev_dst,
1618 Drawable drawable, const struct bitblt_coords *src,
1619 const struct bitblt_coords *dst )
1621 int x_dst, y_dst;
1622 Picture src_pict = 0, dst_pict, mask_pict = 0;
1623 double xscale = src->width / (double)dst->width;
1624 double yscale = src->height / (double)dst->height;
1626 if (drawable) /* using an intermediate pixmap */
1628 x_dst = dst->x;
1629 y_dst = dst->y;
1630 dst_pict = pXRenderCreatePicture( gdi_display, drawable, physdev_dst->pict_format, 0, NULL );
1632 else
1634 x_dst = physdev_dst->x11dev->dc_rect.left + dst->x;
1635 y_dst = physdev_dst->x11dev->dc_rect.top + dst->y;
1636 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1639 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1641 /* mono -> color */
1642 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1644 XRenderColor fg, bg;
1646 get_xrender_color( physdev_dst, GetTextColor( physdev_dst->dev.hdc ), &fg );
1647 get_xrender_color( physdev_dst, GetBkColor( physdev_dst->dev.hdc ), &bg );
1648 fg.alpha = bg.alpha = 0;
1650 xrender_mono_blit( src_pict, dst_pict, physdev_dst->format, &fg, &bg,
1651 physdev_src->x11dev->dc_rect.left + src->x,
1652 physdev_src->x11dev->dc_rect.top + src->y,
1653 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1655 else /* color -> color (can be at different depths) or mono -> mono */
1657 if (physdev_dst->pict_format->depth == 32 && physdev_src->pict_format->depth < 32)
1658 mask_pict = get_no_alpha_mask();
1660 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict,
1661 physdev_src->x11dev->dc_rect.left + src->x,
1662 physdev_src->x11dev->dc_rect.top + src->y,
1663 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1666 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1670 static void xrender_put_image( Pixmap src_pixmap, Picture src_pict, Picture mask_pict, HRGN clip,
1671 XRenderPictFormat *dst_format, struct xrender_physdev *physdev,
1672 Drawable drawable, struct bitblt_coords *src,
1673 struct bitblt_coords *dst, BOOL use_repeat )
1675 int x_dst, y_dst;
1676 Picture dst_pict;
1677 double xscale, yscale;
1679 if (drawable) /* using an intermediate pixmap */
1681 RGNDATA *clip_data = NULL;
1683 if (clip) clip_data = X11DRV_GetRegionData( clip, 0 );
1684 x_dst = dst->x;
1685 y_dst = dst->y;
1686 dst_pict = pXRenderCreatePicture( gdi_display, drawable, dst_format, 0, NULL );
1687 if (clip_data)
1688 pXRenderSetPictureClipRectangles( gdi_display, dst_pict, 0, 0,
1689 (XRectangle *)clip_data->Buffer, clip_data->rdh.nCount );
1690 HeapFree( GetProcessHeap(), 0, clip_data );
1692 else
1694 x_dst = physdev->x11dev->dc_rect.left + dst->x;
1695 y_dst = physdev->x11dev->dc_rect.top + dst->y;
1696 dst_pict = get_xrender_picture( physdev, clip, &dst->visrect );
1699 if (!use_repeat)
1701 xscale = src->width / (double)dst->width;
1702 yscale = src->height / (double)dst->height;
1704 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1706 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict, src->x, src->y, src->width, src->height,
1707 x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1709 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1713 /***********************************************************************
1714 * xrenderdrv_StretchBlt
1716 static BOOL xrenderdrv_StretchBlt( PHYSDEV dst_dev, struct bitblt_coords *dst,
1717 PHYSDEV src_dev, struct bitblt_coords *src, DWORD rop )
1719 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1720 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1721 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
1723 if (src_dev->funcs != dst_dev->funcs)
1725 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pStretchBlt );
1726 return dst_dev->funcs->pStretchBlt( dst_dev, dst, src_dev, src, rop );
1729 /* XRender is of no use for color -> mono */
1730 if (physdev_dst->format == WXR_FORMAT_MONO && physdev_src->format != WXR_FORMAT_MONO)
1731 goto x11drv_fallback;
1733 /* if not stretching, we only need to handle format conversion */
1734 if (!stretch && physdev_dst->format == physdev_src->format) goto x11drv_fallback;
1736 if (rop != SRCCOPY)
1738 GC tmpGC;
1739 Pixmap tmp_pixmap;
1740 struct bitblt_coords tmp;
1742 /* make coordinates relative to tmp pixmap */
1743 tmp = *dst;
1744 tmp.x -= tmp.visrect.left;
1745 tmp.y -= tmp.visrect.top;
1746 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1748 tmpGC = XCreateGC( gdi_display, physdev_dst->x11dev->drawable, 0, NULL );
1749 XSetSubwindowMode( gdi_display, tmpGC, IncludeInferiors );
1750 XSetGraphicsExposures( gdi_display, tmpGC, False );
1751 tmp_pixmap = XCreatePixmap( gdi_display, root_window, tmp.visrect.right - tmp.visrect.left,
1752 tmp.visrect.bottom - tmp.visrect.top, physdev_dst->pict_format->depth );
1754 xrender_stretch_blit( physdev_src, physdev_dst, tmp_pixmap, src, &tmp );
1755 execute_rop( physdev_dst->x11dev, tmp_pixmap, tmpGC, &dst->visrect, rop );
1757 XFreePixmap( gdi_display, tmp_pixmap );
1758 XFreeGC( gdi_display, tmpGC );
1760 else xrender_stretch_blit( physdev_src, physdev_dst, 0, src, dst );
1762 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
1763 return TRUE;
1765 x11drv_fallback:
1766 return X11DRV_StretchBlt( &physdev_dst->x11dev->dev, dst, &physdev_src->x11dev->dev, src, rop );
1770 /***********************************************************************
1771 * xrenderdrv_PutImage
1773 static DWORD xrenderdrv_PutImage( PHYSDEV dev, HRGN clip, BITMAPINFO *info,
1774 const struct gdi_image_bits *bits, struct bitblt_coords *src,
1775 struct bitblt_coords *dst, DWORD rop )
1777 struct xrender_physdev *physdev = get_xrender_dev( dev );
1778 DWORD ret;
1779 Pixmap tmp_pixmap;
1780 GC gc;
1781 enum wxr_format src_format, dst_format;
1782 XRenderPictFormat *pict_format;
1783 Pixmap src_pixmap;
1784 Picture src_pict, mask_pict = 0;
1785 BOOL use_repeat;
1787 dst_format = physdev->format;
1788 src_format = get_xrender_format_from_bitmapinfo( info );
1789 if (!(pict_format = pict_formats[src_format])) goto update_format;
1791 /* make sure we can create an image with the same bpp */
1792 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1793 goto update_format;
1795 /* mono <-> color conversions not supported */
1796 if ((src_format != dst_format) && (src_format == WXR_FORMAT_MONO || dst_format == WXR_FORMAT_MONO))
1797 goto x11drv_fallback;
1799 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1801 if (!has_alpha( src_format ) && has_alpha( dst_format )) mask_pict = get_no_alpha_mask();
1803 ret = create_image_pixmap( info, bits, src, src_format, &src_pixmap, &src_pict, &use_repeat );
1804 if (!ret)
1806 struct bitblt_coords tmp;
1808 if (rop != SRCCOPY)
1810 BOOL restore_region = add_extra_clipping_region( physdev->x11dev, clip );
1812 /* make coordinates relative to tmp pixmap */
1813 tmp = *dst;
1814 tmp.x -= tmp.visrect.left;
1815 tmp.y -= tmp.visrect.top;
1816 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1818 gc = XCreateGC( gdi_display, physdev->x11dev->drawable, 0, NULL );
1819 XSetSubwindowMode( gdi_display, gc, IncludeInferiors );
1820 XSetGraphicsExposures( gdi_display, gc, False );
1821 tmp_pixmap = XCreatePixmap( gdi_display, root_window,
1822 tmp.visrect.right - tmp.visrect.left,
1823 tmp.visrect.bottom - tmp.visrect.top,
1824 physdev->pict_format->depth );
1826 xrender_put_image( src_pixmap, src_pict, mask_pict, NULL, physdev->pict_format,
1827 NULL, tmp_pixmap, src, &tmp, use_repeat );
1828 execute_rop( physdev->x11dev, tmp_pixmap, gc, &dst->visrect, rop );
1830 XFreePixmap( gdi_display, tmp_pixmap );
1831 XFreeGC( gdi_display, gc );
1832 if (restore_region) restore_clipping_region( physdev->x11dev );
1834 else xrender_put_image( src_pixmap, src_pict, mask_pict, clip,
1835 physdev->pict_format, physdev, 0, src, dst, use_repeat );
1837 add_device_bounds( physdev->x11dev, &dst->visrect );
1839 pXRenderFreePicture( gdi_display, src_pict );
1840 XFreePixmap( gdi_display, src_pixmap );
1842 return ret;
1844 update_format:
1845 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1846 set_color_info( pict_formats[dst_format], info );
1847 return ERROR_BAD_FORMAT;
1849 x11drv_fallback:
1850 dev = GET_NEXT_PHYSDEV( dev, pPutImage );
1851 return dev->funcs->pPutImage( dev, clip, info, bits, src, dst, rop );
1855 /***********************************************************************
1856 * xrenderdrv_BlendImage
1858 static DWORD xrenderdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
1859 struct bitblt_coords *src, struct bitblt_coords *dst,
1860 BLENDFUNCTION func )
1862 struct xrender_physdev *physdev = get_xrender_dev( dev );
1863 DWORD ret;
1864 enum wxr_format format;
1865 XRenderPictFormat *pict_format;
1866 Picture dst_pict, src_pict, mask_pict;
1867 Pixmap src_pixmap;
1868 BOOL use_repeat;
1870 format = get_xrender_format_from_bitmapinfo( info );
1871 if (!(func.AlphaFormat & AC_SRC_ALPHA))
1872 format = get_format_without_alpha( format );
1873 else if (format != WXR_FORMAT_A8R8G8B8 || info->bmiHeader.biCompression != BI_RGB)
1874 return ERROR_INVALID_PARAMETER;
1876 if (!(pict_format = pict_formats[format])) goto update_format;
1878 /* make sure we can create an image with the same bpp */
1879 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1880 goto update_format;
1882 if (format == WXR_FORMAT_MONO && physdev->format != WXR_FORMAT_MONO)
1883 goto update_format;
1885 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1887 ret = create_image_pixmap( info, bits, src, format, &src_pixmap, &src_pict, &use_repeat );
1888 if (!ret)
1890 double xscale, yscale;
1892 if (!use_repeat)
1894 xscale = src->width / (double)dst->width;
1895 yscale = src->height / (double)dst->height;
1897 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1899 dst_pict = get_xrender_picture( physdev, 0, &dst->visrect );
1901 EnterCriticalSection( &xrender_cs );
1902 mask_pict = get_mask_pict( func.SourceConstantAlpha * 257 );
1904 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1905 src->x, src->y, src->width, src->height,
1906 physdev->x11dev->dc_rect.left + dst->x,
1907 physdev->x11dev->dc_rect.top + dst->y,
1908 dst->width, dst->height, xscale, yscale );
1910 pXRenderFreePicture( gdi_display, src_pict );
1911 XFreePixmap( gdi_display, src_pixmap );
1913 LeaveCriticalSection( &xrender_cs );
1914 add_device_bounds( physdev->x11dev, &dst->visrect );
1916 return ret;
1918 update_format:
1919 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1920 set_color_info( physdev->pict_format, info );
1921 return ERROR_BAD_FORMAT;
1925 /***********************************************************************
1926 * xrenderdrv_AlphaBlend
1928 static BOOL xrenderdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
1929 PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blendfn )
1931 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1932 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1933 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
1934 XRenderPictureAttributes pa;
1935 Pixmap tmp_pixmap = 0;
1936 double xscale, yscale;
1938 if (src_dev->funcs != dst_dev->funcs)
1940 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pAlphaBlend );
1941 return dst_dev->funcs->pAlphaBlend( dst_dev, dst, src_dev, src, blendfn );
1944 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->format != WXR_FORMAT_A8R8G8B8)
1946 SetLastError( ERROR_INVALID_PARAMETER );
1947 return FALSE;
1950 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1952 xscale = src->width / (double)dst->width;
1953 yscale = src->height / (double)dst->height;
1955 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1957 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1959 /* mono -> color blending needs an intermediate color pixmap */
1960 XRenderColor fg, bg;
1961 int width = src->visrect.right - src->visrect.left;
1962 int height = src->visrect.bottom - src->visrect.top;
1964 /* blending doesn't use the destination DC colors */
1965 fg.red = fg.green = fg.blue = 0;
1966 bg.red = bg.green = bg.blue = 0xffff;
1967 fg.alpha = bg.alpha = 0xffff;
1969 tmp_pixmap = XCreatePixmap( gdi_display, root_window, width, height,
1970 physdev_dst->pict_format->depth );
1971 tmp_pict = pXRenderCreatePicture( gdi_display, tmp_pixmap, physdev_dst->pict_format, 0, NULL );
1973 xrender_mono_blit( src_pict, tmp_pict, physdev_dst->format, &fg, &bg,
1974 src->visrect.left, src->visrect.top, width, height, 0, 0, width, height, 1, 1 );
1976 else if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->pict_format)
1978 /* we need a source picture with no alpha */
1979 enum wxr_format format = get_format_without_alpha( physdev_src->format );
1980 if (format != physdev_src->format)
1982 pa.subwindow_mode = IncludeInferiors;
1983 tmp_pict = pXRenderCreatePicture( gdi_display, physdev_src->x11dev->drawable,
1984 pict_formats[format], CPSubwindowMode, &pa );
1988 if (tmp_pict) src_pict = tmp_pict;
1990 EnterCriticalSection( &xrender_cs );
1991 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
1993 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1994 physdev_src->x11dev->dc_rect.left + src->x,
1995 physdev_src->x11dev->dc_rect.top + src->y,
1996 src->width, src->height,
1997 physdev_dst->x11dev->dc_rect.left + dst->x,
1998 physdev_dst->x11dev->dc_rect.top + dst->y,
1999 dst->width, dst->height, xscale, yscale );
2001 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2002 if (tmp_pixmap) XFreePixmap( gdi_display, tmp_pixmap );
2004 LeaveCriticalSection( &xrender_cs );
2005 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
2006 return TRUE;
2009 /***********************************************************************
2010 * xrenderdrv_GradientFill
2012 static BOOL xrenderdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
2013 void * grad_array, ULONG ngrad, ULONG mode )
2015 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
2016 static const XFixed stops[2] = { 0, 1 << 16 };
2017 struct xrender_physdev *physdev = get_xrender_dev( dev );
2018 XLinearGradient gradient;
2019 XRenderColor colors[2];
2020 Picture src_pict, dst_pict;
2021 unsigned int i;
2022 const GRADIENT_RECT *rect = grad_array;
2023 RECT rc;
2024 POINT pt[2];
2026 if (!pXRenderCreateLinearGradient) goto fallback;
2028 /* <= 16-bpp uses dithering */
2029 if (!physdev->pict_format || physdev->pict_format->depth <= 16) goto fallback;
2031 switch (mode)
2033 case GRADIENT_FILL_RECT_H:
2034 case GRADIENT_FILL_RECT_V:
2035 for (i = 0; i < ngrad; i++, rect++)
2037 const TRIVERTEX *v1 = vert_array + rect->UpperLeft;
2038 const TRIVERTEX *v2 = vert_array + rect->LowerRight;
2040 colors[0].red = v1->Red * 257 / 256;
2041 colors[0].green = v1->Green * 257 / 256;
2042 colors[0].blue = v1->Blue * 257 / 256;
2043 colors[1].red = v2->Red * 257 / 256;
2044 colors[1].green = v2->Green * 257 / 256;
2045 colors[1].blue = v2->Blue * 257 / 256;
2046 /* always ignore alpha since otherwise xrender will want to pre-multiply the colors */
2047 colors[0].alpha = colors[1].alpha = 65535;
2049 pt[0].x = v1->x;
2050 pt[0].y = v1->y;
2051 pt[1].x = v2->x;
2052 pt[1].y = v2->y;
2053 LPtoDP( dev->hdc, pt, 2 );
2054 if (mode == GRADIENT_FILL_RECT_H)
2056 gradient.p1.y = gradient.p2.y = 0;
2057 if (pt[1].x > pt[0].x)
2059 gradient.p1.x = 0;
2060 gradient.p2.x = (pt[1].x - pt[0].x) << 16;
2062 else
2064 gradient.p1.x = (pt[0].x - pt[1].x) << 16;
2065 gradient.p2.x = 0;
2068 else
2070 gradient.p1.x = gradient.p2.x = 0;
2071 if (pt[1].y > pt[0].y)
2073 gradient.p1.y = 0;
2074 gradient.p2.y = (pt[1].y - pt[0].y) << 16;
2076 else
2078 gradient.p1.y = (pt[0].y - pt[1].y) << 16;
2079 gradient.p2.y = 0;
2083 rc.left = min( pt[0].x, pt[1].x );
2084 rc.top = min( pt[0].y, pt[1].y );
2085 rc.right = max( pt[0].x, pt[1].x );
2086 rc.bottom = max( pt[0].y, pt[1].y );
2088 TRACE( "%u gradient %s colors %04x,%04x,%04x,%04x -> %04x,%04x,%04x,%04x\n",
2089 mode, wine_dbgstr_rect( &rc ),
2090 colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha,
2091 colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha );
2093 dst_pict = get_xrender_picture( physdev, 0, NULL );
2095 src_pict = pXRenderCreateLinearGradient( gdi_display, &gradient, stops, colors, 2 );
2096 xrender_blit( PictOpSrc, src_pict, 0, dst_pict,
2097 0, 0, rc.right - rc.left, rc.bottom - rc.top,
2098 physdev->x11dev->dc_rect.left + rc.left,
2099 physdev->x11dev->dc_rect.top + rc.top,
2100 rc.right - rc.left, rc.bottom - rc.top, 1, 1 );
2101 pXRenderFreePicture( gdi_display, src_pict );
2102 add_device_bounds( physdev->x11dev, &rc );
2104 return TRUE;
2107 fallback:
2108 #endif
2109 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
2110 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
2113 /***********************************************************************
2114 * xrenderdrv_SelectBrush
2116 static HBRUSH xrenderdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2118 struct xrender_physdev *physdev = get_xrender_dev( dev );
2119 Pixmap pixmap;
2120 XVisualInfo vis = default_visual;
2121 XRenderPictFormat *format = physdev->pict_format;
2123 if (!pattern) goto x11drv_fallback;
2124 if (pattern->info->bmiHeader.biBitCount == 1) goto x11drv_fallback;
2125 if (physdev->format == WXR_FORMAT_MONO) goto x11drv_fallback;
2127 vis.depth = format->depth;
2128 vis.red_mask = format->direct.redMask << format->direct.red;
2129 vis.green_mask = format->direct.greenMask << format->direct.green;
2130 vis.blue_mask = format->direct.blueMask << format->direct.blue;
2132 pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, pattern->info,
2133 &pattern->bits, pattern->usage );
2134 if (!pixmap) return 0;
2136 if (physdev->x11dev->brush.pixmap) XFreePixmap( gdi_display, physdev->x11dev->brush.pixmap );
2137 physdev->x11dev->brush.pixmap = pixmap;
2138 physdev->x11dev->brush.fillStyle = FillTiled;
2139 physdev->x11dev->brush.pixel = 0; /* ignored */
2140 physdev->x11dev->brush.style = BS_PATTERN;
2141 return hbrush;
2143 x11drv_fallback:
2144 dev = GET_NEXT_PHYSDEV( dev, pSelectBrush );
2145 return dev->funcs->pSelectBrush( dev, hbrush, pattern );
2149 static const struct gdi_dc_funcs xrender_funcs =
2151 NULL, /* pAbortDoc */
2152 NULL, /* pAbortPath */
2153 xrenderdrv_AlphaBlend, /* pAlphaBlend */
2154 NULL, /* pAngleArc */
2155 NULL, /* pArc */
2156 NULL, /* pArcTo */
2157 NULL, /* pBeginPath */
2158 xrenderdrv_BlendImage, /* pBlendImage */
2159 NULL, /* pChord */
2160 NULL, /* pCloseFigure */
2161 xrenderdrv_CreateCompatibleDC, /* pCreateCompatibleDC */
2162 xrenderdrv_CreateDC, /* pCreateDC */
2163 xrenderdrv_DeleteDC, /* pDeleteDC */
2164 NULL, /* pDeleteObject */
2165 NULL, /* pDeviceCapabilities */
2166 NULL, /* pEllipse */
2167 NULL, /* pEndDoc */
2168 NULL, /* pEndPage */
2169 NULL, /* pEndPath */
2170 NULL, /* pEnumFonts */
2171 NULL, /* pEnumICMProfiles */
2172 NULL, /* pExcludeClipRect */
2173 NULL, /* pExtDeviceMode */
2174 xrenderdrv_ExtEscape, /* pExtEscape */
2175 NULL, /* pExtFloodFill */
2176 NULL, /* pExtSelectClipRgn */
2177 xrenderdrv_ExtTextOut, /* pExtTextOut */
2178 NULL, /* pFillPath */
2179 NULL, /* pFillRgn */
2180 NULL, /* pFlattenPath */
2181 NULL, /* pFontIsLinked */
2182 NULL, /* pFrameRgn */
2183 NULL, /* pGdiComment */
2184 NULL, /* pGdiRealizationInfo */
2185 NULL, /* pGetBoundsRect */
2186 NULL, /* pGetCharABCWidths */
2187 NULL, /* pGetCharABCWidthsI */
2188 NULL, /* pGetCharWidth */
2189 NULL, /* pGetDeviceCaps */
2190 NULL, /* pGetDeviceGammaRamp */
2191 NULL, /* pGetFontData */
2192 NULL, /* pGetFontUnicodeRanges */
2193 NULL, /* pGetGlyphIndices */
2194 NULL, /* pGetGlyphOutline */
2195 NULL, /* pGetICMProfile */
2196 NULL, /* pGetImage */
2197 NULL, /* pGetKerningPairs */
2198 NULL, /* pGetNearestColor */
2199 NULL, /* pGetOutlineTextMetrics */
2200 NULL, /* pGetPixel */
2201 NULL, /* pGetSystemPaletteEntries */
2202 NULL, /* pGetTextCharsetInfo */
2203 NULL, /* pGetTextExtentExPoint */
2204 NULL, /* pGetTextExtentExPointI */
2205 NULL, /* pGetTextFace */
2206 NULL, /* pGetTextMetrics */
2207 xrenderdrv_GradientFill, /* pGradientFill */
2208 NULL, /* pIntersectClipRect */
2209 NULL, /* pInvertRgn */
2210 NULL, /* pLineTo */
2211 NULL, /* pModifyWorldTransform */
2212 NULL, /* pMoveTo */
2213 NULL, /* pOffsetClipRgn */
2214 NULL, /* pOffsetViewportOrg */
2215 NULL, /* pOffsetWindowOrg */
2216 NULL, /* pPaintRgn */
2217 NULL, /* pPatBlt */
2218 NULL, /* pPie */
2219 NULL, /* pPolyBezier */
2220 NULL, /* pPolyBezierTo */
2221 NULL, /* pPolyDraw */
2222 NULL, /* pPolyPolygon */
2223 NULL, /* pPolyPolyline */
2224 NULL, /* pPolygon */
2225 NULL, /* pPolyline */
2226 NULL, /* pPolylineTo */
2227 xrenderdrv_PutImage, /* pPutImage */
2228 NULL, /* pRealizeDefaultPalette */
2229 NULL, /* pRealizePalette */
2230 NULL, /* pRectangle */
2231 NULL, /* pResetDC */
2232 NULL, /* pRestoreDC */
2233 NULL, /* pRoundRect */
2234 NULL, /* pSaveDC */
2235 NULL, /* pScaleViewportExt */
2236 NULL, /* pScaleWindowExt */
2237 NULL, /* pSelectBitmap */
2238 xrenderdrv_SelectBrush, /* pSelectBrush */
2239 NULL, /* pSelectClipPath */
2240 xrenderdrv_SelectFont, /* pSelectFont */
2241 NULL, /* pSelectPalette */
2242 NULL, /* pSelectPen */
2243 NULL, /* pSetArcDirection */
2244 NULL, /* pSetBkColor */
2245 NULL, /* pSetBkMode */
2246 NULL, /* pSetBoundsRect */
2247 NULL, /* pSetDCBrushColor */
2248 NULL, /* pSetDCPenColor */
2249 NULL, /* pSetDIBitsToDevice */
2250 xrenderdrv_SetDeviceClipping, /* pSetDeviceClipping */
2251 NULL, /* pSetDeviceGammaRamp */
2252 NULL, /* pSetLayout */
2253 NULL, /* pSetMapMode */
2254 NULL, /* pSetMapperFlags */
2255 NULL, /* pSetPixel */
2256 NULL, /* pSetPolyFillMode */
2257 NULL, /* pSetROP2 */
2258 NULL, /* pSetRelAbs */
2259 NULL, /* pSetStretchBltMode */
2260 NULL, /* pSetTextAlign */
2261 NULL, /* pSetTextCharacterExtra */
2262 NULL, /* pSetTextColor */
2263 NULL, /* pSetTextJustification */
2264 NULL, /* pSetViewportExt */
2265 NULL, /* pSetViewportOrg */
2266 NULL, /* pSetWindowExt */
2267 NULL, /* pSetWindowOrg */
2268 NULL, /* pSetWorldTransform */
2269 NULL, /* pStartDoc */
2270 NULL, /* pStartPage */
2271 xrenderdrv_StretchBlt, /* pStretchBlt */
2272 NULL, /* pStretchDIBits */
2273 NULL, /* pStrokeAndFillPath */
2274 NULL, /* pStrokePath */
2275 NULL, /* pUnrealizePalette */
2276 NULL, /* pWidenPath */
2277 NULL, /* wine_get_wgl_driver */
2278 GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */
2281 #else /* SONAME_LIBXRENDER */
2283 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
2285 TRACE("XRender support not compiled in.\n");
2286 return NULL;
2289 #endif /* SONAME_LIBXRENDER */