mshtml: Fixed leaks in IHTMLStyleSheet::Release and IHTMLStyleSheetsCollection::Relea...
[wine/multimedia.git] / dlls / winex11.drv / xrender.c
blobca2d3daf589ff38c98e1e330b3dbbba489218103
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 if (buflen)
1133 GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1134 else
1135 gm.gmBlackBoxX = gm.gmBlackBoxY = 0; /* empty glyph */
1136 formatEntry->realized[glyph] = TRUE;
1138 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1139 buflen,
1140 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1141 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1143 gi.width = gm.gmBlackBoxX;
1144 gi.height = gm.gmBlackBoxY;
1145 gi.x = -gm.gmptGlyphOrigin.x;
1146 gi.y = gm.gmptGlyphOrigin.y;
1147 gi.xOff = gm.gmCellIncX;
1148 gi.yOff = gm.gmCellIncY;
1150 if(TRACE_ON(xrender)) {
1151 int pitch, i, j;
1152 char output[300];
1153 unsigned char *line;
1155 if(format == AA_None) {
1156 pitch = ((gi.width + 31) / 32) * 4;
1157 for(i = 0; i < gi.height; i++) {
1158 line = (unsigned char*) buf + i * pitch;
1159 output[0] = '\0';
1160 for(j = 0; j < pitch * 8; j++) {
1161 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1163 TRACE("%s\n", output);
1165 } else {
1166 static const char blks[] = " .:;!o*#";
1167 char str[2];
1169 str[1] = '\0';
1170 pitch = ((gi.width + 3) / 4) * 4;
1171 for(i = 0; i < gi.height; i++) {
1172 line = (unsigned char*) buf + i * pitch;
1173 output[0] = '\0';
1174 for(j = 0; j < pitch; j++) {
1175 str[0] = blks[line[j] >> 5];
1176 strcat(output, str);
1178 TRACE("%s\n", output);
1184 if(formatEntry->glyphset) {
1185 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1186 unsigned char *byte = (unsigned char*) buf, c;
1187 int i = buflen;
1189 while(i--) {
1190 c = *byte;
1192 /* magic to flip bit order */
1193 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1194 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1195 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1197 *byte++ = c;
1200 else if ( format != AA_Grey &&
1201 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1203 unsigned int i, *data = (unsigned int *)buf;
1204 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1206 gid = glyph;
1209 XRenderCompositeText seems to ignore 0x0 glyphs when
1210 AA_None, which means we lose the advance width of glyphs
1211 like the space. We'll pretend that such glyphs are 1x1
1212 bitmaps.
1215 if(buflen == 0)
1216 gi.width = gi.height = 1;
1218 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1219 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1222 HeapFree(GetProcessHeap(), 0, buf);
1223 formatEntry->gis[glyph] = gi;
1226 /*************************************************************
1227 * get_tile_pict
1229 * Returns an appropriate Picture for tiling the text colour.
1230 * Call and use result within the xrender_cs
1232 static Picture get_tile_pict( enum wxr_format wxr_format, const XRenderColor *color)
1234 static struct
1236 Pixmap xpm;
1237 Picture pict;
1238 XRenderColor current_color;
1239 } tiles[WXR_NB_FORMATS], *tile;
1241 tile = &tiles[wxr_format];
1243 if(!tile->xpm)
1245 XRenderPictureAttributes pa;
1246 XRenderPictFormat *pict_format = pict_formats[wxr_format];
1248 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, pict_format->depth);
1250 pa.repeat = RepeatNormal;
1251 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, pict_format, CPRepeat, &pa);
1253 /* init current_color to something different from text_pixel */
1254 tile->current_color = *color;
1255 tile->current_color.red ^= 0xffff;
1257 if (wxr_format == WXR_FORMAT_MONO)
1259 /* for a 1bpp bitmap we always need a 1 in the tile */
1260 XRenderColor col;
1261 col.red = col.green = col.blue = 0;
1262 col.alpha = 0xffff;
1263 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1267 if (memcmp( color, &tile->current_color, sizeof(*color) ) && wxr_format != WXR_FORMAT_MONO)
1269 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, color, 0, 0, 1, 1);
1270 tile->current_color = *color;
1272 return tile->pict;
1275 /*************************************************************
1276 * get_mask_pict
1278 * Returns an appropriate Picture for masking with the specified alpha.
1279 * Call and use result within the xrender_cs
1281 static Picture get_mask_pict( int alpha )
1283 static Pixmap pixmap;
1284 static Picture pict;
1285 static int current_alpha;
1287 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1289 if (!pixmap)
1291 XRenderPictureAttributes pa;
1293 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1294 pa.repeat = RepeatNormal;
1295 pict = pXRenderCreatePicture( gdi_display, pixmap,
1296 pict_formats[WXR_FORMAT_A8R8G8B8], CPRepeat, &pa );
1297 current_alpha = -1;
1300 if (alpha != current_alpha)
1302 XRenderColor col;
1303 col.red = col.green = col.blue = 0;
1304 col.alpha = current_alpha = alpha;
1305 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1307 return pict;
1310 /***********************************************************************
1311 * xrenderdrv_ExtTextOut
1313 static BOOL xrenderdrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
1314 const RECT *lprect, LPCWSTR wstr, UINT count, const INT *lpDx )
1316 struct xrender_physdev *physdev = get_xrender_dev( dev );
1317 gsCacheEntry *entry;
1318 gsCacheEntryFormat *formatEntry;
1319 unsigned int idx;
1320 Picture pict, tile_pict = 0;
1321 XGlyphElt16 *elts;
1322 POINT offset, desired, current;
1323 int render_op = PictOpOver;
1324 XRenderColor col;
1325 RECT rect, bounds;
1326 enum glyph_type type = (flags & ETO_GLYPH_INDEX) ? GLYPH_INDEX : GLYPH_WCHAR;
1328 get_xrender_color( physdev, GetTextColor( physdev->dev.hdc ), &col );
1329 pict = get_xrender_picture( physdev, 0, (flags & ETO_CLIPPED) ? lprect : NULL );
1331 if(flags & ETO_OPAQUE)
1333 XRenderColor bg;
1335 if (physdev->format == WXR_FORMAT_MONO)
1336 /* use the inverse of the text color */
1337 bg.red = bg.green = bg.blue = bg.alpha = ~col.alpha;
1338 else
1339 get_xrender_color( physdev, GetBkColor( physdev->dev.hdc ), &bg );
1341 set_xrender_transformation( pict, 1, 1, 0, 0 );
1342 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &bg,
1343 physdev->x11dev->dc_rect.left + lprect->left,
1344 physdev->x11dev->dc_rect.top + lprect->top,
1345 lprect->right - lprect->left,
1346 lprect->bottom - lprect->top );
1347 add_device_bounds( physdev->x11dev, lprect );
1350 if(count == 0) return TRUE;
1352 EnterCriticalSection(&xrender_cs);
1354 entry = glyphsetCache + physdev->cache_index;
1355 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1357 for(idx = 0; idx < count; idx++) {
1358 if( !formatEntry ) {
1359 UploadGlyph(physdev, wstr[idx], type);
1360 /* re-evaluate format entry since aa_flags may have changed */
1361 formatEntry = entry->format[type][aa_type_from_flags( physdev->aa_flags )];
1362 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1363 UploadGlyph(physdev, wstr[idx], type);
1366 if (!formatEntry)
1368 WARN("could not upload requested glyphs\n");
1369 LeaveCriticalSection(&xrender_cs);
1370 return FALSE;
1373 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1374 physdev->x11dev->dc_rect.left + x, physdev->x11dev->dc_rect.top + y);
1376 elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1378 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1379 So we pass zeros to the function and move to our starting position using the first
1380 element of the elts array. */
1382 desired.x = physdev->x11dev->dc_rect.left + x;
1383 desired.y = physdev->x11dev->dc_rect.top + y;
1384 offset.x = offset.y = 0;
1385 current.x = current.y = 0;
1387 tile_pict = get_tile_pict(physdev->format, &col);
1389 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1391 if (physdev->format == WXR_FORMAT_MONO && col.red == 0 && col.green == 0 && col.blue == 0)
1392 render_op = PictOpOutReverse; /* This gives us 'black' text */
1394 reset_bounds( &bounds );
1395 for(idx = 0; idx < count; idx++)
1397 elts[idx].glyphset = formatEntry->glyphset;
1398 elts[idx].chars = wstr + idx;
1399 elts[idx].nchars = 1;
1400 elts[idx].xOff = desired.x - current.x;
1401 elts[idx].yOff = desired.y - current.y;
1403 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1404 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1406 rect.left = desired.x - physdev->x11dev->dc_rect.left - formatEntry->gis[wstr[idx]].x;
1407 rect.top = desired.y - physdev->x11dev->dc_rect.top - formatEntry->gis[wstr[idx]].y;
1408 rect.right = rect.left + formatEntry->gis[wstr[idx]].width;
1409 rect.bottom = rect.top + formatEntry->gis[wstr[idx]].height;
1410 add_bounds_rect( &bounds, &rect );
1412 if(!lpDx)
1414 desired.x += formatEntry->gis[wstr[idx]].xOff;
1415 desired.y += formatEntry->gis[wstr[idx]].yOff;
1417 else
1419 if(flags & ETO_PDY)
1421 offset.x += lpDx[idx * 2];
1422 offset.y += lpDx[idx * 2 + 1];
1424 else
1425 offset.x += lpDx[idx];
1426 desired.x = physdev->x11dev->dc_rect.left + x + offset.x;
1427 desired.y = physdev->x11dev->dc_rect.top + y + offset.y;
1431 /* Make sure we don't have any transforms set from a previous call */
1432 set_xrender_transformation(pict, 1, 1, 0, 0);
1433 pXRenderCompositeText16(gdi_display, render_op,
1434 tile_pict,
1435 pict,
1436 formatEntry->font_format,
1437 0, 0, 0, 0, elts, count);
1438 HeapFree(GetProcessHeap(), 0, elts);
1440 LeaveCriticalSection(&xrender_cs);
1441 add_device_bounds( physdev->x11dev, &bounds );
1442 return TRUE;
1445 /* multiply the alpha channel of a picture */
1446 static void multiply_alpha( Picture pict, XRenderPictFormat *format, int alpha,
1447 int x, int y, int width, int height )
1449 XRenderPictureAttributes pa;
1450 Pixmap src_pixmap, mask_pixmap;
1451 Picture src_pict, mask_pict;
1452 XRenderColor color;
1454 src_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1455 mask_pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, format->depth );
1456 pa.repeat = RepeatNormal;
1457 src_pict = pXRenderCreatePicture( gdi_display, src_pixmap, format, CPRepeat, &pa );
1458 pa.component_alpha = True;
1459 mask_pict = pXRenderCreatePicture( gdi_display, mask_pixmap, format, CPRepeat|CPComponentAlpha, &pa );
1460 color.red = color.green = color.blue = color.alpha = 0xffff;
1461 pXRenderFillRectangle( gdi_display, PictOpSrc, src_pict, &color, 0, 0, 1, 1 );
1462 color.alpha = alpha;
1463 pXRenderFillRectangle( gdi_display, PictOpSrc, mask_pict, &color, 0, 0, 1, 1 );
1464 pXRenderComposite( gdi_display, PictOpInReverse, src_pict, mask_pict, pict,
1465 0, 0, 0, 0, x, y, width, height );
1466 pXRenderFreePicture( gdi_display, src_pict );
1467 pXRenderFreePicture( gdi_display, mask_pict );
1468 XFreePixmap( gdi_display, src_pixmap );
1469 XFreePixmap( gdi_display, mask_pixmap );
1472 /* Helper function for (stretched) blitting using xrender */
1473 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1474 int x_src, int y_src, int width_src, int height_src,
1475 int x_dst, int y_dst, int width_dst, int height_dst,
1476 double xscale, double yscale )
1478 int x_offset, y_offset;
1480 if (width_src < 0)
1482 x_src += width_src + 1;
1483 width_src = -width_src;
1485 if (height_src < 0)
1487 y_src += height_src + 1;
1488 height_src = -height_src;
1490 if (width_dst < 0)
1492 x_dst += width_dst + 1;
1493 width_dst = -width_dst;
1495 if (height_dst < 0)
1497 y_dst += height_dst + 1;
1498 height_dst = -height_dst;
1501 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1502 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1503 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1504 if(xscale != 1.0 || yscale != 1.0)
1506 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1507 * in the wrong quadrant of the x-y plane.
1509 x_offset = (xscale < 0) ? -width_dst : 0;
1510 y_offset = (yscale < 0) ? -height_dst : 0;
1511 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1513 else
1515 x_offset = x_src;
1516 y_offset = y_src;
1517 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1519 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1520 x_offset, y_offset, 0, 0, x_dst, y_dst, width_dst, height_dst );
1523 /* Helper function for (stretched) mono->color blitting using xrender */
1524 static void xrender_mono_blit( Picture src_pict, Picture dst_pict,
1525 enum wxr_format dst_format, XRenderColor *fg, XRenderColor *bg,
1526 int x_src, int y_src, int width_src, int height_src,
1527 int x_dst, int y_dst, int width_dst, int height_dst,
1528 double xscale, double yscale )
1530 Picture tile_pict;
1531 int x_offset, y_offset;
1532 XRenderColor color;
1534 if (width_src < 0)
1536 x_src += width_src + 1;
1537 width_src = -width_src;
1539 if (height_src < 0)
1541 y_src += height_src + 1;
1542 height_src = -height_src;
1544 if (width_dst < 0)
1546 x_dst += width_dst + 1;
1547 width_dst = -width_dst;
1549 if (height_dst < 0)
1551 y_dst += height_dst + 1;
1552 height_dst = -height_dst;
1555 /* When doing a mono->color blit, the source data is used as mask, and the source picture
1556 * contains a 1x1 picture for tiling. The source data effectively acts as an alpha channel to
1557 * the tile data.
1559 EnterCriticalSection( &xrender_cs );
1560 color = *bg;
1561 color.alpha = 0xffff; /* tile pict needs 100% alpha */
1562 tile_pict = get_tile_pict( dst_format, &color );
1564 pXRenderFillRectangle( gdi_display, PictOpSrc, dst_pict, fg, x_dst, y_dst, width_dst, height_dst );
1566 if (xscale != 1.0 || yscale != 1.0)
1568 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1569 * in the wrong quadrant of the x-y plane.
1571 x_offset = (xscale < 0) ? -width_dst : 0;
1572 y_offset = (yscale < 0) ? -height_dst : 0;
1573 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1575 else
1577 x_offset = x_src;
1578 y_offset = y_src;
1579 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1581 pXRenderComposite(gdi_display, PictOpOver, tile_pict, src_pict, dst_pict,
1582 0, 0, x_offset, y_offset, x_dst, y_dst, width_dst, height_dst );
1583 LeaveCriticalSection( &xrender_cs );
1585 /* force the alpha channel for background pixels, it has been set to 100% by the tile */
1586 if (bg->alpha != 0xffff && (dst_format == WXR_FORMAT_A8R8G8B8 || dst_format == WXR_FORMAT_B8G8R8A8))
1587 multiply_alpha( dst_pict, pict_formats[dst_format], bg->alpha,
1588 x_dst, y_dst, width_dst, height_dst );
1591 /* create a pixmap and render picture for an image */
1592 static DWORD create_image_pixmap( BITMAPINFO *info, const struct gdi_image_bits *bits,
1593 struct bitblt_coords *src, enum wxr_format format,
1594 Pixmap *pixmap, Picture *pict, BOOL *use_repeat )
1596 DWORD ret;
1597 int width = src->visrect.right - src->visrect.left;
1598 int height = src->visrect.bottom - src->visrect.top;
1599 int depth = pict_formats[format]->depth;
1600 struct gdi_image_bits dst_bits;
1601 XRenderPictureAttributes pa;
1602 GC gc;
1603 XImage *image;
1605 image = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0, NULL,
1606 info->bmiHeader.biWidth, height, 32, 0 );
1607 if (!image) return ERROR_OUTOFMEMORY;
1609 ret = copy_image_bits( info, (format == WXR_FORMAT_R8G8B8), image, bits, &dst_bits, src, NULL, ~0u );
1610 if (ret) return ret;
1612 image->data = dst_bits.ptr;
1614 *use_repeat = (width == 1 && height == 1);
1615 pa.repeat = *use_repeat ? RepeatNormal : RepeatNone;
1617 *pixmap = XCreatePixmap( gdi_display, root_window, width, height, depth );
1618 gc = XCreateGC( gdi_display, *pixmap, 0, NULL );
1619 XPutImage( gdi_display, *pixmap, gc, image, src->visrect.left, 0, 0, 0, width, height );
1620 *pict = pXRenderCreatePicture( gdi_display, *pixmap, pict_formats[format], CPRepeat, &pa );
1621 XFreeGC( gdi_display, gc );
1623 /* make coordinates relative to the pixmap */
1624 src->x -= src->visrect.left;
1625 src->y -= src->visrect.top;
1626 OffsetRect( &src->visrect, -src->visrect.left, -src->visrect.top );
1628 image->data = NULL;
1629 XDestroyImage( image );
1630 if (dst_bits.free) dst_bits.free( &dst_bits );
1631 return ret;
1634 static void xrender_stretch_blit( struct xrender_physdev *physdev_src, struct xrender_physdev *physdev_dst,
1635 Drawable drawable, const struct bitblt_coords *src,
1636 const struct bitblt_coords *dst )
1638 int x_dst, y_dst;
1639 Picture src_pict = 0, dst_pict, mask_pict = 0;
1640 double xscale = src->width / (double)dst->width;
1641 double yscale = src->height / (double)dst->height;
1643 if (drawable) /* using an intermediate pixmap */
1645 x_dst = dst->x;
1646 y_dst = dst->y;
1647 dst_pict = pXRenderCreatePicture( gdi_display, drawable, physdev_dst->pict_format, 0, NULL );
1649 else
1651 x_dst = physdev_dst->x11dev->dc_rect.left + dst->x;
1652 y_dst = physdev_dst->x11dev->dc_rect.top + dst->y;
1653 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1656 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1658 /* mono -> color */
1659 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1661 XRenderColor fg, bg;
1663 get_xrender_color( physdev_dst, GetTextColor( physdev_dst->dev.hdc ), &fg );
1664 get_xrender_color( physdev_dst, GetBkColor( physdev_dst->dev.hdc ), &bg );
1665 fg.alpha = bg.alpha = 0;
1667 xrender_mono_blit( src_pict, dst_pict, physdev_dst->format, &fg, &bg,
1668 physdev_src->x11dev->dc_rect.left + src->x,
1669 physdev_src->x11dev->dc_rect.top + src->y,
1670 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1672 else /* color -> color (can be at different depths) or mono -> mono */
1674 if (physdev_dst->pict_format->depth == 32 && physdev_src->pict_format->depth < 32)
1675 mask_pict = get_no_alpha_mask();
1677 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict,
1678 physdev_src->x11dev->dc_rect.left + src->x,
1679 physdev_src->x11dev->dc_rect.top + src->y,
1680 src->width, src->height, x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1683 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1687 static void xrender_put_image( Pixmap src_pixmap, Picture src_pict, Picture mask_pict, HRGN clip,
1688 XRenderPictFormat *dst_format, struct xrender_physdev *physdev,
1689 Drawable drawable, struct bitblt_coords *src,
1690 struct bitblt_coords *dst, BOOL use_repeat )
1692 int x_dst, y_dst;
1693 Picture dst_pict;
1694 double xscale, yscale;
1696 if (drawable) /* using an intermediate pixmap */
1698 RGNDATA *clip_data = NULL;
1700 if (clip) clip_data = X11DRV_GetRegionData( clip, 0 );
1701 x_dst = dst->x;
1702 y_dst = dst->y;
1703 dst_pict = pXRenderCreatePicture( gdi_display, drawable, dst_format, 0, NULL );
1704 if (clip_data)
1705 pXRenderSetPictureClipRectangles( gdi_display, dst_pict, 0, 0,
1706 (XRectangle *)clip_data->Buffer, clip_data->rdh.nCount );
1707 HeapFree( GetProcessHeap(), 0, clip_data );
1709 else
1711 x_dst = physdev->x11dev->dc_rect.left + dst->x;
1712 y_dst = physdev->x11dev->dc_rect.top + dst->y;
1713 dst_pict = get_xrender_picture( physdev, clip, &dst->visrect );
1716 if (!use_repeat)
1718 xscale = src->width / (double)dst->width;
1719 yscale = src->height / (double)dst->height;
1721 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1723 xrender_blit( PictOpSrc, src_pict, mask_pict, dst_pict, src->x, src->y, src->width, src->height,
1724 x_dst, y_dst, dst->width, dst->height, xscale, yscale );
1726 if (drawable) pXRenderFreePicture( gdi_display, dst_pict );
1730 /***********************************************************************
1731 * xrenderdrv_StretchBlt
1733 static BOOL xrenderdrv_StretchBlt( PHYSDEV dst_dev, struct bitblt_coords *dst,
1734 PHYSDEV src_dev, struct bitblt_coords *src, DWORD rop )
1736 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1737 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1738 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
1740 if (src_dev->funcs != dst_dev->funcs)
1742 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pStretchBlt );
1743 return dst_dev->funcs->pStretchBlt( dst_dev, dst, src_dev, src, rop );
1746 /* XRender is of no use for color -> mono */
1747 if (physdev_dst->format == WXR_FORMAT_MONO && physdev_src->format != WXR_FORMAT_MONO)
1748 goto x11drv_fallback;
1750 /* if not stretching, we only need to handle format conversion */
1751 if (!stretch && physdev_dst->format == physdev_src->format) goto x11drv_fallback;
1753 if (rop != SRCCOPY)
1755 GC tmpGC;
1756 Pixmap tmp_pixmap;
1757 struct bitblt_coords tmp;
1759 /* make coordinates relative to tmp pixmap */
1760 tmp = *dst;
1761 tmp.x -= tmp.visrect.left;
1762 tmp.y -= tmp.visrect.top;
1763 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1765 tmpGC = XCreateGC( gdi_display, physdev_dst->x11dev->drawable, 0, NULL );
1766 XSetSubwindowMode( gdi_display, tmpGC, IncludeInferiors );
1767 XSetGraphicsExposures( gdi_display, tmpGC, False );
1768 tmp_pixmap = XCreatePixmap( gdi_display, root_window, tmp.visrect.right - tmp.visrect.left,
1769 tmp.visrect.bottom - tmp.visrect.top, physdev_dst->pict_format->depth );
1771 xrender_stretch_blit( physdev_src, physdev_dst, tmp_pixmap, src, &tmp );
1772 execute_rop( physdev_dst->x11dev, tmp_pixmap, tmpGC, &dst->visrect, rop );
1774 XFreePixmap( gdi_display, tmp_pixmap );
1775 XFreeGC( gdi_display, tmpGC );
1777 else xrender_stretch_blit( physdev_src, physdev_dst, 0, src, dst );
1779 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
1780 return TRUE;
1782 x11drv_fallback:
1783 return X11DRV_StretchBlt( &physdev_dst->x11dev->dev, dst, &physdev_src->x11dev->dev, src, rop );
1787 /***********************************************************************
1788 * xrenderdrv_PutImage
1790 static DWORD xrenderdrv_PutImage( PHYSDEV dev, HRGN clip, BITMAPINFO *info,
1791 const struct gdi_image_bits *bits, struct bitblt_coords *src,
1792 struct bitblt_coords *dst, DWORD rop )
1794 struct xrender_physdev *physdev = get_xrender_dev( dev );
1795 DWORD ret;
1796 Pixmap tmp_pixmap;
1797 GC gc;
1798 enum wxr_format src_format, dst_format;
1799 XRenderPictFormat *pict_format;
1800 Pixmap src_pixmap;
1801 Picture src_pict, mask_pict = 0;
1802 BOOL use_repeat;
1804 dst_format = physdev->format;
1805 src_format = get_xrender_format_from_bitmapinfo( info );
1806 if (!(pict_format = pict_formats[src_format])) goto update_format;
1808 /* make sure we can create an image with the same bpp */
1809 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1810 goto update_format;
1812 /* mono <-> color conversions not supported */
1813 if ((src_format != dst_format) && (src_format == WXR_FORMAT_MONO || dst_format == WXR_FORMAT_MONO))
1814 goto x11drv_fallback;
1816 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1818 if (!has_alpha( src_format ) && has_alpha( dst_format )) mask_pict = get_no_alpha_mask();
1820 ret = create_image_pixmap( info, bits, src, src_format, &src_pixmap, &src_pict, &use_repeat );
1821 if (!ret)
1823 struct bitblt_coords tmp;
1825 if (rop != SRCCOPY)
1827 BOOL restore_region = add_extra_clipping_region( physdev->x11dev, clip );
1829 /* make coordinates relative to tmp pixmap */
1830 tmp = *dst;
1831 tmp.x -= tmp.visrect.left;
1832 tmp.y -= tmp.visrect.top;
1833 OffsetRect( &tmp.visrect, -tmp.visrect.left, -tmp.visrect.top );
1835 gc = XCreateGC( gdi_display, physdev->x11dev->drawable, 0, NULL );
1836 XSetSubwindowMode( gdi_display, gc, IncludeInferiors );
1837 XSetGraphicsExposures( gdi_display, gc, False );
1838 tmp_pixmap = XCreatePixmap( gdi_display, root_window,
1839 tmp.visrect.right - tmp.visrect.left,
1840 tmp.visrect.bottom - tmp.visrect.top,
1841 physdev->pict_format->depth );
1843 xrender_put_image( src_pixmap, src_pict, mask_pict, NULL, physdev->pict_format,
1844 NULL, tmp_pixmap, src, &tmp, use_repeat );
1845 execute_rop( physdev->x11dev, tmp_pixmap, gc, &dst->visrect, rop );
1847 XFreePixmap( gdi_display, tmp_pixmap );
1848 XFreeGC( gdi_display, gc );
1849 if (restore_region) restore_clipping_region( physdev->x11dev );
1851 else xrender_put_image( src_pixmap, src_pict, mask_pict, clip,
1852 physdev->pict_format, physdev, 0, src, dst, use_repeat );
1854 add_device_bounds( physdev->x11dev, &dst->visrect );
1856 pXRenderFreePicture( gdi_display, src_pict );
1857 XFreePixmap( gdi_display, src_pixmap );
1859 return ret;
1861 update_format:
1862 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1863 set_color_info( pict_formats[dst_format], info );
1864 return ERROR_BAD_FORMAT;
1866 x11drv_fallback:
1867 dev = GET_NEXT_PHYSDEV( dev, pPutImage );
1868 return dev->funcs->pPutImage( dev, clip, info, bits, src, dst, rop );
1872 /***********************************************************************
1873 * xrenderdrv_BlendImage
1875 static DWORD xrenderdrv_BlendImage( PHYSDEV dev, BITMAPINFO *info, const struct gdi_image_bits *bits,
1876 struct bitblt_coords *src, struct bitblt_coords *dst,
1877 BLENDFUNCTION func )
1879 struct xrender_physdev *physdev = get_xrender_dev( dev );
1880 DWORD ret;
1881 enum wxr_format format;
1882 XRenderPictFormat *pict_format;
1883 Picture dst_pict, src_pict, mask_pict;
1884 Pixmap src_pixmap;
1885 BOOL use_repeat;
1887 format = get_xrender_format_from_bitmapinfo( info );
1888 if (!(func.AlphaFormat & AC_SRC_ALPHA))
1889 format = get_format_without_alpha( format );
1890 else if (format != WXR_FORMAT_A8R8G8B8 || info->bmiHeader.biCompression != BI_RGB)
1891 return ERROR_INVALID_PARAMETER;
1893 if (!(pict_format = pict_formats[format])) goto update_format;
1895 /* make sure we can create an image with the same bpp */
1896 if (info->bmiHeader.biBitCount != pixmap_formats[pict_format->depth]->bits_per_pixel)
1897 goto update_format;
1899 if (format == WXR_FORMAT_MONO && physdev->format != WXR_FORMAT_MONO)
1900 goto update_format;
1902 if (!bits) return ERROR_SUCCESS; /* just querying the format */
1904 ret = create_image_pixmap( info, bits, src, format, &src_pixmap, &src_pict, &use_repeat );
1905 if (!ret)
1907 double xscale, yscale;
1909 if (!use_repeat)
1911 xscale = src->width / (double)dst->width;
1912 yscale = src->height / (double)dst->height;
1914 else xscale = yscale = 1; /* no scaling needed with a repeating source */
1916 dst_pict = get_xrender_picture( physdev, 0, &dst->visrect );
1918 EnterCriticalSection( &xrender_cs );
1919 mask_pict = get_mask_pict( func.SourceConstantAlpha * 257 );
1921 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
1922 src->x, src->y, src->width, src->height,
1923 physdev->x11dev->dc_rect.left + dst->x,
1924 physdev->x11dev->dc_rect.top + dst->y,
1925 dst->width, dst->height, xscale, yscale );
1927 pXRenderFreePicture( gdi_display, src_pict );
1928 XFreePixmap( gdi_display, src_pixmap );
1930 LeaveCriticalSection( &xrender_cs );
1931 add_device_bounds( physdev->x11dev, &dst->visrect );
1933 return ret;
1935 update_format:
1936 if (info->bmiHeader.biHeight > 0) info->bmiHeader.biHeight = -info->bmiHeader.biHeight;
1937 set_color_info( physdev->pict_format, info );
1938 return ERROR_BAD_FORMAT;
1942 /***********************************************************************
1943 * xrenderdrv_AlphaBlend
1945 static BOOL xrenderdrv_AlphaBlend( PHYSDEV dst_dev, struct bitblt_coords *dst,
1946 PHYSDEV src_dev, struct bitblt_coords *src, BLENDFUNCTION blendfn )
1948 struct xrender_physdev *physdev_dst = get_xrender_dev( dst_dev );
1949 struct xrender_physdev *physdev_src = get_xrender_dev( src_dev );
1950 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
1951 XRenderPictureAttributes pa;
1952 Pixmap tmp_pixmap = 0;
1953 double xscale, yscale;
1955 if (src_dev->funcs != dst_dev->funcs)
1957 dst_dev = GET_NEXT_PHYSDEV( dst_dev, pAlphaBlend );
1958 return dst_dev->funcs->pAlphaBlend( dst_dev, dst, src_dev, src, blendfn );
1961 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->format != WXR_FORMAT_A8R8G8B8)
1963 SetLastError( ERROR_INVALID_PARAMETER );
1964 return FALSE;
1967 dst_pict = get_xrender_picture( physdev_dst, 0, &dst->visrect );
1969 xscale = src->width / (double)dst->width;
1970 yscale = src->height / (double)dst->height;
1972 src_pict = get_xrender_picture_source( physdev_src, FALSE );
1974 if (physdev_src->format == WXR_FORMAT_MONO && physdev_dst->format != WXR_FORMAT_MONO)
1976 /* mono -> color blending needs an intermediate color pixmap */
1977 XRenderColor fg, bg;
1978 int width = src->visrect.right - src->visrect.left;
1979 int height = src->visrect.bottom - src->visrect.top;
1981 /* blending doesn't use the destination DC colors */
1982 fg.red = fg.green = fg.blue = 0;
1983 bg.red = bg.green = bg.blue = 0xffff;
1984 fg.alpha = bg.alpha = 0xffff;
1986 tmp_pixmap = XCreatePixmap( gdi_display, root_window, width, height,
1987 physdev_dst->pict_format->depth );
1988 tmp_pict = pXRenderCreatePicture( gdi_display, tmp_pixmap, physdev_dst->pict_format, 0, NULL );
1990 xrender_mono_blit( src_pict, tmp_pict, physdev_dst->format, &fg, &bg,
1991 src->visrect.left, src->visrect.top, width, height, 0, 0, width, height, 1, 1 );
1993 else if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && physdev_src->pict_format)
1995 /* we need a source picture with no alpha */
1996 enum wxr_format format = get_format_without_alpha( physdev_src->format );
1997 if (format != physdev_src->format)
1999 pa.subwindow_mode = IncludeInferiors;
2000 tmp_pict = pXRenderCreatePicture( gdi_display, physdev_src->x11dev->drawable,
2001 pict_formats[format], CPSubwindowMode, &pa );
2005 if (tmp_pict) src_pict = tmp_pict;
2007 EnterCriticalSection( &xrender_cs );
2008 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2010 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2011 physdev_src->x11dev->dc_rect.left + src->x,
2012 physdev_src->x11dev->dc_rect.top + src->y,
2013 src->width, src->height,
2014 physdev_dst->x11dev->dc_rect.left + dst->x,
2015 physdev_dst->x11dev->dc_rect.top + dst->y,
2016 dst->width, dst->height, xscale, yscale );
2018 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2019 if (tmp_pixmap) XFreePixmap( gdi_display, tmp_pixmap );
2021 LeaveCriticalSection( &xrender_cs );
2022 add_device_bounds( physdev_dst->x11dev, &dst->visrect );
2023 return TRUE;
2026 /***********************************************************************
2027 * xrenderdrv_GradientFill
2029 static BOOL xrenderdrv_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
2030 void * grad_array, ULONG ngrad, ULONG mode )
2032 #ifdef HAVE_XRENDERCREATELINEARGRADIENT
2033 static const XFixed stops[2] = { 0, 1 << 16 };
2034 struct xrender_physdev *physdev = get_xrender_dev( dev );
2035 XLinearGradient gradient;
2036 XRenderColor colors[2];
2037 Picture src_pict, dst_pict;
2038 unsigned int i;
2039 const GRADIENT_RECT *rect = grad_array;
2040 RECT rc;
2041 POINT pt[2];
2043 if (!pXRenderCreateLinearGradient) goto fallback;
2045 /* <= 16-bpp uses dithering */
2046 if (!physdev->pict_format || physdev->pict_format->depth <= 16) goto fallback;
2048 switch (mode)
2050 case GRADIENT_FILL_RECT_H:
2051 case GRADIENT_FILL_RECT_V:
2052 for (i = 0; i < ngrad; i++, rect++)
2054 const TRIVERTEX *v1 = vert_array + rect->UpperLeft;
2055 const TRIVERTEX *v2 = vert_array + rect->LowerRight;
2057 colors[0].red = v1->Red * 257 / 256;
2058 colors[0].green = v1->Green * 257 / 256;
2059 colors[0].blue = v1->Blue * 257 / 256;
2060 colors[1].red = v2->Red * 257 / 256;
2061 colors[1].green = v2->Green * 257 / 256;
2062 colors[1].blue = v2->Blue * 257 / 256;
2063 /* always ignore alpha since otherwise xrender will want to pre-multiply the colors */
2064 colors[0].alpha = colors[1].alpha = 65535;
2066 pt[0].x = v1->x;
2067 pt[0].y = v1->y;
2068 pt[1].x = v2->x;
2069 pt[1].y = v2->y;
2070 LPtoDP( dev->hdc, pt, 2 );
2071 if (mode == GRADIENT_FILL_RECT_H)
2073 gradient.p1.y = gradient.p2.y = 0;
2074 if (pt[1].x > pt[0].x)
2076 gradient.p1.x = 0;
2077 gradient.p2.x = (pt[1].x - pt[0].x) << 16;
2079 else
2081 gradient.p1.x = (pt[0].x - pt[1].x) << 16;
2082 gradient.p2.x = 0;
2085 else
2087 gradient.p1.x = gradient.p2.x = 0;
2088 if (pt[1].y > pt[0].y)
2090 gradient.p1.y = 0;
2091 gradient.p2.y = (pt[1].y - pt[0].y) << 16;
2093 else
2095 gradient.p1.y = (pt[0].y - pt[1].y) << 16;
2096 gradient.p2.y = 0;
2100 rc.left = min( pt[0].x, pt[1].x );
2101 rc.top = min( pt[0].y, pt[1].y );
2102 rc.right = max( pt[0].x, pt[1].x );
2103 rc.bottom = max( pt[0].y, pt[1].y );
2105 TRACE( "%u gradient %s colors %04x,%04x,%04x,%04x -> %04x,%04x,%04x,%04x\n",
2106 mode, wine_dbgstr_rect( &rc ),
2107 colors[0].red, colors[0].green, colors[0].blue, colors[0].alpha,
2108 colors[1].red, colors[1].green, colors[1].blue, colors[1].alpha );
2110 dst_pict = get_xrender_picture( physdev, 0, NULL );
2112 src_pict = pXRenderCreateLinearGradient( gdi_display, &gradient, stops, colors, 2 );
2113 xrender_blit( PictOpSrc, src_pict, 0, dst_pict,
2114 0, 0, rc.right - rc.left, rc.bottom - rc.top,
2115 physdev->x11dev->dc_rect.left + rc.left,
2116 physdev->x11dev->dc_rect.top + rc.top,
2117 rc.right - rc.left, rc.bottom - rc.top, 1, 1 );
2118 pXRenderFreePicture( gdi_display, src_pict );
2119 add_device_bounds( physdev->x11dev, &rc );
2121 return TRUE;
2124 fallback:
2125 #endif
2126 dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
2127 return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
2130 /***********************************************************************
2131 * xrenderdrv_SelectBrush
2133 static HBRUSH xrenderdrv_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
2135 struct xrender_physdev *physdev = get_xrender_dev( dev );
2136 Pixmap pixmap;
2137 XVisualInfo vis = default_visual;
2138 XRenderPictFormat *format = physdev->pict_format;
2140 if (!pattern) goto x11drv_fallback;
2141 if (pattern->info->bmiHeader.biBitCount == 1) goto x11drv_fallback;
2142 if (physdev->format == WXR_FORMAT_MONO) goto x11drv_fallback;
2144 vis.depth = format->depth;
2145 vis.red_mask = format->direct.redMask << format->direct.red;
2146 vis.green_mask = format->direct.greenMask << format->direct.green;
2147 vis.blue_mask = format->direct.blueMask << format->direct.blue;
2149 pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, pattern->info,
2150 &pattern->bits, pattern->usage );
2151 if (!pixmap) return 0;
2153 if (physdev->x11dev->brush.pixmap) XFreePixmap( gdi_display, physdev->x11dev->brush.pixmap );
2154 physdev->x11dev->brush.pixmap = pixmap;
2155 physdev->x11dev->brush.fillStyle = FillTiled;
2156 physdev->x11dev->brush.pixel = 0; /* ignored */
2157 physdev->x11dev->brush.style = BS_PATTERN;
2158 return hbrush;
2160 x11drv_fallback:
2161 dev = GET_NEXT_PHYSDEV( dev, pSelectBrush );
2162 return dev->funcs->pSelectBrush( dev, hbrush, pattern );
2166 static const struct gdi_dc_funcs xrender_funcs =
2168 NULL, /* pAbortDoc */
2169 NULL, /* pAbortPath */
2170 xrenderdrv_AlphaBlend, /* pAlphaBlend */
2171 NULL, /* pAngleArc */
2172 NULL, /* pArc */
2173 NULL, /* pArcTo */
2174 NULL, /* pBeginPath */
2175 xrenderdrv_BlendImage, /* pBlendImage */
2176 NULL, /* pChord */
2177 NULL, /* pCloseFigure */
2178 xrenderdrv_CreateCompatibleDC, /* pCreateCompatibleDC */
2179 xrenderdrv_CreateDC, /* pCreateDC */
2180 xrenderdrv_DeleteDC, /* pDeleteDC */
2181 NULL, /* pDeleteObject */
2182 NULL, /* pDeviceCapabilities */
2183 NULL, /* pEllipse */
2184 NULL, /* pEndDoc */
2185 NULL, /* pEndPage */
2186 NULL, /* pEndPath */
2187 NULL, /* pEnumFonts */
2188 NULL, /* pEnumICMProfiles */
2189 NULL, /* pExcludeClipRect */
2190 NULL, /* pExtDeviceMode */
2191 xrenderdrv_ExtEscape, /* pExtEscape */
2192 NULL, /* pExtFloodFill */
2193 NULL, /* pExtSelectClipRgn */
2194 xrenderdrv_ExtTextOut, /* pExtTextOut */
2195 NULL, /* pFillPath */
2196 NULL, /* pFillRgn */
2197 NULL, /* pFlattenPath */
2198 NULL, /* pFontIsLinked */
2199 NULL, /* pFrameRgn */
2200 NULL, /* pGdiComment */
2201 NULL, /* pGdiRealizationInfo */
2202 NULL, /* pGetBoundsRect */
2203 NULL, /* pGetCharABCWidths */
2204 NULL, /* pGetCharABCWidthsI */
2205 NULL, /* pGetCharWidth */
2206 NULL, /* pGetDeviceCaps */
2207 NULL, /* pGetDeviceGammaRamp */
2208 NULL, /* pGetFontData */
2209 NULL, /* pGetFontUnicodeRanges */
2210 NULL, /* pGetGlyphIndices */
2211 NULL, /* pGetGlyphOutline */
2212 NULL, /* pGetICMProfile */
2213 NULL, /* pGetImage */
2214 NULL, /* pGetKerningPairs */
2215 NULL, /* pGetNearestColor */
2216 NULL, /* pGetOutlineTextMetrics */
2217 NULL, /* pGetPixel */
2218 NULL, /* pGetSystemPaletteEntries */
2219 NULL, /* pGetTextCharsetInfo */
2220 NULL, /* pGetTextExtentExPoint */
2221 NULL, /* pGetTextExtentExPointI */
2222 NULL, /* pGetTextFace */
2223 NULL, /* pGetTextMetrics */
2224 xrenderdrv_GradientFill, /* pGradientFill */
2225 NULL, /* pIntersectClipRect */
2226 NULL, /* pInvertRgn */
2227 NULL, /* pLineTo */
2228 NULL, /* pModifyWorldTransform */
2229 NULL, /* pMoveTo */
2230 NULL, /* pOffsetClipRgn */
2231 NULL, /* pOffsetViewportOrg */
2232 NULL, /* pOffsetWindowOrg */
2233 NULL, /* pPaintRgn */
2234 NULL, /* pPatBlt */
2235 NULL, /* pPie */
2236 NULL, /* pPolyBezier */
2237 NULL, /* pPolyBezierTo */
2238 NULL, /* pPolyDraw */
2239 NULL, /* pPolyPolygon */
2240 NULL, /* pPolyPolyline */
2241 NULL, /* pPolygon */
2242 NULL, /* pPolyline */
2243 NULL, /* pPolylineTo */
2244 xrenderdrv_PutImage, /* pPutImage */
2245 NULL, /* pRealizeDefaultPalette */
2246 NULL, /* pRealizePalette */
2247 NULL, /* pRectangle */
2248 NULL, /* pResetDC */
2249 NULL, /* pRestoreDC */
2250 NULL, /* pRoundRect */
2251 NULL, /* pSaveDC */
2252 NULL, /* pScaleViewportExt */
2253 NULL, /* pScaleWindowExt */
2254 NULL, /* pSelectBitmap */
2255 xrenderdrv_SelectBrush, /* pSelectBrush */
2256 NULL, /* pSelectClipPath */
2257 xrenderdrv_SelectFont, /* pSelectFont */
2258 NULL, /* pSelectPalette */
2259 NULL, /* pSelectPen */
2260 NULL, /* pSetArcDirection */
2261 NULL, /* pSetBkColor */
2262 NULL, /* pSetBkMode */
2263 NULL, /* pSetBoundsRect */
2264 NULL, /* pSetDCBrushColor */
2265 NULL, /* pSetDCPenColor */
2266 NULL, /* pSetDIBitsToDevice */
2267 xrenderdrv_SetDeviceClipping, /* pSetDeviceClipping */
2268 NULL, /* pSetDeviceGammaRamp */
2269 NULL, /* pSetLayout */
2270 NULL, /* pSetMapMode */
2271 NULL, /* pSetMapperFlags */
2272 NULL, /* pSetPixel */
2273 NULL, /* pSetPolyFillMode */
2274 NULL, /* pSetROP2 */
2275 NULL, /* pSetRelAbs */
2276 NULL, /* pSetStretchBltMode */
2277 NULL, /* pSetTextAlign */
2278 NULL, /* pSetTextCharacterExtra */
2279 NULL, /* pSetTextColor */
2280 NULL, /* pSetTextJustification */
2281 NULL, /* pSetViewportExt */
2282 NULL, /* pSetViewportOrg */
2283 NULL, /* pSetWindowExt */
2284 NULL, /* pSetWindowOrg */
2285 NULL, /* pSetWorldTransform */
2286 NULL, /* pStartDoc */
2287 NULL, /* pStartPage */
2288 xrenderdrv_StretchBlt, /* pStretchBlt */
2289 NULL, /* pStretchDIBits */
2290 NULL, /* pStrokeAndFillPath */
2291 NULL, /* pStrokePath */
2292 NULL, /* pUnrealizePalette */
2293 NULL, /* pWidenPath */
2294 NULL, /* wine_get_wgl_driver */
2295 GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */
2298 #else /* SONAME_LIBXRENDER */
2300 const struct gdi_dc_funcs *X11DRV_XRender_Init(void)
2302 TRACE("XRender support not compiled in.\n");
2303 return NULL;
2306 #endif /* SONAME_LIBXRENDER */