msi: Don't defer custom actions in the UI sequence if they match the currently runnin...
[wine/multimedia.git] / dlls / winex11.drv / xrender.c
blob671f67aaed48bdf0ddd1e4af49690cb89eeef626
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 const ColorShifts wxr_color_shifts[WXR_NB_FORMATS] =
107 /* format phys red phys green phys blue log red log green log blue */
108 /* WXR_FORMAT_MONO */ { { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 } },
109 /* WXR_FORMAT_GRAY */ { { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 } },
110 /* WXR_FORMAT_X1R5G5B5 */ { {10,5,31}, { 5,5,31}, { 0,5,31}, {10,5,31}, { 5,5,31}, { 0,5,31} },
111 /* WXR_FORMAT_X1B5G5R5 */ { { 0,5,31}, { 5,5,31}, {10,5,31}, { 0,5,31}, { 5,5,31}, {10,5,31} },
112 /* WXR_FORMAT_R5G6B5 */ { {11,5,31}, { 5,6,63}, { 0,5,31}, {11,5,31}, { 5,6,63}, { 0,5,31} },
113 /* WXR_FORMAT_B5G6R5 */ { { 0,5,31}, { 5,6,63}, {11,5,31}, { 0,5,31}, { 5,6,63}, {11,5,31} },
114 /* WXR_FORMAT_R8G8B8 */ { {16,8,255}, { 8,8,255}, { 0,8,255}, {16,8,255}, { 8,8,255}, { 0,8,255} },
115 /* WXR_FORMAT_B8G8R8 */ { { 0,8,255}, { 8,8,255}, {16,8,255}, { 0,8,255}, { 8,8,255}, {16,8,255} },
116 /* WXR_FORMAT_A8R8G8B8 */ { {16,8,255}, { 8,8,255}, { 0,8,255}, {16,8,255}, { 8,8,255}, { 0,8,255} },
117 /* WXR_FORMAT_B8G8R8A8 */ { { 8,8,255}, {16,8,255}, {24,8,255}, { 8,8,255}, {16,8,255}, {24,8,255} },
118 /* WXR_FORMAT_X8R8G8B8 */ { {16,8,255}, { 8,8,255}, { 0,8,255}, {16,8,255}, { 8,8,255}, { 0,8,255} },
119 /* WXR_FORMAT_B8G8R8X8 */ { { 8,8,255}, {16,8,255}, {24,8,255}, { 8,8,255}, {16,8,255}, {24,8,255} },
122 static enum wxr_format default_format = WXR_INVALID_FORMAT;
123 static XRenderPictFormat *pict_formats[WXR_NB_FORMATS + 1 /* invalid format */];
125 typedef struct
127 LOGFONTW lf;
128 XFORM xform;
129 SIZE devsize; /* size in device coords */
130 DWORD hash;
131 } LFANDSIZE;
133 #define INITIAL_REALIZED_BUF_SIZE 128
135 enum glyph_type { GLYPH_INDEX, GLYPH_WCHAR, GLYPH_NBTYPES };
137 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
139 typedef struct
141 GlyphSet glyphset;
142 XRenderPictFormat *font_format;
143 int nrealized;
144 BOOL *realized;
145 XGlyphInfo *gis;
146 } gsCacheEntryFormat;
148 typedef struct
150 LFANDSIZE lfsz;
151 gsCacheEntryFormat *format[GLYPH_NBTYPES][AA_MAXVALUE];
152 INT count;
153 INT next;
154 } gsCacheEntry;
156 struct xrender_physdev
158 struct gdi_physdev dev;
159 X11DRV_PDEVICE *x11dev;
160 HRGN region;
161 enum wxr_format format;
162 UINT aa_flags;
163 int cache_index;
164 BOOL update_clip;
165 Picture pict;
166 Picture pict_src;
167 XRenderPictFormat *pict_format;
170 static inline struct xrender_physdev *get_xrender_dev( PHYSDEV dev )
172 return (struct xrender_physdev *)dev;
175 static const struct gdi_dc_funcs xrender_funcs;
177 static gsCacheEntry *glyphsetCache = NULL;
178 static DWORD glyphsetCacheSize = 0;
179 static INT lastfree = -1;
180 static INT mru = -1;
182 #define INIT_CACHE_SIZE 10
184 static void *xrender_handle;
186 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
187 MAKE_FUNCPTR(XRenderAddGlyphs)
188 MAKE_FUNCPTR(XRenderChangePicture)
189 MAKE_FUNCPTR(XRenderComposite)
190 MAKE_FUNCPTR(XRenderCompositeText16)
191 MAKE_FUNCPTR(XRenderCreateGlyphSet)
192 MAKE_FUNCPTR(XRenderCreatePicture)
193 MAKE_FUNCPTR(XRenderFillRectangle)
194 MAKE_FUNCPTR(XRenderFindFormat)
195 MAKE_FUNCPTR(XRenderFindVisualFormat)
196 MAKE_FUNCPTR(XRenderFreeGlyphSet)
197 MAKE_FUNCPTR(XRenderFreePicture)
198 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
199 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
200 MAKE_FUNCPTR(XRenderCreateLinearGradient)
201 #endif
202 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
203 MAKE_FUNCPTR(XRenderSetPictureTransform)
204 #endif
205 MAKE_FUNCPTR(XRenderQueryExtension)
207 #undef MAKE_FUNCPTR
209 static CRITICAL_SECTION xrender_cs;
210 static CRITICAL_SECTION_DEBUG critsect_debug =
212 0, 0, &xrender_cs,
213 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
214 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
216 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
218 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
219 ( ( (ULONG)_x4 << 24 ) | \
220 ( (ULONG)_x3 << 16 ) | \
221 ( (ULONG)_x2 << 8 ) | \
222 (ULONG)_x1 )
224 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
226 #define GASP_GRIDFIT 0x01
227 #define GASP_DOGRAY 0x02
229 #ifdef WORDS_BIGENDIAN
230 #define get_be_word(x) (x)
231 #define NATIVE_BYTE_ORDER MSBFirst
232 #else
233 #define get_be_word(x) RtlUshortByteSwap(x)
234 #define NATIVE_BYTE_ORDER LSBFirst
235 #endif
237 static BOOL has_alpha( enum wxr_format format )
239 return (format == WXR_FORMAT_A8R8G8B8 || format == WXR_FORMAT_B8G8R8A8);
242 static enum wxr_format get_format_without_alpha( enum wxr_format format )
244 switch (format)
246 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
247 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
248 default: return format;
252 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
254 templ->id = 0;
255 templ->type = PictTypeDirect;
256 templ->depth = fmt->depth;
257 templ->direct.alpha = fmt->alpha;
258 templ->direct.alphaMask = fmt->alphaMask;
259 templ->direct.red = fmt->red;
260 templ->direct.redMask = fmt->redMask;
261 templ->direct.green = fmt->green;
262 templ->direct.greenMask = fmt->greenMask;
263 templ->direct.blue = fmt->blue;
264 templ->direct.blueMask = fmt->blueMask;
265 templ->colormap = 0;
267 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
269 return TRUE;
272 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
274 if(fmt->depth != default_visual.depth) return FALSE;
275 if( (fmt->redMask << fmt->red) != default_visual.red_mask) return FALSE;
276 if( (fmt->greenMask << fmt->green) != default_visual.green_mask) return FALSE;
277 if( (fmt->blueMask << fmt->blue) != default_visual.blue_mask) return FALSE;
279 /* We never select a default ARGB visual */
280 if(fmt->alphaMask) return FALSE;
281 return TRUE;
284 static int load_xrender_formats(void)
286 int count = 0;
287 unsigned int i;
289 for (i = 0; i < WXR_NB_FORMATS; i++)
291 XRenderPictFormat templ;
293 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
295 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, default_visual.visual);
296 if (!pict_formats[i])
298 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
299 if (default_visual.class == DirectColor)
301 XVisualInfo info;
302 if (XMatchVisualInfo( gdi_display, default_visual.screen,
303 default_visual.depth, TrueColor, &info ))
305 pict_formats[i] = pXRenderFindVisualFormat(gdi_display, info.visual);
306 if (pict_formats[i]) default_visual = info;
310 if (pict_formats[i]) default_format = i;
312 else
314 unsigned long mask = 0;
315 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
316 pict_formats[i] = pXRenderFindFormat(gdi_display, mask, &templ, 0);
318 if (pict_formats[i])
320 count++;
321 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_formats[i]->id, i);
324 return count;
327 /***********************************************************************
328 * X11DRV_XRender_Init
330 * Let's see if our XServer has the extension available
333 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
335 int event_base, i;
337 if (!client_side_with_render) return NULL;
338 if (!(xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0))) return NULL;
340 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) return NULL
341 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0)
342 LOAD_FUNCPTR(XRenderAddGlyphs);
343 LOAD_FUNCPTR(XRenderChangePicture);
344 LOAD_FUNCPTR(XRenderComposite);
345 LOAD_FUNCPTR(XRenderCompositeText16);
346 LOAD_FUNCPTR(XRenderCreateGlyphSet);
347 LOAD_FUNCPTR(XRenderCreatePicture);
348 LOAD_FUNCPTR(XRenderFillRectangle);
349 LOAD_FUNCPTR(XRenderFindFormat);
350 LOAD_FUNCPTR(XRenderFindVisualFormat);
351 LOAD_FUNCPTR(XRenderFreeGlyphSet);
352 LOAD_FUNCPTR(XRenderFreePicture);
353 LOAD_FUNCPTR(XRenderSetPictureClipRectangles);
354 LOAD_FUNCPTR(XRenderQueryExtension);
355 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
356 LOAD_OPTIONAL_FUNCPTR(XRenderCreateLinearGradient);
357 #endif
358 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
359 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform);
360 #endif
361 #undef LOAD_OPTIONAL_FUNCPTR
362 #undef LOAD_FUNCPTR
364 if (!pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base)) return NULL;
366 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
367 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
369 ERR_(winediag)("Wine has detected that you probably have a buggy version "
370 "of libXrender. Because of this client side font rendering "
371 "will be disabled. Please upgrade this library.\n");
372 return NULL;
375 if (!default_visual.red_mask || !default_visual.green_mask || !default_visual.blue_mask)
377 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
378 return NULL;
381 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
382 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
384 glyphsetCacheSize = INIT_CACHE_SIZE;
385 lastfree = 0;
386 for(i = 0; i < INIT_CACHE_SIZE; i++) {
387 glyphsetCache[i].next = i + 1;
388 glyphsetCache[i].count = -1;
390 glyphsetCache[i-1].next = -1;
392 return &xrender_funcs;
395 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
396 static void get_xrender_color( struct xrender_physdev *physdev, COLORREF src_color, XRenderColor *dst_color )
398 if (src_color & (1 << 24)) /* PALETTEINDEX */
400 HPALETTE pal = GetCurrentObject( physdev->dev.hdc, OBJ_PAL );
401 PALETTEENTRY pal_ent;
403 if (!GetPaletteEntries( pal, LOWORD(src_color), 1, &pal_ent ))
404 GetPaletteEntries( pal, 0, 1, &pal_ent );
405 dst_color->red = pal_ent.peRed * 257;
406 dst_color->green = pal_ent.peGreen * 257;
407 dst_color->blue = pal_ent.peBlue * 257;
409 else
411 if (src_color >> 16 == 0x10ff) src_color = 0; /* DIBINDEX */
413 dst_color->red = GetRValue( src_color ) * 257;
414 dst_color->green = GetGValue( src_color ) * 257;
415 dst_color->blue = GetBValue( src_color ) * 257;
418 if (physdev->format == WXR_FORMAT_MONO && !dst_color->red && !dst_color->green && !dst_color->blue)
419 dst_color->alpha = 0;
420 else
421 dst_color->alpha = 0xffff;
424 static enum wxr_format get_xrender_format_from_bitmapinfo( const BITMAPINFO *info )
426 if (info->bmiHeader.biPlanes != 1) return WXR_INVALID_FORMAT;
428 switch (info->bmiHeader.biBitCount)
430 case 1:
431 return WXR_FORMAT_MONO;
432 case 4:
433 case 8:
434 break;
435 case 24:
436 if (info->bmiHeader.biCompression != BI_RGB) break;
437 return WXR_FORMAT_R8G8B8;
438 case 16:
439 case 32:
440 if (info->bmiHeader.biCompression == BI_BITFIELDS)
442 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
443 unsigned int i;
445 for (i = 0; i < WXR_NB_FORMATS; i++)
447 if (info->bmiHeader.biBitCount == wxr_formats_template[i].depth &&
448 colors[0] == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
449 colors[1] == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
450 colors[2] == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue))
451 return i;
453 break;
455 if (info->bmiHeader.biCompression != BI_RGB) break;
456 return (info->bmiHeader.biBitCount == 16) ? WXR_FORMAT_X1R5G5B5 : WXR_FORMAT_A8R8G8B8;
458 return WXR_INVALID_FORMAT;
461 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
462 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
464 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
465 XTransform xform = {{
466 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
467 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
468 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
471 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
472 #endif
475 static void update_xrender_clipping( struct xrender_physdev *dev, HRGN rgn )
477 XRenderPictureAttributes pa;
478 RGNDATA *data;
480 if (!rgn)
482 pa.clip_mask = None;
483 pXRenderChangePicture( gdi_display, dev->pict, CPClipMask, &pa );
485 else if ((data = X11DRV_GetRegionData( rgn, 0 )))
487 pXRenderSetPictureClipRectangles( gdi_display, dev->pict,
488 dev->x11dev->dc_rect.left, dev->x11dev->dc_rect.top,
489 (XRectangle *)data->Buffer, data->rdh.nCount );
490 HeapFree( GetProcessHeap(), 0, data );
495 static Picture get_xrender_picture( struct xrender_physdev *dev, HRGN clip_rgn, const RECT *clip_rect )
497 if (!dev->pict && dev->pict_format)
499 XRenderPictureAttributes pa;
501 pa.subwindow_mode = IncludeInferiors;
502 dev->pict = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
503 dev->pict_format, CPSubwindowMode, &pa );
504 TRACE( "Allocing pict=%lx dc=%p drawable=%08lx\n",
505 dev->pict, dev->dev.hdc, dev->x11dev->drawable );
506 dev->update_clip = (dev->region != 0);
509 if (clip_rect)
511 HRGN rgn = CreateRectRgnIndirect( clip_rect );
512 if (clip_rgn) CombineRgn( rgn, rgn, clip_rgn, RGN_AND );
513 if (dev->region) CombineRgn( rgn, rgn, dev->region, RGN_AND );
514 update_xrender_clipping( dev, rgn );
515 DeleteObject( rgn );
517 else if (clip_rgn)
519 if (dev->region)
521 HRGN rgn = CreateRectRgn( 0, 0, 0, 0 );
522 CombineRgn( rgn, clip_rgn, dev->region, RGN_AND );
523 update_xrender_clipping( dev, rgn );
524 DeleteObject( rgn );
526 else update_xrender_clipping( dev, clip_rgn );
528 else if (dev->update_clip) update_xrender_clipping( dev, dev->region );
530 dev->update_clip = (clip_rect || clip_rgn); /* have to update again if we are using a custom region */
531 return dev->pict;
534 static Picture get_xrender_picture_source( struct xrender_physdev *dev, BOOL repeat )
536 if (!dev->pict_src && dev->pict_format)
538 XRenderPictureAttributes pa;
540 pa.subwindow_mode = IncludeInferiors;
541 pa.repeat = repeat ? RepeatNormal : RepeatNone;
542 dev->pict_src = pXRenderCreatePicture( gdi_display, dev->x11dev->drawable,
543 dev->pict_format, CPSubwindowMode|CPRepeat, &pa );
545 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
546 dev->pict_src, dev->dev.hdc, dev->x11dev->drawable, pa.repeat);
549 return dev->pict_src;
552 static void free_xrender_picture( struct xrender_physdev *dev )
554 if (dev->pict || dev->pict_src)
556 XFlush( gdi_display );
557 if (dev->pict)
559 TRACE("freeing pict = %lx dc = %p\n", dev->pict, dev->dev.hdc);
560 pXRenderFreePicture(gdi_display, dev->pict);
561 dev->pict = 0;
563 if(dev->pict_src)
565 TRACE("freeing pict = %lx dc = %p\n", dev->pict_src, dev->dev.hdc);
566 pXRenderFreePicture(gdi_display, dev->pict_src);
567 dev->pict_src = 0;
572 /* return a mask picture used to force alpha to 0 */
573 static Picture get_no_alpha_mask(void)
575 static Pixmap pixmap;
576 static Picture pict;
578 EnterCriticalSection( &xrender_cs );
579 if (!pict)
581 XRenderPictureAttributes pa;
582 XRenderColor col;
584 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
585 pa.repeat = RepeatNormal;
586 pa.component_alpha = True;
587 pict = pXRenderCreatePicture( gdi_display, pixmap, pict_formats[WXR_FORMAT_A8R8G8B8],
588 CPRepeat|CPComponentAlpha, &pa );
589 col.red = col.green = col.blue = 0xffff;
590 col.alpha = 0;
591 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
593 LeaveCriticalSection( &xrender_cs );
594 return pict;
597 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
599 if(p1->hash != p2->hash) return TRUE;
600 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
601 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
602 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
603 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
606 #if 0
607 static void walk_cache(void)
609 int i;
611 EnterCriticalSection(&xrender_cs);
612 for(i=mru; i >= 0; i = glyphsetCache[i].next)
613 TRACE("item %d\n", i);
614 LeaveCriticalSection(&xrender_cs);
616 #endif
618 static int LookupEntry(LFANDSIZE *plfsz)
620 int i, prev_i = -1;
622 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
623 TRACE("%d\n", i);
624 if(glyphsetCache[i].count == -1) break; /* reached free list so stop */
626 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
627 glyphsetCache[i].count++;
628 if(prev_i >= 0) {
629 glyphsetCache[prev_i].next = glyphsetCache[i].next;
630 glyphsetCache[i].next = mru;
631 mru = i;
633 TRACE("found font in cache %d\n", i);
634 return i;
636 prev_i = i;
638 TRACE("font not in cache\n");
639 return -1;
642 static void FreeEntry(int entry)
644 int type, format;
646 for (type = 0; type < GLYPH_NBTYPES; type++)
648 for(format = 0; format < AA_MAXVALUE; format++) {
649 gsCacheEntryFormat * formatEntry;
651 if( !glyphsetCache[entry].format[type][format] )
652 continue;
654 formatEntry = glyphsetCache[entry].format[type][format];
656 if(formatEntry->glyphset) {
657 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
658 formatEntry->glyphset = 0;
660 if(formatEntry->nrealized) {
661 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
662 formatEntry->realized = NULL;
663 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
664 formatEntry->gis = NULL;
665 formatEntry->nrealized = 0;
668 HeapFree(GetProcessHeap(), 0, formatEntry);
669 glyphsetCache[entry].format[type][format] = NULL;
674 static int AllocEntry(void)
676 int best = -1, prev_best = -1, i, prev_i = -1;
678 if(lastfree >= 0) {
679 assert(glyphsetCache[lastfree].count == -1);
680 glyphsetCache[lastfree].count = 1;
681 best = lastfree;
682 lastfree = glyphsetCache[lastfree].next;
683 assert(best != mru);
684 glyphsetCache[best].next = mru;
685 mru = best;
687 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
688 return mru;
691 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
692 if(glyphsetCache[i].count == 0) {
693 best = i;
694 prev_best = prev_i;
696 prev_i = i;
699 if(best >= 0) {
700 TRACE("freeing unused glyphset at cache %d\n", best);
701 FreeEntry(best);
702 glyphsetCache[best].count = 1;
703 if(prev_best >= 0) {
704 glyphsetCache[prev_best].next = glyphsetCache[best].next;
705 glyphsetCache[best].next = mru;
706 mru = best;
707 } else {
708 assert(mru == best);
710 return mru;
713 TRACE("Growing cache\n");
715 if (glyphsetCache)
716 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
717 glyphsetCache,
718 (glyphsetCacheSize + INIT_CACHE_SIZE)
719 * sizeof(*glyphsetCache));
720 else
721 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
722 (glyphsetCacheSize + INIT_CACHE_SIZE)
723 * sizeof(*glyphsetCache));
725 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
726 i++) {
727 glyphsetCache[i].next = i + 1;
728 glyphsetCache[i].count = -1;
730 glyphsetCache[i-1].next = -1;
731 glyphsetCacheSize += INIT_CACHE_SIZE;
733 lastfree = glyphsetCache[best].next;
734 glyphsetCache[best].count = 1;
735 glyphsetCache[best].next = mru;
736 mru = best;
737 TRACE("new free cache slot at %d\n", mru);
738 return mru;
741 static int GetCacheEntry( LFANDSIZE *plfsz )
743 int ret;
744 gsCacheEntry *entry;
746 if((ret = LookupEntry(plfsz)) != -1) return ret;
748 ret = AllocEntry();
749 entry = glyphsetCache + ret;
750 entry->lfsz = *plfsz;
751 return ret;
754 static void dec_ref_cache(int index)
756 assert(index >= 0);
757 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
758 assert(glyphsetCache[index].count > 0);
759 glyphsetCache[index].count--;
762 static void lfsz_calc_hash(LFANDSIZE *plfsz)
764 DWORD hash = 0, *ptr, two_chars;
765 WORD *pwc;
766 unsigned int i;
768 hash ^= plfsz->devsize.cx;
769 hash ^= plfsz->devsize.cy;
770 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
771 hash ^= *ptr;
772 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
773 hash ^= *ptr;
774 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
775 two_chars = *ptr;
776 pwc = (WCHAR *)&two_chars;
777 if(!*pwc) break;
778 *pwc = toupperW(*pwc);
779 pwc++;
780 *pwc = toupperW(*pwc);
781 hash ^= two_chars;
782 if(!*pwc) break;
784 plfsz->hash = hash;
785 return;
788 static AA_Type aa_type_from_flags( UINT aa_flags )
790 switch (aa_flags & 0x7f)
792 case GGO_BITMAP:
793 return AA_None;
794 case WINE_GGO_GRAY16_BITMAP:
795 return AA_Grey;
796 case WINE_GGO_HRGB_BITMAP:
797 return AA_RGB;
798 case WINE_GGO_HBGR_BITMAP:
799 return AA_BGR;
800 case WINE_GGO_VRGB_BITMAP:
801 return AA_VRGB;
802 case WINE_GGO_VBGR_BITMAP:
803 return AA_VBGR;
804 default:
805 FIXME( "unknown flags %x\n", aa_flags );
806 return AA_None;
810 static UINT get_xft_aa_flags( const LOGFONTW *lf )
812 char *value;
813 UINT ret = 0;
815 switch (lf->lfQuality)
817 case NONANTIALIASED_QUALITY:
818 case ANTIALIASED_QUALITY:
819 break;
820 default:
821 if (!(value = XGetDefault( gdi_display, "Xft", "antialias" ))) break;
822 TRACE( "got antialias '%s'\n", value );
823 if (tolower(value[0]) == 'f' || tolower(value[0]) == 'n' ||
824 value[0] == '0' || !strcasecmp( value, "off" ))
826 ret = GGO_BITMAP;
827 break;
829 ret = GGO_GRAY4_BITMAP;
830 /* fall through */
831 case CLEARTYPE_QUALITY:
832 case CLEARTYPE_NATURAL_QUALITY:
833 if (!(value = XGetDefault( gdi_display, "Xft", "rgba" ))) break;
834 TRACE( "got rgba '%s'\n", value );
835 if (!strcmp( value, "rgb" )) ret = WINE_GGO_HRGB_BITMAP;
836 else if (!strcmp( value, "bgr" )) ret = WINE_GGO_HBGR_BITMAP;
837 else if (!strcmp( value, "vrgb" )) ret = WINE_GGO_VRGB_BITMAP;
838 else if (!strcmp( value, "vbgr" )) ret = WINE_GGO_VBGR_BITMAP;
839 else if (!strcmp( value, "none" )) ret = GGO_GRAY4_BITMAP;
840 break;
842 return ret;
845 /**********************************************************************
846 * xrenderdrv_SelectFont
848 static HFONT xrenderdrv_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
850 LFANDSIZE lfsz;
851 struct xrender_physdev *physdev = get_xrender_dev( dev );
852 PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
853 HFONT ret;
855 GetObjectW( hfont, sizeof(lfsz.lf), &lfsz.lf );
856 if (!*aa_flags) *aa_flags = get_xft_aa_flags( &lfsz.lf );
858 ret = next->funcs->pSelectFont( next, hfont, aa_flags );
859 if (!ret) return 0;
861 switch (*aa_flags)
863 case GGO_GRAY2_BITMAP:
864 case GGO_GRAY4_BITMAP:
865 case GGO_GRAY8_BITMAP:
866 physdev->aa_flags = WINE_GGO_GRAY16_BITMAP;
867 break;
868 case 0:
869 physdev->aa_flags = GGO_BITMAP;
870 break;
871 default:
872 physdev->aa_flags = *aa_flags;
873 break;
876 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
877 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
878 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
879 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
880 lfsz.devsize.cx = X11DRV_XWStoDS( dev->hdc, lfsz.lf.lfWidth );
881 lfsz.devsize.cy = X11DRV_YWStoDS( dev->hdc, lfsz.lf.lfHeight );
883 GetTransform( dev->hdc, 0x204, &lfsz.xform );
884 TRACE("font transform %f %f %f %f\n", lfsz.xform.eM11, lfsz.xform.eM12,
885 lfsz.xform.eM21, lfsz.xform.eM22);
887 if (GetGraphicsMode( dev->hdc ) == GM_COMPATIBLE)
889 lfsz.lf.lfOrientation = lfsz.lf.lfEscapement;
890 if (lfsz.xform.eM11 * lfsz.xform.eM22 < 0)
891 lfsz.lf.lfOrientation = -lfsz.lf.lfOrientation;
894 /* Not used fields, would break hashing */
895 lfsz.xform.eDx = lfsz.xform.eDy = 0;
897 lfsz_calc_hash(&lfsz);
899 EnterCriticalSection(&xrender_cs);
900 if (physdev->cache_index != -1)
901 dec_ref_cache( physdev->cache_index );
902 physdev->cache_index = GetCacheEntry( &lfsz );
903 LeaveCriticalSection(&xrender_cs);
904 return ret;
907 static BOOL create_xrender_dc( PHYSDEV *pdev, enum wxr_format format )
909 X11DRV_PDEVICE *x11dev = get_x11drv_dev( *pdev );
910 struct xrender_physdev *physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) );
912 if (!physdev) return FALSE;
913 physdev->x11dev = x11dev;
914 physdev->cache_index = -1;
915 physdev->format = format;
916 physdev->pict_format = pict_formats[format];
917 push_dc_driver( pdev, &physdev->dev, &xrender_funcs );
918 return TRUE;
921 /* store the color mask data in the bitmap info structure */
922 static void set_color_info( XRenderPictFormat *format, BITMAPINFO *info )
924 DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize);
926 info->bmiHeader.biPlanes = 1;
927 info->bmiHeader.biBitCount = pixmap_formats[format->depth]->bits_per_pixel;
928 info->bmiHeader.biCompression = BI_RGB;
929 info->bmiHeader.biClrUsed = 0;
931 switch (info->bmiHeader.biBitCount)
933 case 16:
934 colors[0] = format->direct.redMask << format->direct.red;
935 colors[1] = format->direct.greenMask << format->direct.green;
936 colors[2] = format->direct.blueMask << format->direct.blue;
937 info->bmiHeader.biCompression = BI_BITFIELDS;
938 break;
939 case 32:
940 colors[0] = format->direct.redMask << format->direct.red;
941 colors[1] = format->direct.greenMask << format->direct.green;
942 colors[2] = format->direct.blueMask << format->direct.blue;
943 if (colors[0] != 0xff0000 || colors[1] != 0x00ff00 || colors[2] != 0x0000ff)
944 info->bmiHeader.biCompression = BI_BITFIELDS;
945 break;
950 /**********************************************************************
951 * xrenderdrv_CreateDC
953 static BOOL xrenderdrv_CreateDC( PHYSDEV *pdev, LPCWSTR driver, LPCWSTR device,
954 LPCWSTR output, const DEVMODEW* initData )
956 return create_xrender_dc( pdev, default_format );
959 /**********************************************************************
960 * xrenderdrv_CreateCompatibleDC
962 static BOOL xrenderdrv_CreateCompatibleDC( PHYSDEV orig, PHYSDEV *pdev )
964 if (orig) /* chain to x11drv first */
966 orig = GET_NEXT_PHYSDEV( orig, pCreateCompatibleDC );
967 if (!orig->funcs->pCreateCompatibleDC( orig, pdev )) return FALSE;
969 /* otherwise we have been called by x11drv */
971 return create_xrender_dc( pdev, WXR_FORMAT_MONO );
974 /**********************************************************************
975 * xrenderdrv_DeleteDC
977 static BOOL xrenderdrv_DeleteDC( PHYSDEV dev )
979 struct xrender_physdev *physdev = get_xrender_dev( dev );
981 free_xrender_picture( physdev );
983 EnterCriticalSection( &xrender_cs );
984 if (physdev->cache_index != -1) dec_ref_cache( physdev->cache_index );
985 LeaveCriticalSection( &xrender_cs );
987 HeapFree( GetProcessHeap(), 0, physdev );
988 return TRUE;
991 /**********************************************************************
992 * xrenderdrv_ExtEscape
994 static INT xrenderdrv_ExtEscape( PHYSDEV dev, INT escape, INT in_count, LPCVOID in_data,
995 INT out_count, LPVOID out_data )
997 struct xrender_physdev *physdev = get_xrender_dev( dev );
999 dev = GET_NEXT_PHYSDEV( dev, pExtEscape );
1001 if (escape == X11DRV_ESCAPE && in_data && in_count >= sizeof(enum x11drv_escape_codes))
1003 if (*(const enum x11drv_escape_codes *)in_data == X11DRV_SET_DRAWABLE)
1005 BOOL ret = dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
1006 if (ret) free_xrender_picture( physdev ); /* pict format doesn't change, only drawable */
1007 return ret;
1010 return dev->funcs->pExtEscape( dev, escape, in_count, in_data, out_count, out_data );
1013 /***********************************************************************
1014 * xrenderdrv_SetDeviceClipping
1016 static void xrenderdrv_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
1018 struct xrender_physdev *physdev = get_xrender_dev( dev );
1020 physdev->region = rgn;
1021 physdev->update_clip = TRUE;
1023 dev = GET_NEXT_PHYSDEV( dev, pSetDeviceClipping );
1024 dev->funcs->pSetDeviceClipping( dev, rgn );
1028 /************************************************************************
1029 * UploadGlyph
1031 * Helper to ExtTextOut. Must be called inside xrender_cs
1033 static void UploadGlyph(struct xrender_physdev *physDev, UINT glyph, enum glyph_type type)
1035 unsigned int buflen;
1036 char *buf;
1037 Glyph gid;
1038 GLYPHMETRICS gm;
1039 XGlyphInfo gi;
1040 gsCacheEntry *entry = glyphsetCache + physDev->cache_index;
1041 gsCacheEntryFormat *formatEntry;
1042 UINT ggo_format = physDev->aa_flags;
1043 AA_Type format = aa_type_from_flags( physDev->aa_flags );
1044 enum wxr_format wxr_format;
1045 static const char zero[4];
1046 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1048 if (type == GLYPH_INDEX) ggo_format |= GGO_GLYPH_INDEX;
1049 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1050 if(buflen == GDI_ERROR) {
1051 if(format != AA_None) {
1052 format = AA_None;
1053 physDev->aa_flags = GGO_BITMAP;
1054 ggo_format = (ggo_format & GGO_GLYPH_INDEX) | GGO_BITMAP;
1055 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1057 if(buflen == GDI_ERROR) {
1058 WARN("GetGlyphOutlineW failed using default glyph\n");
1059 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0, ggo_format, &gm, 0, NULL, &identity);
1060 if(buflen == GDI_ERROR) {
1061 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1062 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0x20, ggo_format, &gm, 0, NULL, &identity);
1063 if(buflen == GDI_ERROR) {
1064 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1065 return;
1069 TRACE("Turning off antialiasing for this monochrome font\n");
1072 /* If there is nothing for the current type, we create the entry. */
1073 if( !entry->format[type][format] ) {
1074 entry->format[type][format] = HeapAlloc(GetProcessHeap(),
1075 HEAP_ZERO_MEMORY,
1076 sizeof(gsCacheEntryFormat));
1078 formatEntry = entry->format[type][format];
1080 if(formatEntry->nrealized <= glyph) {
1081 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1083 if (formatEntry->realized)
1084 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1085 HEAP_ZERO_MEMORY,
1086 formatEntry->realized,
1087 formatEntry->nrealized * sizeof(BOOL));
1088 else
1089 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1090 HEAP_ZERO_MEMORY,
1091 formatEntry->nrealized * sizeof(BOOL));
1093 if (formatEntry->gis)
1094 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1095 HEAP_ZERO_MEMORY,
1096 formatEntry->gis,
1097 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1098 else
1099 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1100 HEAP_ZERO_MEMORY,
1101 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1105 if(formatEntry->glyphset == 0) {
1106 switch(format) {
1107 case AA_Grey:
1108 wxr_format = WXR_FORMAT_GRAY;
1109 break;
1111 case AA_RGB:
1112 case AA_BGR:
1113 case AA_VRGB:
1114 case AA_VBGR:
1115 wxr_format = WXR_FORMAT_A8R8G8B8;
1116 break;
1118 default:
1119 ERR("aa = %d - not implemented\n", format);
1120 /* fall through */
1121 case AA_None:
1122 wxr_format = WXR_FORMAT_MONO;
1123 break;
1126 formatEntry->font_format = pict_formats[wxr_format];
1127 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format);
1131 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1132 GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1133 formatEntry->realized[glyph] = TRUE;
1135 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1136 buflen,
1137 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1138 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1140 gi.width = gm.gmBlackBoxX;
1141 gi.height = gm.gmBlackBoxY;
1142 gi.x = -gm.gmptGlyphOrigin.x;
1143 gi.y = gm.gmptGlyphOrigin.y;
1144 gi.xOff = gm.gmCellIncX;
1145 gi.yOff = gm.gmCellIncY;
1147 if(TRACE_ON(xrender)) {
1148 int pitch, i, j;
1149 char output[300];
1150 unsigned char *line;
1152 if(format == AA_None) {
1153 pitch = ((gi.width + 31) / 32) * 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 * 8; j++) {
1158 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1160 TRACE("%s\n", output);
1162 } else {
1163 static const char blks[] = " .:;!o*#";
1164 char str[2];
1166 str[1] = '\0';
1167 pitch = ((gi.width + 3) / 4) * 4;
1168 for(i = 0; i < gi.height; i++) {
1169 line = (unsigned char*) buf + i * pitch;
1170 output[0] = '\0';
1171 for(j = 0; j < pitch; j++) {
1172 str[0] = blks[line[j] >> 5];
1173 strcat(output, str);
1175 TRACE("%s\n", output);
1181 if(formatEntry->glyphset) {
1182 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1183 unsigned char *byte = (unsigned char*) buf, c;
1184 int i = buflen;
1186 while(i--) {
1187 c = *byte;
1189 /* magic to flip bit order */
1190 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1191 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1192 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1194 *byte++ = c;
1197 else if ( format != AA_Grey &&
1198 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1200 unsigned int i, *data = (unsigned int *)buf;
1201 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1203 gid = glyph;
1206 XRenderCompositeText seems to ignore 0x0 glyphs when
1207 AA_None, which means we lose the advance width of glyphs
1208 like the space. We'll pretend that such glyphs are 1x1
1209 bitmaps.
1212 if(buflen == 0)
1213 gi.width = gi.height = 1;
1215 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1216 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1219 HeapFree(GetProcessHeap(), 0, buf);
1220 formatEntry->gis[glyph] = gi;
1223 /*************************************************************
1224 * get_tile_pict
1226 * Returns an appropriate Picture for tiling the text colour.
1227 * Call and use result within the xrender_cs
1229 static Picture get_tile_pict( enum wxr_format wxr_format, const XRenderColor *color)
1231 static struct
1233 Pixmap xpm;
1234 Picture pict;
1235 XRenderColor current_color;
1236 } tiles[WXR_NB_FORMATS], *tile;
1238 tile = &tiles[wxr_format];
1240 if(!tile->xpm)
1242 XRenderPictureAttributes pa;
1243 XRenderPictFormat *pict_format = pict_formats[wxr_format];
1245 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_format->depth);
1247 pa.repeat = RepeatNormal;
1248 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_format, CPRepeat, &pa);
1250 /* init current_color to something different from text_pixel */
1251 tile->current_color = *color;
1252 tile->current_color.red ^= 0xffff;
1254 if (wxr_format == WXR_FORMAT_MONO)
1256 /* for a 1bpp bitmap we always need a 1 in the tile */
1257 XRenderColor col;
1258 col.red = col.green = col.blue = 0;
1259 col.alpha = 0xffff;
1260 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1264 if (memcmp( color, &tile->current_color, sizeof(*color) ) && wxr_format != WXR_FORMAT_MONO)
1266 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, color, 0, 0, 1, 1);
1267 tile->current_color = *color;
1269 return tile->pict;
1272 /*************************************************************
1273 * get_mask_pict
1275 * Returns an appropriate Picture for masking with the specified alpha.
1276 * Call and use result within the xrender_cs
1278 static Picture get_mask_pict( int alpha )
1280 static Pixmap pixmap;
1281 static Picture pict;
1282 static int current_alpha;
1284 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1286 if (!pixmap)
1288 XRenderPictureAttributes pa;
1290 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1291 pa.repeat = RepeatNormal;
1292 pict = pXRenderCreatePicture( gdi_display, pixmap,
1293 pict_formats[WXR_FORMAT_A8R8G8B8], CPRepeat, &pa );
1294 current_alpha = -1;
1297 if (alpha != current_alpha)
1299 XRenderColor col;
1300 col.red = col.green = col.blue = 0;
1301 col.alpha = current_alpha = alpha;
1302 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1304 return pict;
1307 /***********************************************************************
1308 * xrenderdrv_ExtTextOut
1310 static BOOL xrenderdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
1311 const RECT *lprect, LPCWSTR wstr, UINT count, const INT *lpDx )
1313 struct xrender_physdev *physdev = get_xrender_dev( dev );
1314 gsCacheEntry *entry;
1315 gsCacheEntryFormat *formatEntry;
1316 unsigned int idx;
1317 Picture pict, tile_pict = 0;
1318 XGlyphElt16 *elts;
1319 POINT offset, desired, current;
1320 int render_op = PictOpOver;
1321 XRenderColor col;
1322 RECT rect, bounds;
1323 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
1325 get_xrender_color( physdev, GetTextColor( physdev->dev.hdc ), &col );
1326 pict = get_xrender_picture( physdev, 0, (flags & ETO_CLIPPED) ? lprect : NULL );
1328 if(flags & ETO_OPAQUE)
1330 XRenderColor bg;
1332 if (physdev->format == WXR_FORMAT_MONO)
1333 /* use the inverse of the text color */
1334 bg.red = bg.green = bg.blue = bg.alpha = ~col.alpha;
1335 else
1336 get_xrender_color( physdev, GetBkColor( physdev->dev.hdc ), &bg );
1338 set_xrender_transformation( pict, 1, 1, 0, 0 );
1339 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &bg,
1340 physdev->x11dev->dc_rect.left + lprect->left,
1341 physdev->x11dev->dc_rect.top + lprect->top,
1342 lprect->right - lprect->left,
1343 lprect->bottom - lprect->top );
1344 add_device_bounds( physdev->x11dev, lprect );
1347 if(count == 0) return TRUE;
1349 EnterCriticalSection(&xrender_cs);
1351 entry = glyphsetCache + physdev->cache_index;
1352 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1354 for(idx = 0; idx < count; idx++) {
1355 if( !formatEntry ) {
1356 UploadGlyph(physdev, wstr[idx], type);
1357 /* re-evaluate format entry since aa_flags may have changed */
1358 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1359 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1360 UploadGlyph(physdev, wstr[idx], type);
1363 if (!formatEntry)
1365 WARN("could not upload requested glyphs\n");
1366 LeaveCriticalSection(&xrender_cs);
1367 return FALSE;
1370 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1371 physdev->x11dev->dc_rect.left + x, physdev->x11dev->dc_rect.top + y);
1373 elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1375 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1376 So we pass zeros to the function and move to our starting position using the first
1377 element of the elts array. */
1379 desired.x = physdev->x11dev->dc_rect.left + x;
1380 desired.y = physdev->x11dev->dc_rect.top + y;
1381 offset.x = offset.y = 0;
1382 current.x = current.y = 0;
1384 tile_pict = get_tile_pict(physdev->format, &col);
1386 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1388 if (physdev->format == WXR_FORMAT_MONO && col.red == 0 && col.green == 0 && col.blue == 0)
1389 render_op = PictOpOutReverse; /* This gives us 'black' text */
1391 reset_bounds( &bounds );
1392 for(idx = 0; idx < count; idx++)
1394 elts[idx].glyphset = formatEntry->glyphset;
1395 elts[idx].chars = wstr + idx;
1396 elts[idx].nchars = 1;
1397 elts[idx].xOff = desired.x - current.x;
1398 elts[idx].yOff = desired.y - current.y;
1400 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1401 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1403 rect.left = desired.x - physdev->x11dev->dc_rect.left - formatEntry->gis[wstr[idx]].x;
1404 rect.top = desired.y - physdev->x11dev->dc_rect.top - formatEntry->gis[wstr[idx]].y;
1405 rect.right = rect.left + formatEntry->gis[wstr[idx]].width;
1406 rect.bottom = rect.top + formatEntry->gis[wstr[idx]].height;
1407 add_bounds_rect( &bounds, &rect );
1409 if(!lpDx)
1411 desired.x += formatEntry->gis[wstr[idx]].xOff;
1412 desired.y += formatEntry->gis[wstr[idx]].yOff;
1414 else
1416 if(flags & ETO_PDY)
1418 offset.x += lpDx[idx * 2];
1419 offset.y += lpDx[idx * 2 + 1];
1421 else
1422 offset.x += lpDx[idx];
1423 desired.x = physdev->x11dev->dc_rect.left + x + offset.x;
1424 desired.y = physdev->x11dev->dc_rect.top + y + offset.y;
1428 /* Make sure we don't have any transforms set from a previous call */
1429 set_xrender_transformation(pict, 1, 1, 0, 0);
1430 pXRenderCompositeText16(gdi_display, render_op,
1431 tile_pict,
1432 pict,
1433 formatEntry->font_format,
1434 0, 0, 0, 0, elts, count);
1435 HeapFree(GetProcessHeap(), 0, elts);
1437 LeaveCriticalSection(&xrender_cs);
1438 add_device_bounds( physdev->x11dev, &bounds );
1439 return TRUE;
1442 /* multiply the alpha channel of a picture */
1443 static void multiply_alpha( Picture pict, XRenderPictFormat *format, int alpha,
1444 int x, int y, int width, int height )
1446 XRenderPictureAttributes pa;
1447 Pixmap src_pixmap, mask_pixmap;
1448 Picture src_pict, mask_pict;
1449 XRenderColor color;
1451 src_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1452 mask_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1453 pa.repeat = RepeatNormal;
1454 src_pict = pXRenderCreatePicture( gdi_display, src_pixmap, format, CPRepeat, &pa );
1455 pa.component_alpha = True;
1456 mask_pict = pXRenderCreatePicture( gdi_display, mask_pixmap, format, CPRepeat|CPComponentAlpha, &pa );
1457 color.red = color.green = color.blue = color.alpha = 0xffff;
1458 pXRenderFillRectangle( gdi_display, PictOpSrc, src_pict, &color, 0, 0, 1, 1 );
1459 color.alpha = alpha;
1460 pXRenderFillRectangle( gdi_display, PictOpSrc, mask_pict, &color, 0, 0, 1, 1 );
1461 pXRenderComposite( gdi_display, PictOpInReverse, src_pict, mask_pict, pict,
1462 0, 0, 0, 0, x, y, width, height );
1463 pXRenderFreePicture( gdi_display, src_pict );
1464 pXRenderFreePicture( gdi_display, mask_pict );
1465 XFreePixmap( gdi_display, src_pixmap );
1466 XFreePixmap( gdi_display, mask_pixmap );
1469 /* Helper function for (stretched) blitting using xrender */
1470 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1471 int x_src, int y_src, int width_src, int height_src,
1472 int x_dst, int y_dst, int width_dst, int height_dst,
1473 double xscale, double yscale )
1475 int x_offset, y_offset;
1477 if (width_src < 0)
1479 x_src += width_src + 1;
1480 width_src = -width_src;
1482 if (height_src < 0)
1484 y_src += height_src + 1;
1485 height_src = -height_src;
1487 if (width_dst < 0)
1489 x_dst += width_dst + 1;
1490 width_dst = -width_dst;
1492 if (height_dst < 0)
1494 y_dst += height_dst + 1;
1495 height_dst = -height_dst;
1498 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1499 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1500 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1501 if(xscale != 1.0 || yscale != 1.0)
1503 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1504 * in the wrong quadrant of the x-y plane.
1506 x_offset = (xscale < 0) ? -width_dst : 0;
1507 y_offset = (yscale < 0) ? -height_dst : 0;
1508 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1510 else
1512 x_offset = x_src;
1513 y_offset = y_src;
1514 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1516 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1517 x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst );
1520 /* Helper function for (stretched) mono->color blitting using xrender */
1521 static void xrender_mono_blit( Picture src_pict, Picture dst_pict,
1522 enum wxr_format dst_format, XRenderColor *fg, XRenderColor *bg,
1523 int x_src, int y_src, int width_src, int height_src,
1524 int x_dst, int y_dst, int width_dst, int height_dst,
1525 double xscale, double yscale )
1527 Picture tile_pict;
1528 int x_offset, y_offset;
1529 XRenderColor color;
1531 if (width_src < 0)
1533 x_src += width_src + 1;
1534 width_src = -width_src;
1536 if (height_src < 0)
1538 y_src += height_src + 1;
1539 height_src = -height_src;
1541 if (width_dst < 0)
1543 x_dst += width_dst + 1;
1544 width_dst = -width_dst;
1546 if (height_dst < 0)
1548 y_dst += height_dst + 1;
1549 height_dst = -height_dst;
1552 /* When doing a mono->color blit, the source data is used as mask, and the source picture
1553 * contains a 1x1 picture for tiling. The source data effectively acts as an alpha channel to
1554 * the tile data.
1556 EnterCriticalSection( &xrender_cs );
1557 color = *bg;
1558 color.alpha = 0xffff; /* tile pict needs 100% alpha */
1559 tile_pict = get_tile_pict( dst_format, &color );
1561 pXRenderFillRectangle( gdi_display, PictOpSrc, dst_pict, fg, x_dst, y_dst, width_dst, height_dst );
1563 if (xscale != 1.0 || yscale != 1.0)
1565 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1566 * in the wrong quadrant of the x-y plane.
1568 x_offset = (xscale < 0) ? -width_dst : 0;
1569 y_offset = (yscale < 0) ? -height_dst : 0;
1570 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1572 else
1574 x_offset = x_src;
1575 y_offset = y_src;
1576 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1578 pXRenderComposite(gdi_display, PictOpOver, tile_pict, src_pict, dst_pict,
1579 0, 0, x_offset, y_offset, x_dst, y_dst, width_dst, height_dst );
1580 LeaveCriticalSection( &xrender_cs );
1582 /* force the alpha channel for background pixels, it has been set to 100% by the tile */
1583 if (bg->alpha != 0xffff && (dst_format == WXR_FORMAT_A8R8G8B8 || dst_format == WXR_FORMAT_B8G8R8A8))
1584 multiply_alpha( dst_pict, pict_formats[dst_format], bg->alpha,
1585 x_dst, y_dst, width_dst, height_dst );
1588 /* create a pixmap and render picture for an image */
1589 static DWORD create_image_pixmap( BITMAPINFO *info, const struct gdi_image_bits *bits,
1590 struct bitblt_coords *src, enum wxr_format format,
1591 Pixmap *pixmap, Picture *pict, BOOL *use_repeat )
1593 DWORD ret;
1594 int width = src->visrect.right - src->visrect.left;
1595 int height = src->visrect.bottom - src->visrect.top;
1596 int depth = pict_formats[format]->depth;
1597 struct gdi_image_bits dst_bits;
1598 XRenderPictureAttributes pa;
1599 GC gc;
1600 XImage *image;
1602 image = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0, NULL,
1603 info->bmiHeader.biWidth, height, 32, 0 );
1604 if (!image) return ERROR_OUTOFMEMORY;
1606 ret = copy_image_bits( info, (format == WXR_FORMAT_R8G8B8), image, bits, &dst_bits, src, NULL, ~0u );
1607 if (ret) return ret;
1609 image->data = dst_bits.ptr;
1611 *use_repeat = (width == 1 && height == 1);
1612 pa.repeat = *use_repeat ? RepeatNormal : RepeatNone;
1614 *pixmap = XCreatePixmap( gdi_display, root_window, width, height, depth );
1615 gc = XCreateGC( gdi_display, *pixmap, 0, NULL );
1616 XPutImage( gdi_display, *pixmap, gc, image, src->visrect.left, 0, 0, 0, width, height );
1617 *pict = pXRenderCreatePicture( gdi_display, *pixmap, pict_formats[format], CPRepeat, &pa );
1618 XFreeGC( gdi_display, gc );
1620 /* make coordinates relative to the pixmap */
1621 src->x -= src->visrect.left;
1622 src->y -= src->visrect.top;
1623 OffsetRect( &src->visrect, -src->visrect.left, -src->visrect.top );
1625 image->data = NULL;
1626 XDestroyImage( image );
1627 if (dst_bits.free) dst_bits.free( &dst_bits );
1628 return ret;
1631 static void xrender_stretch_blit( struct xrender_physdev *physdev_src, struct xrender_physdev *physdev_dst,
1632 Drawable drawable, const struct bitblt_coords *src,
1633 const struct bitblt_coords *dst )
1635 int x_dst, y_dst;
1636 Picture src_pict = 0, dst_pict, mask_pict = 0;
1637 double xscale = src->width / (double)dst->width;
1638 double yscale = src->height / (double)dst->height;
1640 if (drawable) /* using an intermediate pixmap */
1642 x_dst = dst->x;
1643 y_dst = dst->y;
1644 dst_pict = pXRenderCreatePicture( gdi_display, drawable, physdev_dst->pict_format, 0, NULL );
1646 else
1648 x_dst = physdev_dst->x11dev->dc_rect.left + dst->x;
1649 y_dst = physdev_dst->x11dev->dc_rect.top + dst->y;
1650 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1653 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1655 /* mono -> color */
1656 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1658 XRenderColor fg, bg;
1660 get_xrender_color( physdev_dst, GetTextColor( physdev_dst->dev.hdc ), &fg );
1661 get_xrender_color( physdev_dst, GetBkColor( physdev_dst->dev.hdc ), &bg );
1662 fg.alpha = bg.alpha = 0;
1664 xrender_mono_blit( src_pict, dst_pict, physdev_dst->format, &fg, &bg,
1665 physdev_src->x11dev->dc_rect.left + src->x,
1666 physdev_src->x11dev->dc_rect.top + src->y,
1667 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1669 else /* color -> color (can be at different depths) or mono -> mono */
1671 if (physdev_dst->pict_format->depth == 32 && physdev_src->pict_format->depth < 32)
1672 mask_pict = get_no_alpha_mask();
1674 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict,
1675 physdev_src->x11dev->dc_rect.left + src->x,
1676 physdev_src->x11dev->dc_rect.top + src->y,
1677 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1680 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1684 static void xrender_put_image( Pixmap src_pixmap, Picture src_pict, Picture mask_pict, HRGN clip,
1685 XRenderPictFormat *dst_format, struct xrender_physdev *physdev,
1686 Drawable drawable, struct bitblt_coords *src,
1687 struct bitblt_coords *dst, BOOL use_repeat )
1689 int x_dst, y_dst;
1690 Picture dst_pict;
1691 double xscale, yscale;
1693 if (drawable) /* using an intermediate pixmap */
1695 RGNDATA *clip_data = NULL;
1697 if (clip) clip_data = X11DRV_GetRegionData( clip, 0 );
1698 x_dst = dst->x;
1699 y_dst = dst->y;
1700 dst_pict = pXRenderCreatePicture( gdi_display, drawable, dst_format, 0, NULL );
1701 if (clip_data)
1702 pXRenderSetPictureClipRectangles( gdi_display, dst_pict, 0, 0,
1703 (XRectangle *)clip_data->Buffer, clip_data->rdh.nCount );
1704 HeapFree( GetProcessHeap(), 0, clip_data );
1706 else
1708 x_dst = physdev->x11dev->dc_rect.left + dst->x;
1709 y_dst = physdev->x11dev->dc_rect.top + dst->y;
1710 dst_pict = get_xrender_picture( physdev, clip, &dst->visrect );
1713 if (!use_repeat)
1715 xscale = src->width / (double)dst->width;
1716 yscale = src->height / (double)dst->height;
1718 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1720 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict, src->x, src->y, src->width, src->height,
1721 x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1723 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1727 /***********************************************************************
1728 * xrenderdrv_StretchBlt
1730 static BOOL xrenderdrv_StretchBlt( PHYSDEV dst_dev, struct bitblt_coords *dst,
1731 PHYSDEV src_dev, struct bitblt_coords *src, DWORD rop )
1733 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1734 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1735 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
1737 if (src_dev->funcs != dst_dev->funcs)
1739 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pStretchBlt );
1740 return dst_dev->funcs->pStretchBlt( dst_dev, dst, src_dev, src, rop );
1743 /* XRender is of no use for color -> mono */
1744 if (physdev_dst->format == WXR_FORMAT_MONO && physdev_src->format != WXR_FORMAT_MONO)
1745 goto x11drv_fallback;
1747 /* if not stretching, we only need to handle format conversion */
1748 if (!stretch && physdev_dst->format == physdev_src->format) goto x11drv_fallback;
1750 if (rop != SRCCOPY)
1752 GC tmpGC;
1753 Pixmap tmp_pixmap;
1754 struct bitblt_coords tmp;
1756 /* make coordinates relative to tmp pixmap */
1757 tmp = *dst;
1758 tmp.x -= tmp.visrect.left;
1759 tmp.y -= tmp.visrect.top;
1760 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1762 tmpGC = XCreateGC( gdi_display, physdev_dst->x11dev->drawable, 0, NULL );
1763 XSetSubwindowMode( gdi_display, tmpGC, IncludeInferiors );
1764 XSetGraphicsExposures( gdi_display, tmpGC, False );
1765 tmp_pixmap = XCreatePixmap( gdi_display, root_window, tmp.visrect.right - tmp.visrect.left,
1766 tmp.visrect.bottom - tmp.visrect.top, physdev_dst->pict_format->depth );
1768 xrender_stretch_blit( physdev_src, physdev_dst, tmp_pixmap, src, &tmp );
1769 execute_rop( physdev_dst->x11dev, tmp_pixmap, tmpGC, &dst->visrect, rop );
1771 XFreePixmap( gdi_display, tmp_pixmap );
1772 XFreeGC( gdi_display, tmpGC );
1774 else xrender_stretch_blit( physdev_src, physdev_dst, 0, src, dst );
1776 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
1777 return TRUE;
1779 x11drv_fallback:
1780 return X11DRV_StretchBlt( &physdev_dst->x11dev->dev, dst, &physdev_src->x11dev->dev, src, rop );
1784 /***********************************************************************
1785 * xrenderdrv_PutImage
1787 static DWORD xrenderdrv_PutImage( PHYSDEV dev, HRGN clip, BITMAPINFO *info,
1788 const struct gdi_image_bits *bits, struct bitblt_coords *src,
1789 struct bitblt_coords *dst, DWORD rop )
1791 struct xrender_physdev *physdev = get_xrender_dev( dev );
1792 DWORD ret;
1793 Pixmap tmp_pixmap;
1794 GC gc;
1795 enum wxr_format src_format, dst_format;
1796 XRenderPictFormat *pict_format;
1797 Pixmap src_pixmap;
1798 Picture src_pict, mask_pict = 0;
1799 BOOL use_repeat;
1801 dst_format = physdev->format;
1802 src_format = get_xrender_format_from_bitmapinfo( info );
1803 if (!(pict_format = pict_formats[src_format])) goto update_format;
1805 /* make sure we can create an image with the same bpp */
1806 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1807 goto update_format;
1809 /* mono <-> color conversions not supported */
1810 if ((src_format != dst_format) && (src_format == WXR_FORMAT_MONO || dst_format == WXR_FORMAT_MONO))
1811 goto x11drv_fallback;
1813 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1815 if (!has_alpha( src_format ) && has_alpha( dst_format )) mask_pict = get_no_alpha_mask();
1817 ret = create_image_pixmap( info, bits, src, src_format, &src_pixmap, &src_pict, &use_repeat );
1818 if (!ret)
1820 struct bitblt_coords tmp;
1822 if (rop != SRCCOPY)
1824 BOOL restore_region = add_extra_clipping_region( physdev->x11dev, clip );
1826 /* make coordinates relative to tmp pixmap */
1827 tmp = *dst;
1828 tmp.x -= tmp.visrect.left;
1829 tmp.y -= tmp.visrect.top;
1830 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1832 gc = XCreateGC( gdi_display, physdev->x11dev->drawable, 0, NULL );
1833 XSetSubwindowMode( gdi_display, gc, IncludeInferiors );
1834 XSetGraphicsExposures( gdi_display, gc, False );
1835 tmp_pixmap = XCreatePixmap( gdi_display, root_window,
1836 tmp.visrect.right - tmp.visrect.left,
1837 tmp.visrect.bottom - tmp.visrect.top,
1838 physdev->pict_format->depth );
1840 xrender_put_image( src_pixmap, src_pict, mask_pict, NULL, physdev->pict_format,
1841 NULL, tmp_pixmap, src, &tmp, use_repeat );
1842 execute_rop( physdev->x11dev, tmp_pixmap, gc, &dst->visrect, rop );
1844 XFreePixmap( gdi_display, tmp_pixmap );
1845 XFreeGC( gdi_display, gc );
1846 if (restore_region) restore_clipping_region( physdev->x11dev );
1848 else xrender_put_image( src_pixmap, src_pict, mask_pict, clip,
1849 physdev->pict_format, physdev, 0, src, dst, use_repeat );
1851 add_device_bounds( physdev->x11dev, &dst->visrect );
1853 pXRenderFreePicture( gdi_display, src_pict );
1854 XFreePixmap( gdi_display, src_pixmap );
1856 return ret;
1858 update_format:
1859 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1860 set_color_info( pict_formats[dst_format], info );
1861 return ERROR_BAD_FORMAT;
1863 x11drv_fallback:
1864 dev = GET_NEXT_PHYSDEV( dev, pPutImage );
1865 return dev->funcs->pPutImage( dev, clip, info, bits, src, dst, rop );
1869 /***********************************************************************
1870 * xrenderdrv_BlendImage
1872 static DWORD xrenderdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
1873 struct bitblt_coords *src, struct bitblt_coords *dst,
1874 BLENDFUNCTION func )
1876 struct xrender_physdev *physdev = get_xrender_dev( dev );
1877 DWORD ret;
1878 enum wxr_format format;
1879 XRenderPictFormat *pict_format;
1880 Picture dst_pict, src_pict, mask_pict;
1881 Pixmap src_pixmap;
1882 BOOL use_repeat;
1884 format = get_xrender_format_from_bitmapinfo( info );
1885 if (!(func.AlphaFormat & AC_SRC_ALPHA))
1886 format = get_format_without_alpha( format );
1887 else if (format != WXR_FORMAT_A8R8G8B8 || info->bmiHeader.biCompression != BI_RGB)
1888 return ERROR_INVALID_PARAMETER;
1890 if (!(pict_format = pict_formats[format])) goto update_format;
1892 /* make sure we can create an image with the same bpp */
1893 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1894 goto update_format;
1896 if (format == WXR_FORMAT_MONO && physdev->format != WXR_FORMAT_MONO)
1897 goto update_format;
1899 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1901 ret = create_image_pixmap( info, bits, src, format, &src_pixmap, &src_pict, &use_repeat );
1902 if (!ret)
1904 double xscale, yscale;
1906 if (!use_repeat)
1908 xscale = src->width / (double)dst->width;
1909 yscale = src->height / (double)dst->height;
1911 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1913 dst_pict = get_xrender_picture( physdev, 0, &dst->visrect );
1915 EnterCriticalSection( &xrender_cs );
1916 mask_pict = get_mask_pict( func.SourceConstantAlpha * 257 );
1918 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1919 src->x, src->y, src->width, src->height,
1920 physdev->x11dev->dc_rect.left + dst->x,
1921 physdev->x11dev->dc_rect.top + dst->y,
1922 dst->width, dst->height, xscale, yscale );
1924 pXRenderFreePicture( gdi_display, src_pict );
1925 XFreePixmap( gdi_display, src_pixmap );
1927 LeaveCriticalSection( &xrender_cs );
1928 add_device_bounds( physdev->x11dev, &dst->visrect );
1930 return ret;
1932 update_format:
1933 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1934 set_color_info( physdev->pict_format, info );
1935 return ERROR_BAD_FORMAT;
1939 /***********************************************************************
1940 * xrenderdrv_AlphaBlend
1942 static BOOL xrenderdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
1943 PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blendfn )
1945 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1946 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1947 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
1948 XRenderPictureAttributes pa;
1949 Pixmap tmp_pixmap = 0;
1950 double xscale, yscale;
1952 if (src_dev->funcs != dst_dev->funcs)
1954 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pAlphaBlend );
1955 return dst_dev->funcs->pAlphaBlend( dst_dev, dst, src_dev, src, blendfn );
1958 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->format != WXR_FORMAT_A8R8G8B8)
1960 SetLastError( ERROR_INVALID_PARAMETER );
1961 return FALSE;
1964 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1966 xscale = src->width / (double)dst->width;
1967 yscale = src->height / (double)dst->height;
1969 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1971 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1973 /* mono -> color blending needs an intermediate color pixmap */
1974 XRenderColor fg, bg;
1975 int width = src->visrect.right - src->visrect.left;
1976 int height = src->visrect.bottom - src->visrect.top;
1978 /* blending doesn't use the destination DC colors */
1979 fg.red = fg.green = fg.blue = 0;
1980 bg.red = bg.green = bg.blue = 0xffff;
1981 fg.alpha = bg.alpha = 0xffff;
1983 tmp_pixmap = XCreatePixmap( gdi_display, root_window, width, height,
1984 physdev_dst->pict_format->depth );
1985 tmp_pict = pXRenderCreatePicture( gdi_display, tmp_pixmap, physdev_dst->pict_format, 0, NULL );
1987 xrender_mono_blit( src_pict, tmp_pict, physdev_dst->format, &fg, &bg,
1988 src->visrect.left, src->visrect.top, width, height, 0, 0, width, height, 1, 1 );
1990 else if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->pict_format)
1992 /* we need a source picture with no alpha */
1993 enum wxr_format format = get_format_without_alpha( physdev_src->format );
1994 if (format != physdev_src->format)
1996 pa.subwindow_mode = IncludeInferiors;
1997 tmp_pict = pXRenderCreatePicture( gdi_display, physdev_src->x11dev->drawable,
1998 pict_formats[format], CPSubwindowMode, &pa );
2002 if (tmp_pict) src_pict = tmp_pict;
2004 EnterCriticalSection( &xrender_cs );
2005 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2007 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2008 physdev_src->x11dev->dc_rect.left + src->x,
2009 physdev_src->x11dev->dc_rect.top + src->y,
2010 src->width, src->height,
2011 physdev_dst->x11dev->dc_rect.left + dst->x,
2012 physdev_dst->x11dev->dc_rect.top + dst->y,
2013 dst->width, dst->height, xscale, yscale );
2015 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2016 if (tmp_pixmap) XFreePixmap( gdi_display, tmp_pixmap );
2018 LeaveCriticalSection( &xrender_cs );
2019 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
2020 return TRUE;
2023 /***********************************************************************
2024 * xrenderdrv_GradientFill
2026 static BOOL xrenderdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
2027 void * grad_array, ULONG ngrad, ULONG mode )
2029 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
2030 static const XFixed stops[2] = { 0, 1 << 16 };
2031 struct xrender_physdev *physdev = get_xrender_dev( dev );
2032 XLinearGradient gradient;
2033 XRenderColor colors[2];
2034 Picture src_pict, dst_pict;
2035 unsigned int i;
2036 const GRADIENT_RECT *rect = grad_array;
2037 RECT rc;
2038 POINT pt[2];
2040 if (!pXRenderCreateLinearGradient) goto fallback;
2042 /* <= 16-bpp uses dithering */
2043 if (!physdev->pict_format || physdev->pict_format->depth <= 16) goto fallback;
2045 switch (mode)
2047 case GRADIENT_FILL_RECT_H:
2048 case GRADIENT_FILL_RECT_V:
2049 for (i = 0; i < ngrad; i++, rect++)
2051 const TRIVERTEX *v1 = vert_array + rect->UpperLeft;
2052 const TRIVERTEX *v2 = vert_array + rect->LowerRight;
2054 colors[0].red = v1->Red * 257 / 256;
2055 colors[0].green = v1->Green * 257 / 256;
2056 colors[0].blue = v1->Blue * 257 / 256;
2057 colors[1].red = v2->Red * 257 / 256;
2058 colors[1].green = v2->Green * 257 / 256;
2059 colors[1].blue = v2->Blue * 257 / 256;
2060 /* always ignore alpha since otherwise xrender will want to pre-multiply the colors */
2061 colors[0].alpha = colors[1].alpha = 65535;
2063 pt[0].x = v1->x;
2064 pt[0].y = v1->y;
2065 pt[1].x = v2->x;
2066 pt[1].y = v2->y;
2067 LPtoDP( dev->hdc, pt, 2 );
2068 if (mode == GRADIENT_FILL_RECT_H)
2070 gradient.p1.y = gradient.p2.y = 0;
2071 if (pt[1].x > pt[0].x)
2073 gradient.p1.x = 0;
2074 gradient.p2.x = (pt[1].x - pt[0].x) << 16;
2076 else
2078 gradient.p1.x = (pt[0].x - pt[1].x) << 16;
2079 gradient.p2.x = 0;
2082 else
2084 gradient.p1.x = gradient.p2.x = 0;
2085 if (pt[1].y > pt[0].y)
2087 gradient.p1.y = 0;
2088 gradient.p2.y = (pt[1].y - pt[0].y) << 16;
2090 else
2092 gradient.p1.y = (pt[0].y - pt[1].y) << 16;
2093 gradient.p2.y = 0;
2097 rc.left = min( pt[0].x, pt[1].x );
2098 rc.top = min( pt[0].y, pt[1].y );
2099 rc.right = max( pt[0].x, pt[1].x );
2100 rc.bottom = max( pt[0].y, pt[1].y );
2102 TRACE( "%u gradient %s colors %04x,%04x,%04x,%04x -> %04x,%04x,%04x,%04x\n",
2103 mode, wine_dbgstr_rect( &rc ),
2104 colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha,
2105 colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha );
2107 dst_pict = get_xrender_picture( physdev, 0, NULL );
2109 src_pict = pXRenderCreateLinearGradient( gdi_display, &gradient, stops, colors, 2 );
2110 xrender_blit( PictOpSrc, src_pict, 0, dst_pict,
2111 0, 0, rc.right - rc.left, rc.bottom - rc.top,
2112 physdev->x11dev->dc_rect.left + rc.left,
2113 physdev->x11dev->dc_rect.top + rc.top,
2114 rc.right - rc.left, rc.bottom - rc.top, 1, 1 );
2115 pXRenderFreePicture( gdi_display, src_pict );
2116 add_device_bounds( physdev->x11dev, &rc );
2118 return TRUE;
2121 fallback:
2122 #endif
2123 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
2124 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
2127 /***********************************************************************
2128 * xrenderdrv_SelectBrush
2130 static HBRUSH xrenderdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2132 struct xrender_physdev *physdev = get_xrender_dev( dev );
2133 Pixmap pixmap;
2134 XVisualInfo vis = default_visual;
2135 XRenderPictFormat *format = physdev->pict_format;
2137 if (!pattern) goto x11drv_fallback;
2138 if (pattern->info->bmiHeader.biBitCount == 1) goto x11drv_fallback;
2139 if (physdev->format == WXR_FORMAT_MONO) goto x11drv_fallback;
2141 vis.depth = format->depth;
2142 vis.red_mask = format->direct.redMask << format->direct.red;
2143 vis.green_mask = format->direct.greenMask << format->direct.green;
2144 vis.blue_mask = format->direct.blueMask << format->direct.blue;
2146 pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, pattern->info,
2147 &pattern->bits, pattern->usage );
2148 if (!pixmap) return 0;
2150 if (physdev->x11dev->brush.pixmap) XFreePixmap( gdi_display, physdev->x11dev->brush.pixmap );
2151 physdev->x11dev->brush.pixmap = pixmap;
2152 physdev->x11dev->brush.fillStyle = FillTiled;
2153 physdev->x11dev->brush.pixel = 0; /* ignored */
2154 physdev->x11dev->brush.style = BS_PATTERN;
2155 return hbrush;
2157 x11drv_fallback:
2158 dev = GET_NEXT_PHYSDEV( dev, pSelectBrush );
2159 return dev->funcs->pSelectBrush( dev, hbrush, pattern );
2163 static const struct gdi_dc_funcs xrender_funcs =
2165 NULL, /* pAbortDoc */
2166 NULL, /* pAbortPath */
2167 xrenderdrv_AlphaBlend, /* pAlphaBlend */
2168 NULL, /* pAngleArc */
2169 NULL, /* pArc */
2170 NULL, /* pArcTo */
2171 NULL, /* pBeginPath */
2172 xrenderdrv_BlendImage, /* pBlendImage */
2173 NULL, /* pChord */
2174 NULL, /* pCloseFigure */
2175 xrenderdrv_CreateCompatibleDC, /* pCreateCompatibleDC */
2176 xrenderdrv_CreateDC, /* pCreateDC */
2177 xrenderdrv_DeleteDC, /* pDeleteDC */
2178 NULL, /* pDeleteObject */
2179 NULL, /* pDeviceCapabilities */
2180 NULL, /* pEllipse */
2181 NULL, /* pEndDoc */
2182 NULL, /* pEndPage */
2183 NULL, /* pEndPath */
2184 NULL, /* pEnumFonts */
2185 NULL, /* pEnumICMProfiles */
2186 NULL, /* pExcludeClipRect */
2187 NULL, /* pExtDeviceMode */
2188 xrenderdrv_ExtEscape, /* pExtEscape */
2189 NULL, /* pExtFloodFill */
2190 NULL, /* pExtSelectClipRgn */
2191 xrenderdrv_ExtTextOut, /* pExtTextOut */
2192 NULL, /* pFillPath */
2193 NULL, /* pFillRgn */
2194 NULL, /* pFlattenPath */
2195 NULL, /* pFontIsLinked */
2196 NULL, /* pFrameRgn */
2197 NULL, /* pGdiComment */
2198 NULL, /* pGdiRealizationInfo */
2199 NULL, /* pGetBoundsRect */
2200 NULL, /* pGetCharABCWidths */
2201 NULL, /* pGetCharABCWidthsI */
2202 NULL, /* pGetCharWidth */
2203 NULL, /* pGetDeviceCaps */
2204 NULL, /* pGetDeviceGammaRamp */
2205 NULL, /* pGetFontData */
2206 NULL, /* pGetFontUnicodeRanges */
2207 NULL, /* pGetGlyphIndices */
2208 NULL, /* pGetGlyphOutline */
2209 NULL, /* pGetICMProfile */
2210 NULL, /* pGetImage */
2211 NULL, /* pGetKerningPairs */
2212 NULL, /* pGetNearestColor */
2213 NULL, /* pGetOutlineTextMetrics */
2214 NULL, /* pGetPixel */
2215 NULL, /* pGetSystemPaletteEntries */
2216 NULL, /* pGetTextCharsetInfo */
2217 NULL, /* pGetTextExtentExPoint */
2218 NULL, /* pGetTextExtentExPointI */
2219 NULL, /* pGetTextFace */
2220 NULL, /* pGetTextMetrics */
2221 xrenderdrv_GradientFill, /* pGradientFill */
2222 NULL, /* pIntersectClipRect */
2223 NULL, /* pInvertRgn */
2224 NULL, /* pLineTo */
2225 NULL, /* pModifyWorldTransform */
2226 NULL, /* pMoveTo */
2227 NULL, /* pOffsetClipRgn */
2228 NULL, /* pOffsetViewportOrg */
2229 NULL, /* pOffsetWindowOrg */
2230 NULL, /* pPaintRgn */
2231 NULL, /* pPatBlt */
2232 NULL, /* pPie */
2233 NULL, /* pPolyBezier */
2234 NULL, /* pPolyBezierTo */
2235 NULL, /* pPolyDraw */
2236 NULL, /* pPolyPolygon */
2237 NULL, /* pPolyPolyline */
2238 NULL, /* pPolygon */
2239 NULL, /* pPolyline */
2240 NULL, /* pPolylineTo */
2241 xrenderdrv_PutImage, /* pPutImage */
2242 NULL, /* pRealizeDefaultPalette */
2243 NULL, /* pRealizePalette */
2244 NULL, /* pRectangle */
2245 NULL, /* pResetDC */
2246 NULL, /* pRestoreDC */
2247 NULL, /* pRoundRect */
2248 NULL, /* pSaveDC */
2249 NULL, /* pScaleViewportExt */
2250 NULL, /* pScaleWindowExt */
2251 NULL, /* pSelectBitmap */
2252 xrenderdrv_SelectBrush, /* pSelectBrush */
2253 NULL, /* pSelectClipPath */
2254 xrenderdrv_SelectFont, /* pSelectFont */
2255 NULL, /* pSelectPalette */
2256 NULL, /* pSelectPen */
2257 NULL, /* pSetArcDirection */
2258 NULL, /* pSetBkColor */
2259 NULL, /* pSetBkMode */
2260 NULL, /* pSetBoundsRect */
2261 NULL, /* pSetDCBrushColor */
2262 NULL, /* pSetDCPenColor */
2263 NULL, /* pSetDIBitsToDevice */
2264 xrenderdrv_SetDeviceClipping, /* pSetDeviceClipping */
2265 NULL, /* pSetDeviceGammaRamp */
2266 NULL, /* pSetLayout */
2267 NULL, /* pSetMapMode */
2268 NULL, /* pSetMapperFlags */
2269 NULL, /* pSetPixel */
2270 NULL, /* pSetPolyFillMode */
2271 NULL, /* pSetROP2 */
2272 NULL, /* pSetRelAbs */
2273 NULL, /* pSetStretchBltMode */
2274 NULL, /* pSetTextAlign */
2275 NULL, /* pSetTextCharacterExtra */
2276 NULL, /* pSetTextColor */
2277 NULL, /* pSetTextJustification */
2278 NULL, /* pSetViewportExt */
2279 NULL, /* pSetViewportOrg */
2280 NULL, /* pSetWindowExt */
2281 NULL, /* pSetWindowOrg */
2282 NULL, /* pSetWorldTransform */
2283 NULL, /* pStartDoc */
2284 NULL, /* pStartPage */
2285 xrenderdrv_StretchBlt, /* pStretchBlt */
2286 NULL, /* pStretchDIBits */
2287 NULL, /* pStrokeAndFillPath */
2288 NULL, /* pStrokePath */
2289 NULL, /* pUnrealizePalette */
2290 NULL, /* pWidenPath */
2291 NULL, /* wine_get_wgl_driver */
2292 GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */
2295 #else /* SONAME_LIBXRENDER */
2297 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
2299 TRACE("XRender support not compiled in.\n");
2300 return NULL;
2303 #endif /* SONAME_LIBXRENDER */