ole32: Add support for rendering HBITMAP clipboard objects.
[wine.git] / dlls / winex11.drv / xrender.c
blobfde0b91ce920d5eb3293082c52114f149dcfa96a
1 /*
2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
5 * Copyright 2009 Roderick Colenbrander
7 * Some parts also:
8 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <stdlib.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "x11drv.h"
35 #include "winternl.h"
36 #include "wine/library.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 int using_client_side_fonts = FALSE;
42 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
44 #ifdef SONAME_LIBXRENDER
46 static BOOL X11DRV_XRender_Installed = FALSE;
48 #include <X11/Xlib.h>
49 #include <X11/extensions/Xrender.h>
51 #ifndef RepeatNone /* added in 0.10 */
52 #define RepeatNone 0
53 #define RepeatNormal 1
54 #define RepeatPad 2
55 #define RepeatReflect 3
56 #endif
58 typedef enum wine_xrformat
60 WXR_FORMAT_MONO,
61 WXR_FORMAT_GRAY,
62 WXR_FORMAT_X1R5G5B5,
63 WXR_FORMAT_X1B5G5R5,
64 WXR_FORMAT_R5G6B5,
65 WXR_FORMAT_B5G6R5,
66 WXR_FORMAT_R8G8B8,
67 WXR_FORMAT_B8G8R8,
68 WXR_FORMAT_A8R8G8B8,
69 WXR_FORMAT_B8G8R8A8,
70 WXR_FORMAT_X8R8G8B8,
71 WXR_FORMAT_B8G8R8X8,
72 WXR_NB_FORMATS
73 } WXRFormat;
75 typedef struct wine_xrender_format_template
77 WXRFormat wxr_format;
78 unsigned int depth;
79 unsigned int alpha;
80 unsigned int alphaMask;
81 unsigned int red;
82 unsigned int redMask;
83 unsigned int green;
84 unsigned int greenMask;
85 unsigned int blue;
86 unsigned int blueMask;
87 } WineXRenderFormatTemplate;
89 static const WineXRenderFormatTemplate wxr_formats_template[WXR_NB_FORMATS] =
91 /* Format depth alpha mask red mask green mask blue mask*/
92 {WXR_FORMAT_MONO, 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
93 {WXR_FORMAT_GRAY, 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
94 {WXR_FORMAT_X1R5G5B5, 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
95 {WXR_FORMAT_X1B5G5R5, 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
96 {WXR_FORMAT_R5G6B5, 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
97 {WXR_FORMAT_B5G6R5, 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
98 {WXR_FORMAT_R8G8B8, 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
99 {WXR_FORMAT_B8G8R8, 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
100 {WXR_FORMAT_A8R8G8B8, 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
101 {WXR_FORMAT_B8G8R8A8, 32, 0, 0xff, 8, 0xff, 16, 0xff, 24, 0xff },
102 {WXR_FORMAT_X8R8G8B8, 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
103 {WXR_FORMAT_B8G8R8X8, 32, 0, 0, 8, 0xff, 16, 0xff, 24, 0xff },
106 typedef struct wine_xrender_format
108 WXRFormat format;
109 XRenderPictFormat *pict_format;
110 } WineXRenderFormat;
112 static WineXRenderFormat wxr_formats[WXR_NB_FORMATS];
113 static int WineXRenderFormatsListSize = 0;
114 static WineXRenderFormat *default_format = NULL;
116 typedef struct
118 LOGFONTW lf;
119 XFORM xform;
120 SIZE devsize; /* size in device coords */
121 DWORD hash;
122 } LFANDSIZE;
124 #define INITIAL_REALIZED_BUF_SIZE 128
126 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
128 typedef struct
130 GlyphSet glyphset;
131 const WineXRenderFormat *font_format;
132 int nrealized;
133 BOOL *realized;
134 void **bitmaps;
135 XGlyphInfo *gis;
136 } gsCacheEntryFormat;
138 typedef struct
140 LFANDSIZE lfsz;
141 AA_Type aa_default;
142 gsCacheEntryFormat * format[AA_MAXVALUE];
143 INT count;
144 INT next;
145 } gsCacheEntry;
147 struct xrender_info
149 int cache_index;
150 Picture pict;
151 Picture pict_src;
152 const WineXRenderFormat *format;
155 static gsCacheEntry *glyphsetCache = NULL;
156 static DWORD glyphsetCacheSize = 0;
157 static INT lastfree = -1;
158 static INT mru = -1;
160 #define INIT_CACHE_SIZE 10
162 static int antialias = 1;
164 static void *xrender_handle;
166 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
167 MAKE_FUNCPTR(XRenderAddGlyphs)
168 MAKE_FUNCPTR(XRenderComposite)
169 MAKE_FUNCPTR(XRenderCompositeString8)
170 MAKE_FUNCPTR(XRenderCompositeString16)
171 MAKE_FUNCPTR(XRenderCompositeString32)
172 MAKE_FUNCPTR(XRenderCompositeText16)
173 MAKE_FUNCPTR(XRenderCreateGlyphSet)
174 MAKE_FUNCPTR(XRenderCreatePicture)
175 MAKE_FUNCPTR(XRenderFillRectangle)
176 MAKE_FUNCPTR(XRenderFindFormat)
177 MAKE_FUNCPTR(XRenderFindVisualFormat)
178 MAKE_FUNCPTR(XRenderFreeGlyphSet)
179 MAKE_FUNCPTR(XRenderFreePicture)
180 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
181 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
182 MAKE_FUNCPTR(XRenderSetPictureTransform)
183 #endif
184 MAKE_FUNCPTR(XRenderQueryExtension)
186 #ifdef SONAME_LIBFONTCONFIG
187 #include <fontconfig/fontconfig.h>
188 MAKE_FUNCPTR(FcConfigSubstitute)
189 MAKE_FUNCPTR(FcDefaultSubstitute)
190 MAKE_FUNCPTR(FcFontMatch)
191 MAKE_FUNCPTR(FcInit)
192 MAKE_FUNCPTR(FcPatternCreate)
193 MAKE_FUNCPTR(FcPatternDestroy)
194 MAKE_FUNCPTR(FcPatternAddInteger)
195 MAKE_FUNCPTR(FcPatternAddString)
196 MAKE_FUNCPTR(FcPatternGetBool)
197 MAKE_FUNCPTR(FcPatternGetInteger)
198 MAKE_FUNCPTR(FcPatternGetString)
199 static void *fontconfig_handle;
200 static BOOL fontconfig_installed;
201 #endif
203 #undef MAKE_FUNCPTR
205 static CRITICAL_SECTION xrender_cs;
206 static CRITICAL_SECTION_DEBUG critsect_debug =
208 0, 0, &xrender_cs,
209 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
210 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
212 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
214 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
215 ( ( (ULONG)_x4 << 24 ) | \
216 ( (ULONG)_x3 << 16 ) | \
217 ( (ULONG)_x2 << 8 ) | \
218 (ULONG)_x1 )
220 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
222 #define GASP_GRIDFIT 0x01
223 #define GASP_DOGRAY 0x02
225 #ifdef WORDS_BIGENDIAN
226 #define get_be_word(x) (x)
227 #define NATIVE_BYTE_ORDER MSBFirst
228 #else
229 #define get_be_word(x) RtlUshortByteSwap(x)
230 #define NATIVE_BYTE_ORDER LSBFirst
231 #endif
233 static WXRFormat get_format_without_alpha( WXRFormat format )
235 switch (format)
237 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
238 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
239 default: return format;
243 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
245 templ->id = 0;
246 templ->type = PictTypeDirect;
247 templ->depth = fmt->depth;
248 templ->direct.alpha = fmt->alpha;
249 templ->direct.alphaMask = fmt->alphaMask;
250 templ->direct.red = fmt->red;
251 templ->direct.redMask = fmt->redMask;
252 templ->direct.green = fmt->green;
253 templ->direct.greenMask = fmt->greenMask;
254 templ->direct.blue = fmt->blue;
255 templ->direct.blueMask = fmt->blueMask;
256 templ->colormap = 0;
258 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
260 return TRUE;
263 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
265 if(fmt->depth != screen_depth)
266 return FALSE;
267 if( (fmt->redMask << fmt->red) != visual->red_mask)
268 return FALSE;
269 if( (fmt->greenMask << fmt->green) != visual->green_mask)
270 return FALSE;
271 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
272 return FALSE;
274 /* We never select a default ARGB visual */
275 if(fmt->alphaMask)
276 return FALSE;
278 return TRUE;
281 static int load_xrender_formats(void)
283 unsigned int i;
284 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
286 XRenderPictFormat templ, *pict_format;
288 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
290 wine_tsx11_lock();
291 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
292 if(!pict_format)
294 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
295 if (visual->class == DirectColor)
297 XVisualInfo info;
298 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
299 screen_depth, TrueColor, &info ))
301 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
302 if (pict_format) visual = info.visual;
306 wine_tsx11_unlock();
308 if(pict_format)
310 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
311 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
312 default_format = &wxr_formats[WineXRenderFormatsListSize];
313 WineXRenderFormatsListSize++;
314 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
317 else
319 unsigned long mask = 0;
320 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
322 wine_tsx11_lock();
323 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
324 wine_tsx11_unlock();
326 if(pict_format)
328 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
329 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
330 WineXRenderFormatsListSize++;
331 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
335 return WineXRenderFormatsListSize;
338 /***********************************************************************
339 * X11DRV_XRender_Init
341 * Let's see if our XServer has the extension available
344 void X11DRV_XRender_Init(void)
346 int event_base, i;
348 if (client_side_with_render &&
349 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
350 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
351 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
354 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
355 LOAD_FUNCPTR(XRenderAddGlyphs)
356 LOAD_FUNCPTR(XRenderComposite)
357 LOAD_FUNCPTR(XRenderCompositeString8)
358 LOAD_FUNCPTR(XRenderCompositeString16)
359 LOAD_FUNCPTR(XRenderCompositeString32)
360 LOAD_FUNCPTR(XRenderCompositeText16)
361 LOAD_FUNCPTR(XRenderCreateGlyphSet)
362 LOAD_FUNCPTR(XRenderCreatePicture)
363 LOAD_FUNCPTR(XRenderFillRectangle)
364 LOAD_FUNCPTR(XRenderFindFormat)
365 LOAD_FUNCPTR(XRenderFindVisualFormat)
366 LOAD_FUNCPTR(XRenderFreeGlyphSet)
367 LOAD_FUNCPTR(XRenderFreePicture)
368 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
369 LOAD_FUNCPTR(XRenderQueryExtension)
370 #undef LOAD_FUNCPTR
371 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
372 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
373 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
374 #undef LOAD_OPTIONAL_FUNCPTR
375 #endif
377 wine_tsx11_lock();
378 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
379 wine_tsx11_unlock();
380 if(X11DRV_XRender_Installed) {
381 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
382 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
384 wine_tsx11_unlock();
385 WINE_MESSAGE(
386 "Wine has detected that you probably have a buggy version\n"
387 "of libXrender.so . Because of this client side font rendering\n"
388 "will be disabled. Please upgrade this library.\n");
389 X11DRV_XRender_Installed = FALSE;
390 return;
393 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
394 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
395 X11DRV_XRender_Installed = FALSE;
400 #ifdef SONAME_LIBFONTCONFIG
401 if ((fontconfig_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0)))
403 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(fontconfig_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
404 LOAD_FUNCPTR(FcConfigSubstitute);
405 LOAD_FUNCPTR(FcDefaultSubstitute);
406 LOAD_FUNCPTR(FcFontMatch);
407 LOAD_FUNCPTR(FcInit);
408 LOAD_FUNCPTR(FcPatternCreate);
409 LOAD_FUNCPTR(FcPatternDestroy);
410 LOAD_FUNCPTR(FcPatternAddInteger);
411 LOAD_FUNCPTR(FcPatternAddString);
412 LOAD_FUNCPTR(FcPatternGetBool);
413 LOAD_FUNCPTR(FcPatternGetInteger);
414 LOAD_FUNCPTR(FcPatternGetString);
415 #undef LOAD_FUNCPTR
416 fontconfig_installed = pFcInit();
418 else TRACE( "cannot find the fontconfig library " SONAME_LIBFONTCONFIG "\n" );
419 #endif
421 sym_not_found:
422 if(X11DRV_XRender_Installed || client_side_with_core)
424 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
425 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
427 glyphsetCacheSize = INIT_CACHE_SIZE;
428 lastfree = 0;
429 for(i = 0; i < INIT_CACHE_SIZE; i++) {
430 glyphsetCache[i].next = i + 1;
431 glyphsetCache[i].count = -1;
433 glyphsetCache[i-1].next = -1;
434 using_client_side_fonts = 1;
436 if(!X11DRV_XRender_Installed) {
437 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
438 if(screen_depth <= 8 || !client_side_antialias_with_core)
439 antialias = 0;
440 } else {
441 if(screen_depth <= 8 || !client_side_antialias_with_render)
442 antialias = 0;
445 else TRACE("Using X11 core fonts\n");
448 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
449 static void get_xrender_color(const WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
451 XRenderPictFormat *pf = wxr_format->pict_format;
453 if(pf->direct.redMask)
454 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
455 else
456 dst_color->red = 0;
458 if(pf->direct.greenMask)
459 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
460 else
461 dst_color->green = 0;
463 if(pf->direct.blueMask)
464 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
465 else
466 dst_color->blue = 0;
468 dst_color->alpha = 0xffff;
471 static const WineXRenderFormat *get_xrender_format(WXRFormat format)
473 int i;
474 for(i=0; i<WineXRenderFormatsListSize; i++)
476 if(wxr_formats[i].format == format)
478 TRACE("Returning wxr_format=%#x\n", format);
479 return &wxr_formats[i];
482 return NULL;
485 static const WineXRenderFormat *get_xrender_format_from_color_shifts(int depth, ColorShifts *shifts)
487 int redMask, greenMask, blueMask;
488 unsigned int i;
490 if(depth == 1)
491 return get_xrender_format(WXR_FORMAT_MONO);
493 /* physDevs of a depth <=8, don't have color_shifts set and XRender can't handle those except for 1-bit */
494 if(!shifts)
495 return default_format;
497 redMask = shifts->physicalRed.max << shifts->physicalRed.shift;
498 greenMask = shifts->physicalGreen.max << shifts->physicalGreen.shift;
499 blueMask = shifts->physicalBlue.max << shifts->physicalBlue.shift;
501 /* Try to locate a format which matches the specification of the dibsection. */
502 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
504 if( depth == wxr_formats_template[i].depth &&
505 redMask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
506 greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
507 blueMask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
510 /* When we reach this stage the format was found in our template table but this doesn't mean that
511 * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
513 return get_xrender_format(wxr_formats_template[i].wxr_format);
517 /* This should not happen because when we reach 'shifts' must have been set and we only allows shifts which are backed by X */
518 ERR("No XRender format found!\n");
519 return NULL;
522 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
523 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
525 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
526 XTransform xform = {{
527 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
528 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
529 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
532 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
533 #endif
536 /* check if we can use repeating instead of scaling for the specified source DC */
537 static BOOL use_source_repeat( X11DRV_PDEVICE *physDev )
539 return (physDev->bitmap &&
540 physDev->drawable_rect.right - physDev->drawable_rect.left == 1 &&
541 physDev->drawable_rect.bottom - physDev->drawable_rect.top == 1);
544 static struct xrender_info *get_xrender_info(X11DRV_PDEVICE *physDev)
546 if(!physDev->xrender)
548 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physDev->xrender));
550 if(!physDev->xrender)
552 ERR("Unable to allocate XRENDERINFO!\n");
553 return NULL;
555 physDev->xrender->cache_index = -1;
557 if (!physDev->xrender->format)
558 physDev->xrender->format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
560 return physDev->xrender;
563 static Picture get_xrender_picture(X11DRV_PDEVICE *physDev)
565 struct xrender_info *info = get_xrender_info(physDev);
566 if (!info) return 0;
568 if (!info->pict && info->format)
570 XRenderPictureAttributes pa;
571 RGNDATA *clip = X11DRV_GetRegionData( physDev->region, 0 );
573 wine_tsx11_lock();
574 pa.subwindow_mode = IncludeInferiors;
575 info->pict = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
576 CPSubwindowMode, &pa);
577 if (info->pict && clip)
578 pXRenderSetPictureClipRectangles( gdi_display, info->pict,
579 physDev->dc_rect.left, physDev->dc_rect.top,
580 (XRectangle *)clip->Buffer, clip->rdh.nCount );
581 wine_tsx11_unlock();
582 TRACE("Allocing pict=%lx dc=%p drawable=%08lx\n", info->pict, physDev->hdc, physDev->drawable);
583 HeapFree( GetProcessHeap(), 0, clip );
586 return info->pict;
589 static Picture get_xrender_picture_source(X11DRV_PDEVICE *physDev, BOOL repeat)
591 struct xrender_info *info = get_xrender_info(physDev);
592 if (!info) return 0;
594 if (!info->pict_src && info->format)
596 XRenderPictureAttributes pa;
598 wine_tsx11_lock();
599 pa.subwindow_mode = IncludeInferiors;
600 pa.repeat = repeat ? RepeatNormal : RepeatNone;
601 info->pict_src = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
602 CPSubwindowMode|CPRepeat, &pa);
603 wine_tsx11_unlock();
605 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
606 info->pict_src, physDev->hdc, physDev->drawable, pa.repeat);
609 return info->pict_src;
612 /* return a mask picture used to force alpha to 0 */
613 static Picture get_no_alpha_mask(void)
615 static Pixmap pixmap;
616 static Picture pict;
618 wine_tsx11_lock();
619 if (!pict)
621 const WineXRenderFormat *fmt = get_xrender_format( WXR_FORMAT_A8R8G8B8 );
622 XRenderPictureAttributes pa;
623 XRenderColor col;
625 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
626 pa.repeat = RepeatNormal;
627 pa.component_alpha = True;
628 pict = pXRenderCreatePicture( gdi_display, pixmap, fmt->pict_format,
629 CPRepeat|CPComponentAlpha, &pa );
630 col.red = col.green = col.blue = 0xffff;
631 col.alpha = 0;
632 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
634 wine_tsx11_unlock();
635 return pict;
638 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
640 if(p1->hash != p2->hash) return TRUE;
641 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
642 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
643 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
644 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
647 #if 0
648 static void walk_cache(void)
650 int i;
652 EnterCriticalSection(&xrender_cs);
653 for(i=mru; i >= 0; i = glyphsetCache[i].next)
654 TRACE("item %d\n", i);
655 LeaveCriticalSection(&xrender_cs);
657 #endif
659 static int LookupEntry(LFANDSIZE *plfsz)
661 int i, prev_i = -1;
663 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
664 TRACE("%d\n", i);
665 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
666 i = -1;
667 break;
670 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
671 glyphsetCache[i].count++;
672 if(prev_i >= 0) {
673 glyphsetCache[prev_i].next = glyphsetCache[i].next;
674 glyphsetCache[i].next = mru;
675 mru = i;
677 TRACE("found font in cache %d\n", i);
678 return i;
680 prev_i = i;
682 TRACE("font not in cache\n");
683 return -1;
686 static void FreeEntry(int entry)
688 int i, format;
690 for(format = 0; format < AA_MAXVALUE; format++) {
691 gsCacheEntryFormat * formatEntry;
693 if( !glyphsetCache[entry].format[format] )
694 continue;
696 formatEntry = glyphsetCache[entry].format[format];
698 if(formatEntry->glyphset) {
699 wine_tsx11_lock();
700 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
701 wine_tsx11_unlock();
702 formatEntry->glyphset = 0;
704 if(formatEntry->nrealized) {
705 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
706 formatEntry->realized = NULL;
707 if(formatEntry->bitmaps) {
708 for(i = 0; i < formatEntry->nrealized; i++)
709 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
710 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
711 formatEntry->bitmaps = NULL;
713 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
714 formatEntry->gis = NULL;
715 formatEntry->nrealized = 0;
718 HeapFree(GetProcessHeap(), 0, formatEntry);
719 glyphsetCache[entry].format[format] = NULL;
723 static int AllocEntry(void)
725 int best = -1, prev_best = -1, i, prev_i = -1;
727 if(lastfree >= 0) {
728 assert(glyphsetCache[lastfree].count == -1);
729 glyphsetCache[lastfree].count = 1;
730 best = lastfree;
731 lastfree = glyphsetCache[lastfree].next;
732 assert(best != mru);
733 glyphsetCache[best].next = mru;
734 mru = best;
736 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
737 return mru;
740 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
741 if(glyphsetCache[i].count == 0) {
742 best = i;
743 prev_best = prev_i;
745 prev_i = i;
748 if(best >= 0) {
749 TRACE("freeing unused glyphset at cache %d\n", best);
750 FreeEntry(best);
751 glyphsetCache[best].count = 1;
752 if(prev_best >= 0) {
753 glyphsetCache[prev_best].next = glyphsetCache[best].next;
754 glyphsetCache[best].next = mru;
755 mru = best;
756 } else {
757 assert(mru == best);
759 return mru;
762 TRACE("Growing cache\n");
764 if (glyphsetCache)
765 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
766 glyphsetCache,
767 (glyphsetCacheSize + INIT_CACHE_SIZE)
768 * sizeof(*glyphsetCache));
769 else
770 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
771 (glyphsetCacheSize + INIT_CACHE_SIZE)
772 * sizeof(*glyphsetCache));
774 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
775 i++) {
776 glyphsetCache[i].next = i + 1;
777 glyphsetCache[i].count = -1;
779 glyphsetCache[i-1].next = -1;
780 glyphsetCacheSize += INIT_CACHE_SIZE;
782 lastfree = glyphsetCache[best].next;
783 glyphsetCache[best].count = 1;
784 glyphsetCache[best].next = mru;
785 mru = best;
786 TRACE("new free cache slot at %d\n", mru);
787 return mru;
790 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
792 DWORD size;
793 WORD *gasp, *buffer;
794 WORD num_recs;
795 DWORD ppem;
796 TEXTMETRICW tm;
798 *flags = 0;
800 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
801 if(size == GDI_ERROR)
802 return FALSE;
804 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
805 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
807 GetTextMetricsW(physDev->hdc, &tm);
808 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
810 gasp++;
811 num_recs = get_be_word(*gasp);
812 gasp++;
813 while(num_recs--)
815 *flags = get_be_word(*(gasp + 1));
816 if(ppem <= get_be_word(*gasp))
817 break;
818 gasp += 2;
820 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
822 HeapFree(GetProcessHeap(), 0, buffer);
823 return TRUE;
826 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
828 AA_Type ret;
829 WORD flags;
830 UINT font_smoothing_type, font_smoothing_orientation;
832 if (X11DRV_XRender_Installed && subpixel &&
833 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
834 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
836 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
837 &font_smoothing_orientation, 0) &&
838 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
840 ret = AA_BGR;
842 else
843 ret = AA_RGB;
844 /*FIXME
845 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
846 But, Wine's subpixel rendering can support the portrait mode.
849 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
850 ret = AA_Grey;
851 else
852 ret = AA_None;
854 return ret;
857 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
859 int ret;
860 int format;
861 gsCacheEntry *entry;
862 static int hinter = -1;
863 static int subpixel = -1;
864 BOOL font_smoothing;
866 if((ret = LookupEntry(plfsz)) != -1) return ret;
868 ret = AllocEntry();
869 entry = glyphsetCache + ret;
870 entry->lfsz = *plfsz;
871 for( format = 0; format < AA_MAXVALUE; format++ ) {
872 assert( !entry->format[format] );
875 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
877 if(hinter == -1 || subpixel == -1)
879 RASTERIZER_STATUS status;
880 GetRasterizerCaps(&status, sizeof(status));
881 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
882 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
885 switch (plfsz->lf.lfQuality)
887 case ANTIALIASED_QUALITY:
888 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
889 return ret; /* ignore further configuration */
890 case CLEARTYPE_QUALITY:
891 case CLEARTYPE_NATURAL_QUALITY:
892 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
893 break;
894 case DEFAULT_QUALITY:
895 case DRAFT_QUALITY:
896 case PROOF_QUALITY:
897 default:
898 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
899 font_smoothing)
901 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
903 else
904 entry->aa_default = AA_None;
905 break;
908 font_smoothing = TRUE; /* default to enabled */
909 #ifdef SONAME_LIBFONTCONFIG
910 if (fontconfig_installed)
912 FcPattern *match, *pattern = pFcPatternCreate();
913 FcResult result;
914 char family[LF_FACESIZE * 4];
916 WideCharToMultiByte( CP_UTF8, 0, plfsz->lf.lfFaceName, -1, family, sizeof(family), NULL, NULL );
917 pFcPatternAddString( pattern, FC_FAMILY, (FcChar8 *)family );
918 if (plfsz->lf.lfWeight != FW_DONTCARE)
920 int weight;
921 switch (plfsz->lf.lfWeight)
923 case FW_THIN: weight = FC_WEIGHT_THIN; break;
924 case FW_EXTRALIGHT: weight = FC_WEIGHT_EXTRALIGHT; break;
925 case FW_LIGHT: weight = FC_WEIGHT_LIGHT; break;
926 case FW_NORMAL: weight = FC_WEIGHT_NORMAL; break;
927 case FW_MEDIUM: weight = FC_WEIGHT_MEDIUM; break;
928 case FW_SEMIBOLD: weight = FC_WEIGHT_SEMIBOLD; break;
929 case FW_BOLD: weight = FC_WEIGHT_BOLD; break;
930 case FW_EXTRABOLD: weight = FC_WEIGHT_EXTRABOLD; break;
931 case FW_HEAVY: weight = FC_WEIGHT_HEAVY; break;
932 default: weight = (plfsz->lf.lfWeight - 80) / 4; break;
934 pFcPatternAddInteger( pattern, FC_WEIGHT, weight );
936 pFcPatternAddInteger( pattern, FC_SLANT, plfsz->lf.lfItalic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
937 pFcConfigSubstitute( NULL, pattern, FcMatchPattern );
938 pFcDefaultSubstitute( pattern );
939 if ((match = pFcFontMatch( NULL, pattern, &result )))
941 int rgba;
942 FcBool antialias;
944 if (pFcPatternGetBool( match, FC_ANTIALIAS, 0, &antialias ) != FcResultMatch)
945 antialias = TRUE;
946 if (pFcPatternGetInteger( match, FC_RGBA, 0, &rgba ) == FcResultMatch)
948 FcChar8 *file;
949 if (pFcPatternGetString( match, FC_FILE, 0, &file ) != FcResultMatch) file = NULL;
951 TRACE( "fontconfig returned rgba %u antialias %u for font %s file %s\n",
952 rgba, antialias, debugstr_w(plfsz->lf.lfFaceName), debugstr_a((char *)file) );
954 switch (rgba)
956 case FC_RGBA_RGB: entry->aa_default = AA_RGB; break;
957 case FC_RGBA_BGR: entry->aa_default = AA_BGR; break;
958 case FC_RGBA_VRGB: entry->aa_default = AA_VRGB; break;
959 case FC_RGBA_VBGR: entry->aa_default = AA_VBGR; break;
960 case FC_RGBA_NONE: entry->aa_default = AA_Grey; break;
963 if (!antialias) font_smoothing = FALSE;
964 pFcPatternDestroy( match );
966 pFcPatternDestroy( pattern );
968 #endif /* SONAME_LIBFONTCONFIG */
970 /* now check Xft resources */
972 char *value;
973 BOOL antialias = TRUE;
975 wine_tsx11_lock();
976 if ((value = XGetDefault( gdi_display, "Xft", "antialias" )))
978 if (tolower(value[0]) == 'f' || tolower(value[0]) == 'n' ||
979 value[0] == '0' || !strcasecmp( value, "off" ))
980 antialias = FALSE;
982 if ((value = XGetDefault( gdi_display, "Xft", "rgba" )))
984 TRACE( "Xft resource returned rgba '%s' antialias %u\n", value, antialias );
985 if (!strcmp( value, "rgb" )) entry->aa_default = AA_RGB;
986 else if (!strcmp( value, "bgr" )) entry->aa_default = AA_BGR;
987 else if (!strcmp( value, "vrgb" )) entry->aa_default = AA_VRGB;
988 else if (!strcmp( value, "vbgr" )) entry->aa_default = AA_VBGR;
989 else if (!strcmp( value, "none" )) entry->aa_default = AA_Grey;
991 wine_tsx11_unlock();
992 if (!antialias) font_smoothing = FALSE;
995 if (!font_smoothing) entry->aa_default = AA_None;
997 /* we can't support subpixel without xrender */
998 if (!X11DRV_XRender_Installed && entry->aa_default > AA_Grey) entry->aa_default = AA_Grey;
1000 else
1001 entry->aa_default = AA_None;
1003 return ret;
1006 static void dec_ref_cache(int index)
1008 assert(index >= 0);
1009 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
1010 assert(glyphsetCache[index].count > 0);
1011 glyphsetCache[index].count--;
1014 static void lfsz_calc_hash(LFANDSIZE *plfsz)
1016 DWORD hash = 0, *ptr, two_chars;
1017 WORD *pwc;
1018 int i;
1020 hash ^= plfsz->devsize.cx;
1021 hash ^= plfsz->devsize.cy;
1022 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
1023 hash ^= *ptr;
1024 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
1025 hash ^= *ptr;
1026 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
1027 two_chars = *ptr;
1028 pwc = (WCHAR *)&two_chars;
1029 if(!*pwc) break;
1030 *pwc = toupperW(*pwc);
1031 pwc++;
1032 *pwc = toupperW(*pwc);
1033 hash ^= two_chars;
1034 if(!*pwc) break;
1036 plfsz->hash = hash;
1037 return;
1040 /***********************************************************************
1041 * X11DRV_XRender_Finalize
1043 void X11DRV_XRender_Finalize(void)
1045 int i;
1047 EnterCriticalSection(&xrender_cs);
1048 for(i = mru; i >= 0; i = glyphsetCache[i].next)
1049 FreeEntry(i);
1050 LeaveCriticalSection(&xrender_cs);
1054 /***********************************************************************
1055 * X11DRV_XRender_SelectFont
1057 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1059 LFANDSIZE lfsz;
1060 struct xrender_info *info;
1062 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
1063 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
1064 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
1065 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
1066 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
1067 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
1068 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
1070 GetTransform( physDev->hdc, 0x204, &lfsz.xform );
1071 TRACE("font transform %f %f %f %f\n", lfsz.xform.eM11, lfsz.xform.eM12,
1072 lfsz.xform.eM21, lfsz.xform.eM22);
1074 /* Not used fields, would break hashing */
1075 lfsz.xform.eDx = lfsz.xform.eDy = 0;
1077 lfsz_calc_hash(&lfsz);
1079 info = get_xrender_info(physDev);
1080 if (!info) return 0;
1082 EnterCriticalSection(&xrender_cs);
1083 if(info->cache_index != -1)
1084 dec_ref_cache(info->cache_index);
1085 info->cache_index = GetCacheEntry(physDev, &lfsz);
1086 LeaveCriticalSection(&xrender_cs);
1087 return 0;
1090 /***********************************************************************
1091 * X11DRV_XRender_SetDeviceClipping
1093 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
1095 if (physDev->xrender->pict)
1097 wine_tsx11_lock();
1098 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1099 physDev->dc_rect.left, physDev->dc_rect.top,
1100 (XRectangle *)data->Buffer, data->rdh.nCount );
1101 wine_tsx11_unlock();
1105 /***********************************************************************
1106 * X11DRV_XRender_DeleteDC
1108 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1110 X11DRV_XRender_UpdateDrawable(physDev);
1112 EnterCriticalSection(&xrender_cs);
1113 if(physDev->xrender->cache_index != -1)
1114 dec_ref_cache(physDev->xrender->cache_index);
1115 LeaveCriticalSection(&xrender_cs);
1117 HeapFree(GetProcessHeap(), 0, physDev->xrender);
1118 physDev->xrender = NULL;
1119 return;
1122 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
1124 const WineXRenderFormat *fmt;
1125 ColorShifts shifts;
1127 /* When XRender is not around we can only use the screen_depth and when needed we perform depth conversion
1128 * in software. Further we also return the screen depth for paletted formats or TrueColor formats with a low
1129 * number of bits because XRender can't handle paletted formats and 8-bit TrueColor does not exist for XRender. */
1130 if (!X11DRV_XRender_Installed || bits_pixel <= 8)
1131 return FALSE;
1133 if (dib)
1135 X11DRV_PALETTE_ComputeColorShifts(&shifts, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
1136 fmt = get_xrender_format_from_color_shifts(dib->dsBm.bmBitsPixel, &shifts);
1138 /* Common formats should be in our picture format table. */
1139 if (!fmt)
1141 TRACE("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n",
1142 dib->dsBm.bmBitsPixel, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
1143 return FALSE;
1146 else
1148 int red_mask, green_mask, blue_mask;
1150 /* We are dealing with a DDB */
1151 switch (bits_pixel)
1153 case 16:
1154 fmt = get_xrender_format(WXR_FORMAT_R5G6B5);
1155 break;
1156 case 24:
1157 fmt = get_xrender_format(WXR_FORMAT_R8G8B8);
1158 break;
1159 case 32:
1160 fmt = get_xrender_format(WXR_FORMAT_A8R8G8B8);
1161 break;
1162 default:
1163 fmt = NULL;
1166 if (!fmt)
1168 TRACE("Unhandled DDB bits_pixel=%d\n", bits_pixel);
1169 return FALSE;
1172 red_mask = fmt->pict_format->direct.redMask << fmt->pict_format->direct.red;
1173 green_mask = fmt->pict_format->direct.greenMask << fmt->pict_format->direct.green;
1174 blue_mask = fmt->pict_format->direct.blueMask << fmt->pict_format->direct.blue;
1175 X11DRV_PALETTE_ComputeColorShifts(&shifts, red_mask, green_mask, blue_mask);
1178 physBitmap->pixmap_depth = fmt->pict_format->depth;
1179 physBitmap->trueColor = TRUE;
1180 physBitmap->pixmap_color_shifts = shifts;
1181 return TRUE;
1184 /***********************************************************************
1185 * X11DRV_XRender_UpdateDrawable
1187 * Deletes the pict and tile when the drawable changes.
1189 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1191 struct xrender_info *info = physDev->xrender;
1193 if (info->pict || info->pict_src)
1195 wine_tsx11_lock();
1196 XFlush( gdi_display );
1197 if (info->pict)
1199 TRACE("freeing pict = %lx dc = %p\n", info->pict, physDev->hdc);
1200 pXRenderFreePicture(gdi_display, info->pict);
1201 info->pict = 0;
1203 if(info->pict_src)
1205 TRACE("freeing pict = %lx dc = %p\n", info->pict_src, physDev->hdc);
1206 pXRenderFreePicture(gdi_display, info->pict_src);
1207 info->pict_src = 0;
1209 wine_tsx11_unlock();
1212 info->format = NULL;
1215 /************************************************************************
1216 * UploadGlyph
1218 * Helper to ExtTextOut. Must be called inside xrender_cs
1220 static void UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
1222 unsigned int buflen;
1223 char *buf;
1224 Glyph gid;
1225 GLYPHMETRICS gm;
1226 XGlyphInfo gi;
1227 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
1228 gsCacheEntryFormat *formatEntry;
1229 UINT ggo_format = GGO_GLYPH_INDEX;
1230 WXRFormat wxr_format;
1231 static const char zero[4];
1232 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1234 switch(format) {
1235 case AA_Grey:
1236 ggo_format |= WINE_GGO_GRAY16_BITMAP;
1237 break;
1238 case AA_RGB:
1239 ggo_format |= WINE_GGO_HRGB_BITMAP;
1240 break;
1241 case AA_BGR:
1242 ggo_format |= WINE_GGO_HBGR_BITMAP;
1243 break;
1244 case AA_VRGB:
1245 ggo_format |= WINE_GGO_VRGB_BITMAP;
1246 break;
1247 case AA_VBGR:
1248 ggo_format |= WINE_GGO_VBGR_BITMAP;
1249 break;
1251 default:
1252 ERR("aa = %d - not implemented\n", format);
1253 case AA_None:
1254 ggo_format |= GGO_BITMAP;
1255 break;
1258 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1259 if(buflen == GDI_ERROR) {
1260 if(format != AA_None) {
1261 format = AA_None;
1262 entry->aa_default = AA_None;
1263 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
1264 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1266 if(buflen == GDI_ERROR) {
1267 WARN("GetGlyphOutlineW failed using default glyph\n");
1268 buflen = GetGlyphOutlineW(physDev->hdc, 0, ggo_format, &gm, 0, NULL, &identity);
1269 if(buflen == GDI_ERROR) {
1270 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1271 buflen = GetGlyphOutlineW(physDev->hdc, 0x20, ggo_format, &gm, 0, NULL, &identity);
1272 if(buflen == GDI_ERROR) {
1273 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1274 return;
1278 TRACE("Turning off antialiasing for this monochrome font\n");
1281 /* If there is nothing for the current type, we create the entry. */
1282 if( !entry->format[format] ) {
1283 entry->format[format] = HeapAlloc(GetProcessHeap(),
1284 HEAP_ZERO_MEMORY,
1285 sizeof(gsCacheEntryFormat));
1287 formatEntry = entry->format[format];
1289 if(formatEntry->nrealized <= glyph) {
1290 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1292 if (formatEntry->realized)
1293 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1294 HEAP_ZERO_MEMORY,
1295 formatEntry->realized,
1296 formatEntry->nrealized * sizeof(BOOL));
1297 else
1298 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1299 HEAP_ZERO_MEMORY,
1300 formatEntry->nrealized * sizeof(BOOL));
1302 if(!X11DRV_XRender_Installed) {
1303 if (formatEntry->bitmaps)
1304 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
1305 HEAP_ZERO_MEMORY,
1306 formatEntry->bitmaps,
1307 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1308 else
1309 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
1310 HEAP_ZERO_MEMORY,
1311 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1313 if (formatEntry->gis)
1314 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1315 HEAP_ZERO_MEMORY,
1316 formatEntry->gis,
1317 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1318 else
1319 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1320 HEAP_ZERO_MEMORY,
1321 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1325 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
1326 switch(format) {
1327 case AA_Grey:
1328 wxr_format = WXR_FORMAT_GRAY;
1329 break;
1331 case AA_RGB:
1332 case AA_BGR:
1333 case AA_VRGB:
1334 case AA_VBGR:
1335 wxr_format = WXR_FORMAT_A8R8G8B8;
1336 break;
1338 default:
1339 ERR("aa = %d - not implemented\n", format);
1340 case AA_None:
1341 wxr_format = WXR_FORMAT_MONO;
1342 break;
1345 wine_tsx11_lock();
1346 formatEntry->font_format = get_xrender_format(wxr_format);
1347 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
1348 wine_tsx11_unlock();
1352 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1353 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1354 formatEntry->realized[glyph] = TRUE;
1356 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1357 buflen,
1358 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1359 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1361 gi.width = gm.gmBlackBoxX;
1362 gi.height = gm.gmBlackBoxY;
1363 gi.x = -gm.gmptGlyphOrigin.x;
1364 gi.y = gm.gmptGlyphOrigin.y;
1365 gi.xOff = gm.gmCellIncX;
1366 gi.yOff = gm.gmCellIncY;
1368 if(TRACE_ON(xrender)) {
1369 int pitch, i, j;
1370 char output[300];
1371 unsigned char *line;
1373 if(format == AA_None) {
1374 pitch = ((gi.width + 31) / 32) * 4;
1375 for(i = 0; i < gi.height; i++) {
1376 line = (unsigned char*) buf + i * pitch;
1377 output[0] = '\0';
1378 for(j = 0; j < pitch * 8; j++) {
1379 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1381 TRACE("%s\n", output);
1383 } else {
1384 static const char blks[] = " .:;!o*#";
1385 char str[2];
1387 str[1] = '\0';
1388 pitch = ((gi.width + 3) / 4) * 4;
1389 for(i = 0; i < gi.height; i++) {
1390 line = (unsigned char*) buf + i * pitch;
1391 output[0] = '\0';
1392 for(j = 0; j < pitch; j++) {
1393 str[0] = blks[line[j] >> 5];
1394 strcat(output, str);
1396 TRACE("%s\n", output);
1402 if(formatEntry->glyphset) {
1403 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1404 unsigned char *byte = (unsigned char*) buf, c;
1405 int i = buflen;
1407 while(i--) {
1408 c = *byte;
1410 /* magic to flip bit order */
1411 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1412 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1413 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1415 *byte++ = c;
1418 else if ( format != AA_Grey &&
1419 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1421 unsigned int i, *data = (unsigned int *)buf;
1422 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1424 gid = glyph;
1427 XRenderCompositeText seems to ignore 0x0 glyphs when
1428 AA_None, which means we lose the advance width of glyphs
1429 like the space. We'll pretend that such glyphs are 1x1
1430 bitmaps.
1433 if(buflen == 0)
1434 gi.width = gi.height = 1;
1436 wine_tsx11_lock();
1437 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1438 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1439 wine_tsx11_unlock();
1440 HeapFree(GetProcessHeap(), 0, buf);
1441 } else {
1442 formatEntry->bitmaps[glyph] = buf;
1445 formatEntry->gis[glyph] = gi;
1448 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1449 void *bitmap, XGlyphInfo *gi)
1451 unsigned char *srcLine = bitmap, *src;
1452 unsigned char bits, bitsMask;
1453 int width = gi->width;
1454 int stride = ((width + 31) & ~31) >> 3;
1455 int height = gi->height;
1456 int w;
1457 int xspan, lenspan;
1459 TRACE("%d, %d\n", x, y);
1460 x -= gi->x;
1461 y -= gi->y;
1462 while (height--)
1464 src = srcLine;
1465 srcLine += stride;
1466 w = width;
1468 bitsMask = 0x80; /* FreeType is always MSB first */
1469 bits = *src++;
1471 xspan = x;
1472 while (w)
1474 if (bits & bitsMask)
1476 lenspan = 0;
1479 lenspan++;
1480 if (lenspan == w)
1481 break;
1482 bitsMask = bitsMask >> 1;
1483 if (!bitsMask)
1485 bits = *src++;
1486 bitsMask = 0x80;
1488 } while (bits & bitsMask);
1489 XFillRectangle (gdi_display, physDev->drawable,
1490 physDev->gc, xspan, y, lenspan, 1);
1491 xspan += lenspan;
1492 w -= lenspan;
1494 else
1498 w--;
1499 xspan++;
1500 if (!w)
1501 break;
1502 bitsMask = bitsMask >> 1;
1503 if (!bitsMask)
1505 bits = *src++;
1506 bitsMask = 0x80;
1508 } while (!(bits & bitsMask));
1511 y++;
1515 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1516 void *bitmap, XGlyphInfo *gi)
1518 unsigned char *srcLine = bitmap, *src, bits;
1519 int width = gi->width;
1520 int stride = ((width + 3) & ~3);
1521 int height = gi->height;
1522 int w;
1523 int xspan, lenspan;
1525 x -= gi->x;
1526 y -= gi->y;
1527 while (height--)
1529 src = srcLine;
1530 srcLine += stride;
1531 w = width;
1533 bits = *src++;
1534 xspan = x;
1535 while (w)
1537 if (bits >= 0x80)
1539 lenspan = 0;
1542 lenspan++;
1543 if (lenspan == w)
1544 break;
1545 bits = *src++;
1546 } while (bits >= 0x80);
1547 XFillRectangle (gdi_display, physDev->drawable,
1548 physDev->gc, xspan, y, lenspan, 1);
1549 xspan += lenspan;
1550 w -= lenspan;
1552 else
1556 w--;
1557 xspan++;
1558 if (!w)
1559 break;
1560 bits = *src++;
1561 } while (bits < 0x80);
1564 y++;
1569 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1571 int s, l;
1573 s = 0;
1574 while ((mask & 1) == 0)
1576 mask >>= 1;
1577 s++;
1579 l = 0;
1580 while ((mask & 1) == 1)
1582 mask >>= 1;
1583 l++;
1585 *shift = s;
1586 *len = l;
1589 static DWORD GetField (DWORD pixel, int shift, int len)
1591 pixel = pixel & (((1 << (len)) - 1) << shift);
1592 pixel = pixel << (32 - (shift + len)) >> 24;
1593 while (len < 8)
1595 pixel |= (pixel >> len);
1596 len <<= 1;
1598 return pixel;
1602 static DWORD PutField (DWORD pixel, int shift, int len)
1604 shift = shift - (8 - len);
1605 if (len <= 8)
1606 pixel &= (((1 << len) - 1) << (8 - len));
1607 if (shift < 0)
1608 pixel >>= -shift;
1609 else
1610 pixel <<= shift;
1611 return pixel;
1614 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1615 int color)
1617 int r_shift, r_len;
1618 int g_shift, g_len;
1619 int b_shift, b_len;
1620 BYTE *maskLine, *mask, m;
1621 int maskStride;
1622 DWORD pixel;
1623 int width, height;
1624 int w, tx;
1625 BYTE src_r, src_g, src_b;
1627 x -= gi->x;
1628 y -= gi->y;
1629 width = gi->width;
1630 height = gi->height;
1632 maskLine = bitmap;
1633 maskStride = (width + 3) & ~3;
1635 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1636 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1637 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1639 src_r = GetField(color, r_shift, r_len);
1640 src_g = GetField(color, g_shift, g_len);
1641 src_b = GetField(color, b_shift, b_len);
1643 for(; height--; y++)
1645 mask = maskLine;
1646 maskLine += maskStride;
1647 w = width;
1648 tx = x;
1650 if(y < 0) continue;
1651 if(y >= image->height) break;
1653 for(; w--; tx++)
1655 if(tx >= image->width) break;
1657 m = *mask++;
1658 if(tx < 0) continue;
1660 if (m == 0xff)
1661 XPutPixel (image, tx, y, color);
1662 else if (m)
1664 BYTE r, g, b;
1666 pixel = XGetPixel (image, tx, y);
1668 r = GetField(pixel, r_shift, r_len);
1669 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1670 g = GetField(pixel, g_shift, g_len);
1671 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1672 b = GetField(pixel, b_shift, b_len);
1673 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1675 pixel = (PutField (r, r_shift, r_len) |
1676 PutField (g, g_shift, g_len) |
1677 PutField (b, b_shift, b_len));
1678 XPutPixel (image, tx, y, pixel);
1684 /*************************************************************
1685 * get_tile_pict
1687 * Returns an appropriate Picture for tiling the text colour.
1688 * Call and use result within the xrender_cs
1690 static Picture get_tile_pict(const WineXRenderFormat *wxr_format, int text_pixel)
1692 static struct
1694 Pixmap xpm;
1695 Picture pict;
1696 int current_color;
1697 } tiles[WXR_NB_FORMATS], *tile;
1698 XRenderColor col;
1700 tile = &tiles[wxr_format->format];
1702 if(!tile->xpm)
1704 XRenderPictureAttributes pa;
1706 wine_tsx11_lock();
1707 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1709 pa.repeat = RepeatNormal;
1710 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1711 wine_tsx11_unlock();
1713 /* init current_color to something different from text_pixel */
1714 tile->current_color = ~text_pixel;
1716 if(wxr_format->format == WXR_FORMAT_MONO)
1718 /* for a 1bpp bitmap we always need a 1 in the tile */
1719 col.red = col.green = col.blue = 0;
1720 col.alpha = 0xffff;
1721 wine_tsx11_lock();
1722 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1723 wine_tsx11_unlock();
1727 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1729 get_xrender_color(wxr_format, text_pixel, &col);
1730 wine_tsx11_lock();
1731 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1732 wine_tsx11_unlock();
1733 tile->current_color = text_pixel;
1735 return tile->pict;
1738 /*************************************************************
1739 * get_mask_pict
1741 * Returns an appropriate Picture for masking with the specified alpha.
1742 * Call and use result within the xrender_cs
1744 static Picture get_mask_pict( int alpha )
1746 static Pixmap pixmap;
1747 static Picture pict;
1748 static int current_alpha;
1750 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1752 if (!pixmap)
1754 const WineXRenderFormat *fmt = get_xrender_format( WXR_FORMAT_A8R8G8B8 );
1755 XRenderPictureAttributes pa;
1757 wine_tsx11_lock();
1758 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1759 pa.repeat = RepeatNormal;
1760 pict = pXRenderCreatePicture( gdi_display, pixmap, fmt->pict_format, CPRepeat, &pa );
1761 wine_tsx11_unlock();
1762 current_alpha = -1;
1765 if (alpha != current_alpha)
1767 XRenderColor col;
1768 col.red = col.green = col.blue = 0;
1769 col.alpha = current_alpha = alpha;
1770 wine_tsx11_lock();
1771 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1772 wine_tsx11_unlock();
1774 return pict;
1777 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1779 return 1;
1782 /***********************************************************************
1783 * X11DRV_XRender_ExtTextOut
1785 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1786 const RECT *lprect, LPCWSTR wstr, UINT count,
1787 const INT *lpDx )
1789 XGCValues xgcval;
1790 gsCacheEntry *entry;
1791 gsCacheEntryFormat *formatEntry;
1792 BOOL retv = FALSE;
1793 int textPixel, backgroundPixel;
1794 HRGN saved_region = 0;
1795 BOOL disable_antialias = FALSE;
1796 AA_Type aa_type = AA_None;
1797 DIBSECTION bmp;
1798 unsigned int idx;
1799 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1800 Picture tile_pict = 0;
1802 /* Do we need to disable antialiasing because of palette mode? */
1803 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1804 TRACE("bitmap is not a DIB\n");
1806 else if (bmp.dsBmih.biBitCount <= 8) {
1807 TRACE("Disabling antialiasing\n");
1808 disable_antialias = TRUE;
1811 xgcval.function = GXcopy;
1812 xgcval.background = physDev->backgroundPixel;
1813 xgcval.fill_style = FillSolid;
1814 wine_tsx11_lock();
1815 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1816 wine_tsx11_unlock();
1818 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1820 if(physDev->depth == 1) {
1821 if((physDev->textPixel & 0xffffff) == 0) {
1822 textPixel = 0;
1823 backgroundPixel = 1;
1824 } else {
1825 textPixel = 1;
1826 backgroundPixel = 0;
1828 } else {
1829 textPixel = physDev->textPixel;
1830 backgroundPixel = physDev->backgroundPixel;
1833 if(flags & ETO_OPAQUE)
1835 wine_tsx11_lock();
1836 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1837 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1838 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1839 lprect->right - lprect->left, lprect->bottom - lprect->top );
1840 wine_tsx11_unlock();
1843 if(count == 0)
1845 retv = TRUE;
1846 goto done_unlock;
1849 if (flags & ETO_CLIPPED)
1851 HRGN clip_region;
1853 clip_region = CreateRectRgnIndirect( lprect );
1854 /* make a copy of the current device region */
1855 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1856 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1857 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1858 DeleteObject( clip_region );
1861 EnterCriticalSection(&xrender_cs);
1863 entry = glyphsetCache + physDev->xrender->cache_index;
1864 if( disable_antialias == FALSE )
1865 aa_type = entry->aa_default;
1866 formatEntry = entry->format[aa_type];
1868 for(idx = 0; idx < count; idx++) {
1869 if( !formatEntry ) {
1870 UploadGlyph(physDev, wstr[idx], aa_type);
1871 /* re-evaluate antialias since aa_default may have changed */
1872 if( disable_antialias == FALSE )
1873 aa_type = entry->aa_default;
1874 formatEntry = entry->format[aa_type];
1875 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1876 UploadGlyph(physDev, wstr[idx], aa_type);
1879 if (!formatEntry)
1881 WARN("could not upload requested glyphs\n");
1882 LeaveCriticalSection(&xrender_cs);
1883 goto done_unlock;
1886 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1887 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1889 if(X11DRV_XRender_Installed)
1891 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1892 POINT offset = {0, 0};
1893 POINT desired, current;
1894 int render_op = PictOpOver;
1895 Picture pict = get_xrender_picture(physDev);
1897 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1898 So we pass zeros to the function and move to our starting position using the first
1899 element of the elts array. */
1901 desired.x = physDev->dc_rect.left + x;
1902 desired.y = physDev->dc_rect.top + y;
1903 current.x = current.y = 0;
1905 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1907 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1909 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1910 render_op = PictOpOutReverse; /* This gives us 'black' text */
1912 for(idx = 0; idx < count; idx++)
1914 elts[idx].glyphset = formatEntry->glyphset;
1915 elts[idx].chars = wstr + idx;
1916 elts[idx].nchars = 1;
1917 elts[idx].xOff = desired.x - current.x;
1918 elts[idx].yOff = desired.y - current.y;
1920 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1921 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1923 if(!lpDx)
1925 desired.x += formatEntry->gis[wstr[idx]].xOff;
1926 desired.y += formatEntry->gis[wstr[idx]].yOff;
1928 else
1930 if(flags & ETO_PDY)
1932 offset.x += lpDx[idx * 2];
1933 offset.y += lpDx[idx * 2 + 1];
1935 else
1936 offset.x += lpDx[idx];
1937 desired.x = physDev->dc_rect.left + x + offset.x;
1938 desired.y = physDev->dc_rect.top + y + offset.y;
1941 wine_tsx11_lock();
1942 /* Make sure we don't have any transforms set from a previous call */
1943 set_xrender_transformation(pict, 1, 1, 0, 0);
1944 pXRenderCompositeText16(gdi_display, render_op,
1945 tile_pict,
1946 pict,
1947 formatEntry->font_format->pict_format,
1948 0, 0, 0, 0, elts, count);
1949 wine_tsx11_unlock();
1950 HeapFree(GetProcessHeap(), 0, elts);
1951 } else {
1952 POINT offset = {0, 0};
1953 wine_tsx11_lock();
1954 XSetForeground( gdi_display, physDev->gc, textPixel );
1956 if(aa_type == AA_None || physDev->depth == 1)
1958 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1960 if(aa_type == AA_None)
1961 sharp_glyph_fn = SharpGlyphMono;
1962 else
1963 sharp_glyph_fn = SharpGlyphGray;
1965 for(idx = 0; idx < count; idx++) {
1966 sharp_glyph_fn(physDev,
1967 physDev->dc_rect.left + x + offset.x,
1968 physDev->dc_rect.top + y + offset.y,
1969 formatEntry->bitmaps[wstr[idx]],
1970 &formatEntry->gis[wstr[idx]]);
1971 if(lpDx)
1973 if(flags & ETO_PDY)
1975 offset.x += lpDx[idx * 2];
1976 offset.y += lpDx[idx * 2 + 1];
1978 else
1979 offset.x += lpDx[idx];
1981 else
1983 offset.x += formatEntry->gis[wstr[idx]].xOff;
1984 offset.y += formatEntry->gis[wstr[idx]].yOff;
1987 } else {
1988 XImage *image;
1989 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1990 RECT extents = {0, 0, 0, 0};
1991 POINT cur = {0, 0};
1992 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1993 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1995 TRACE("drawable %dx%d\n", w, h);
1997 for(idx = 0; idx < count; idx++) {
1998 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1999 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
2000 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
2001 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
2002 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
2003 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
2004 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
2005 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
2007 if(lpDx)
2009 if(flags & ETO_PDY)
2011 cur.x += lpDx[idx * 2];
2012 cur.y += lpDx[idx * 2 + 1];
2014 else
2015 cur.x += lpDx[idx];
2017 else
2019 cur.x += formatEntry->gis[wstr[idx]].xOff;
2020 cur.y += formatEntry->gis[wstr[idx]].yOff;
2023 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
2024 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
2026 if(physDev->dc_rect.left + x + extents.left >= 0) {
2027 image_x = physDev->dc_rect.left + x + extents.left;
2028 image_off_x = 0;
2029 } else {
2030 image_x = 0;
2031 image_off_x = physDev->dc_rect.left + x + extents.left;
2033 if(physDev->dc_rect.top + y + extents.top >= 0) {
2034 image_y = physDev->dc_rect.top + y + extents.top;
2035 image_off_y = 0;
2036 } else {
2037 image_y = 0;
2038 image_off_y = physDev->dc_rect.top + y + extents.top;
2040 if(physDev->dc_rect.left + x + extents.right < w)
2041 image_w = physDev->dc_rect.left + x + extents.right - image_x;
2042 else
2043 image_w = w - image_x;
2044 if(physDev->dc_rect.top + y + extents.bottom < h)
2045 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
2046 else
2047 image_h = h - image_y;
2049 if(image_w <= 0 || image_h <= 0) goto no_image;
2051 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
2052 image = XGetImage(gdi_display, physDev->drawable,
2053 image_x, image_y, image_w, image_h,
2054 AllPlanes, ZPixmap);
2055 X11DRV_check_error();
2057 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
2058 gdi_display, (int)physDev->drawable, image_x, image_y,
2059 image_w, image_h, AllPlanes, ZPixmap,
2060 physDev->depth, image);
2061 if(!image) {
2062 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
2063 physDev->depth);
2064 GC gc;
2065 XGCValues gcv;
2067 gcv.graphics_exposures = False;
2068 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
2069 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
2070 image_w, image_h, 0, 0);
2071 XFreeGC(gdi_display, gc);
2072 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
2073 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
2074 ZPixmap);
2075 X11DRV_check_error();
2076 XFreePixmap(gdi_display, xpm);
2078 if(!image) goto no_image;
2080 image->red_mask = visual->red_mask;
2081 image->green_mask = visual->green_mask;
2082 image->blue_mask = visual->blue_mask;
2084 for(idx = 0; idx < count; idx++) {
2085 SmoothGlyphGray(image,
2086 offset.x + image_off_x - extents.left,
2087 offset.y + image_off_y - extents.top,
2088 formatEntry->bitmaps[wstr[idx]],
2089 &formatEntry->gis[wstr[idx]],
2090 physDev->textPixel);
2091 if(lpDx)
2093 if(flags & ETO_PDY)
2095 offset.x += lpDx[idx * 2];
2096 offset.y += lpDx[idx * 2 + 1];
2098 else
2099 offset.x += lpDx[idx];
2101 else
2103 offset.x += formatEntry->gis[wstr[idx]].xOff;
2104 offset.y += formatEntry->gis[wstr[idx]].yOff;
2107 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
2108 image_x, image_y, image_w, image_h);
2109 XDestroyImage(image);
2111 no_image:
2112 wine_tsx11_unlock();
2114 LeaveCriticalSection(&xrender_cs);
2116 if (flags & ETO_CLIPPED)
2118 /* restore the device region */
2119 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
2120 DeleteObject( saved_region );
2123 retv = TRUE;
2125 done_unlock:
2126 X11DRV_UnlockDIBSection( physDev, TRUE );
2127 return retv;
2130 /* Helper function for (stretched) blitting using xrender */
2131 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
2132 int x_src, int y_src, int x_dst, int y_dst,
2133 double xscale, double yscale, int width, int height )
2135 int x_offset, y_offset;
2137 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
2138 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
2139 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
2140 if(xscale != 1.0 || yscale != 1.0)
2142 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2143 * in the wrong quadrant of the x-y plane.
2145 x_offset = (xscale < 0) ? -width : 0;
2146 y_offset = (yscale < 0) ? -height : 0;
2147 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
2149 else
2151 x_offset = x_src;
2152 y_offset = y_src;
2153 set_xrender_transformation(src_pict, 1, 1, 0, 0);
2155 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
2156 x_offset, y_offset, 0, 0, x_dst, y_dst, width, height );
2159 /* Helper function for (stretched) mono->color blitting using xrender */
2160 static void xrender_mono_blit( Picture src_pict, Picture mask_pict, Picture dst_pict,
2161 int x_src, int y_src, double xscale, double yscale, int width, int height )
2163 int x_offset, y_offset;
2165 /* When doing a mono->color blit, 'src_pict' contains a 1x1 picture for tiling, the actual
2166 * source data is in mask_pict. The 'src_pict' data effectively acts as an alpha channel to the
2167 * tile data. We need PictOpOver for correct rendering.
2168 * Note since the 'source data' is in the mask picture, we have to pass x_src / y_src using
2169 * mask_x / mask_y
2171 if (xscale != 1.0 || yscale != 1.0)
2173 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2174 * in the wrong quadrant of the x-y plane.
2176 x_offset = (xscale < 0) ? -width : 0;
2177 y_offset = (yscale < 0) ? -height : 0;
2178 set_xrender_transformation(mask_pict, xscale, yscale, x_src, y_src);
2180 else
2182 x_offset = x_src;
2183 y_offset = y_src;
2184 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
2186 pXRenderComposite(gdi_display, PictOpOver, src_pict, mask_pict, dst_pict,
2187 0, 0, x_offset, y_offset, 0, 0, width, height);
2190 /******************************************************************************
2191 * AlphaBlend
2193 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2194 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2196 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
2197 struct xrender_info *src_info = get_xrender_info( devSrc );
2198 double xscale, yscale;
2199 BOOL use_repeat;
2201 if(!X11DRV_XRender_Installed) {
2202 FIXME("Unable to AlphaBlend without Xrender\n");
2203 return FALSE;
2206 if (devSrc != devDst) X11DRV_LockDIBSection( devSrc, DIB_Status_GdiMod );
2207 X11DRV_LockDIBSection( devDst, DIB_Status_GdiMod );
2209 dst_pict = get_xrender_picture( devDst );
2211 use_repeat = use_source_repeat( devSrc );
2212 if (!use_repeat)
2214 xscale = src->width / (double)dst->width;
2215 yscale = src->height / (double)dst->height;
2217 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2219 if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && src_info->format)
2221 /* we need a source picture with no alpha */
2222 WXRFormat format = get_format_without_alpha( src_info->format->format );
2223 if (format != src_info->format->format)
2225 XRenderPictureAttributes pa;
2226 const WineXRenderFormat *fmt = get_xrender_format( format );
2228 wine_tsx11_lock();
2229 pa.subwindow_mode = IncludeInferiors;
2230 pa.repeat = use_repeat ? RepeatNormal : RepeatNone;
2231 tmp_pict = pXRenderCreatePicture( gdi_display, devSrc->drawable, fmt->pict_format,
2232 CPSubwindowMode|CPRepeat, &pa );
2233 wine_tsx11_unlock();
2234 src_pict = tmp_pict;
2238 if (!src_pict) src_pict = get_xrender_picture_source( devSrc, use_repeat );
2240 EnterCriticalSection( &xrender_cs );
2241 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2243 wine_tsx11_lock();
2244 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2245 devSrc->dc_rect.left + src->visrect.left, devSrc->dc_rect.top + src->visrect.top,
2246 devDst->dc_rect.left + dst->visrect.left, devDst->dc_rect.top + dst->visrect.top,
2247 xscale, yscale,
2248 dst->visrect.right - dst->visrect.left, dst->visrect.bottom - dst->visrect.top );
2249 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2250 wine_tsx11_unlock();
2252 LeaveCriticalSection( &xrender_cs );
2253 if (devSrc != devDst) X11DRV_UnlockDIBSection( devSrc, FALSE );
2254 X11DRV_UnlockDIBSection( devDst, TRUE );
2255 return TRUE;
2259 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2261 /* At depths >1, the depth of physBitmap and physDev might not be the same e.g. the physbitmap might be a 16-bit DIB while the physdev uses 24-bit */
2262 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2263 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2264 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2266 wine_tsx11_lock();
2267 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2269 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2270 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2271 (src_format->format == dst_format->format) )
2273 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2274 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2276 else /* We need depth conversion */
2278 Picture src_pict, dst_pict;
2279 XRenderPictureAttributes pa;
2280 pa.subwindow_mode = IncludeInferiors;
2281 pa.repeat = RepeatNone;
2283 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2284 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2286 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, 0, 0, 0, 0, 1.0, 1.0, width, height);
2287 pXRenderFreePicture(gdi_display, src_pict);
2288 pXRenderFreePicture(gdi_display, dst_pict);
2290 wine_tsx11_unlock();
2293 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2294 Pixmap pixmap, GC gc,
2295 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2297 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
2298 int width = dst->visrect.right - dst->visrect.left;
2299 int height = dst->visrect.bottom - dst->visrect.top;
2300 int x_src = physDevSrc->dc_rect.left + src->visrect.left;
2301 int y_src = physDevSrc->dc_rect.top + src->visrect.top;
2302 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2303 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2304 Picture src_pict=0, dst_pict=0, mask_pict=0;
2305 BOOL use_repeat;
2306 double xscale, yscale;
2308 XRenderPictureAttributes pa;
2309 pa.subwindow_mode = IncludeInferiors;
2310 pa.repeat = RepeatNone;
2312 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n",
2313 physDevSrc->depth, src->width, src->height, x_src, y_src);
2314 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, dst->width, dst->height);
2316 if(!X11DRV_XRender_Installed)
2318 TRACE("Not using XRender since it is not available or disabled\n");
2319 return FALSE;
2322 /* XRender can't handle palettes, so abort */
2323 if(X11DRV_PALETTE_XPixelToPalette)
2324 return FALSE;
2326 /* XRender is of no use in this case */
2327 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2328 return FALSE;
2330 /* Just use traditional X copy when the formats match and we don't need stretching */
2331 if((src_info->format->format == dst_format->format) && !stretch)
2333 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2334 wine_tsx11_lock();
2335 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2336 wine_tsx11_unlock();
2337 return TRUE;
2340 use_repeat = use_source_repeat( physDevSrc );
2341 if (!use_repeat)
2343 xscale = src->width / (double)dst->width;
2344 yscale = src->height / (double)dst->height;
2346 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2348 /* mono -> color */
2349 if(physDevSrc->depth == 1 && physDevDst->depth > 1)
2351 XRenderColor col;
2352 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2354 /* We use the source drawable as a mask */
2355 mask_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2357 /* Use backgroundPixel as the foreground color */
2358 EnterCriticalSection( &xrender_cs );
2359 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2361 /* Create a destination picture and fill it with textPixel color as the background color */
2362 wine_tsx11_lock();
2363 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2364 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2366 xrender_mono_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2368 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2369 wine_tsx11_unlock();
2370 LeaveCriticalSection( &xrender_cs );
2372 else /* color -> color (can be at different depths) or mono -> mono */
2374 if (physDevDst->depth == 32 && physDevSrc->depth < 32) mask_pict = get_no_alpha_mask();
2375 src_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2377 wine_tsx11_lock();
2378 dst_pict = pXRenderCreatePicture(gdi_display,
2379 pixmap, dst_format->pict_format,
2380 CPSubwindowMode|CPRepeat, &pa);
2382 xrender_blit(PictOpSrc, src_pict, mask_pict, dst_pict,
2383 x_src, y_src, 0, 0, xscale, yscale, width, height);
2385 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2386 wine_tsx11_unlock();
2388 return TRUE;
2391 #else /* SONAME_LIBXRENDER */
2393 void X11DRV_XRender_Init(void)
2395 TRACE("XRender support not compiled in.\n");
2396 return;
2399 void X11DRV_XRender_Finalize(void)
2403 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2405 assert(0);
2406 return FALSE;
2409 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2411 assert(0);
2412 return;
2415 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2417 assert(0);
2418 return;
2421 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2422 const RECT *lprect, LPCWSTR wstr, UINT count,
2423 const INT *lpDx )
2425 assert(0);
2426 return FALSE;
2429 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2431 assert(0);
2432 return;
2435 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2436 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2438 FIXME("not supported - XRENDER headers were missing at compile time\n");
2439 return FALSE;
2442 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2444 wine_tsx11_lock();
2445 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2447 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2448 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2449 wine_tsx11_unlock();
2452 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
2454 return FALSE;
2457 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2458 Pixmap pixmap, GC gc,
2459 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2461 return FALSE;
2463 #endif /* SONAME_LIBXRENDER */