winetest: Include language settings in OS info.
[wine/multimedia.git] / dlls / winex11.drv / xrender.c
blobad8e08bb3ddd6aec5cdf2e55fbc9f1b10f280a1e
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 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
352 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
353 LOAD_FUNCPTR(XRenderAddGlyphs)
354 LOAD_FUNCPTR(XRenderComposite)
355 LOAD_FUNCPTR(XRenderCompositeString8)
356 LOAD_FUNCPTR(XRenderCompositeString16)
357 LOAD_FUNCPTR(XRenderCompositeString32)
358 LOAD_FUNCPTR(XRenderCompositeText16)
359 LOAD_FUNCPTR(XRenderCreateGlyphSet)
360 LOAD_FUNCPTR(XRenderCreatePicture)
361 LOAD_FUNCPTR(XRenderFillRectangle)
362 LOAD_FUNCPTR(XRenderFindFormat)
363 LOAD_FUNCPTR(XRenderFindVisualFormat)
364 LOAD_FUNCPTR(XRenderFreeGlyphSet)
365 LOAD_FUNCPTR(XRenderFreePicture)
366 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
367 LOAD_FUNCPTR(XRenderQueryExtension)
368 #undef LOAD_FUNCPTR
369 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
370 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
371 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
372 #undef LOAD_OPTIONAL_FUNCPTR
373 #endif
375 wine_tsx11_lock();
376 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
377 wine_tsx11_unlock();
378 if(X11DRV_XRender_Installed) {
379 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
380 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
382 wine_tsx11_unlock();
383 WINE_MESSAGE(
384 "Wine has detected that you probably have a buggy version\n"
385 "of libXrender.so . Because of this client side font rendering\n"
386 "will be disabled. Please upgrade this library.\n");
387 X11DRV_XRender_Installed = FALSE;
388 return;
391 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
392 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
393 X11DRV_XRender_Installed = FALSE;
398 #ifdef SONAME_LIBFONTCONFIG
399 if ((fontconfig_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0)))
401 #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;}
402 LOAD_FUNCPTR(FcConfigSubstitute);
403 LOAD_FUNCPTR(FcDefaultSubstitute);
404 LOAD_FUNCPTR(FcFontMatch);
405 LOAD_FUNCPTR(FcInit);
406 LOAD_FUNCPTR(FcPatternCreate);
407 LOAD_FUNCPTR(FcPatternDestroy);
408 LOAD_FUNCPTR(FcPatternAddInteger);
409 LOAD_FUNCPTR(FcPatternAddString);
410 LOAD_FUNCPTR(FcPatternGetBool);
411 LOAD_FUNCPTR(FcPatternGetInteger);
412 LOAD_FUNCPTR(FcPatternGetString);
413 #undef LOAD_FUNCPTR
414 fontconfig_installed = pFcInit();
416 else TRACE( "cannot find the fontconfig library " SONAME_LIBFONTCONFIG "\n" );
417 #endif
419 sym_not_found:
420 if(X11DRV_XRender_Installed || client_side_with_core)
422 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
423 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
425 glyphsetCacheSize = INIT_CACHE_SIZE;
426 lastfree = 0;
427 for(i = 0; i < INIT_CACHE_SIZE; i++) {
428 glyphsetCache[i].next = i + 1;
429 glyphsetCache[i].count = -1;
431 glyphsetCache[i-1].next = -1;
432 using_client_side_fonts = 1;
434 if(!X11DRV_XRender_Installed) {
435 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
436 if(screen_depth <= 8 || !client_side_antialias_with_core)
437 antialias = 0;
438 } else {
439 if(screen_depth <= 8 || !client_side_antialias_with_render)
440 antialias = 0;
443 else TRACE("Using X11 core fonts\n");
446 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
447 static void get_xrender_color(const WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
449 XRenderPictFormat *pf = wxr_format->pict_format;
451 if(pf->direct.redMask)
452 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
453 else
454 dst_color->red = 0;
456 if(pf->direct.greenMask)
457 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
458 else
459 dst_color->green = 0;
461 if(pf->direct.blueMask)
462 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
463 else
464 dst_color->blue = 0;
466 dst_color->alpha = 0xffff;
469 static const WineXRenderFormat *get_xrender_format(WXRFormat format)
471 int i;
472 for(i=0; i<WineXRenderFormatsListSize; i++)
474 if(wxr_formats[i].format == format)
476 TRACE("Returning wxr_format=%#x\n", format);
477 return &wxr_formats[i];
480 return NULL;
483 static const WineXRenderFormat *get_xrender_format_from_color_shifts(int depth, ColorShifts *shifts)
485 int redMask, greenMask, blueMask;
486 unsigned int i;
488 if(depth == 1)
489 return get_xrender_format(WXR_FORMAT_MONO);
491 /* physDevs of a depth <=8, don't have color_shifts set and XRender can't handle those except for 1-bit */
492 if(!shifts)
493 return default_format;
495 redMask = shifts->physicalRed.max << shifts->physicalRed.shift;
496 greenMask = shifts->physicalGreen.max << shifts->physicalGreen.shift;
497 blueMask = shifts->physicalBlue.max << shifts->physicalBlue.shift;
499 /* Try to locate a format which matches the specification of the dibsection. */
500 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
502 if( depth == wxr_formats_template[i].depth &&
503 redMask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
504 greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
505 blueMask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
508 /* When we reach this stage the format was found in our template table but this doesn't mean that
509 * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
511 return get_xrender_format(wxr_formats_template[i].wxr_format);
515 /* This should not happen because when we reach 'shifts' must have been set and we only allows shifts which are backed by X */
516 ERR("No XRender format found!\n");
517 return NULL;
520 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
521 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
523 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
524 XTransform xform = {{
525 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
526 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
527 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
530 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
531 #endif
534 /* check if we can use repeating instead of scaling for the specified source DC */
535 static BOOL use_source_repeat( X11DRV_PDEVICE *physDev )
537 return (physDev->bitmap &&
538 physDev->drawable_rect.right - physDev->drawable_rect.left == 1 &&
539 physDev->drawable_rect.bottom - physDev->drawable_rect.top == 1);
542 static struct xrender_info *get_xrender_info(X11DRV_PDEVICE *physDev)
544 if(!physDev->xrender)
546 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physDev->xrender));
548 if(!physDev->xrender)
550 ERR("Unable to allocate XRENDERINFO!\n");
551 return NULL;
553 physDev->xrender->cache_index = -1;
555 if (!physDev->xrender->format)
556 physDev->xrender->format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
558 return physDev->xrender;
561 static Picture get_xrender_picture(X11DRV_PDEVICE *physDev)
563 struct xrender_info *info = get_xrender_info(physDev);
564 if (!info) return 0;
566 if (!info->pict && info->format)
568 XRenderPictureAttributes pa;
569 RGNDATA *clip = X11DRV_GetRegionData( physDev->region, 0 );
571 wine_tsx11_lock();
572 pa.subwindow_mode = IncludeInferiors;
573 info->pict = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
574 CPSubwindowMode, &pa);
575 if (info->pict && clip)
576 pXRenderSetPictureClipRectangles( gdi_display, info->pict,
577 physDev->dc_rect.left, physDev->dc_rect.top,
578 (XRectangle *)clip->Buffer, clip->rdh.nCount );
579 wine_tsx11_unlock();
580 TRACE("Allocing pict=%lx dc=%p drawable=%08lx\n", info->pict, physDev->dev.hdc, physDev->drawable);
581 HeapFree( GetProcessHeap(), 0, clip );
584 return info->pict;
587 static Picture get_xrender_picture_source(X11DRV_PDEVICE *physDev, BOOL repeat)
589 struct xrender_info *info = get_xrender_info(physDev);
590 if (!info) return 0;
592 if (!info->pict_src && info->format)
594 XRenderPictureAttributes pa;
596 wine_tsx11_lock();
597 pa.subwindow_mode = IncludeInferiors;
598 pa.repeat = repeat ? RepeatNormal : RepeatNone;
599 info->pict_src = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
600 CPSubwindowMode|CPRepeat, &pa);
601 wine_tsx11_unlock();
603 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
604 info->pict_src, physDev->dev.hdc, physDev->drawable, pa.repeat);
607 return info->pict_src;
610 /* return a mask picture used to force alpha to 0 */
611 static Picture get_no_alpha_mask(void)
613 static Pixmap pixmap;
614 static Picture pict;
616 wine_tsx11_lock();
617 if (!pict)
619 const WineXRenderFormat *fmt = get_xrender_format( WXR_FORMAT_A8R8G8B8 );
620 XRenderPictureAttributes pa;
621 XRenderColor col;
623 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
624 pa.repeat = RepeatNormal;
625 pa.component_alpha = True;
626 pict = pXRenderCreatePicture( gdi_display, pixmap, fmt->pict_format,
627 CPRepeat|CPComponentAlpha, &pa );
628 col.red = col.green = col.blue = 0xffff;
629 col.alpha = 0;
630 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
632 wine_tsx11_unlock();
633 return pict;
636 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
638 if(p1->hash != p2->hash) return TRUE;
639 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
640 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
641 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
642 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
645 #if 0
646 static void walk_cache(void)
648 int i;
650 EnterCriticalSection(&xrender_cs);
651 for(i=mru; i >= 0; i = glyphsetCache[i].next)
652 TRACE("item %d\n", i);
653 LeaveCriticalSection(&xrender_cs);
655 #endif
657 static int LookupEntry(LFANDSIZE *plfsz)
659 int i, prev_i = -1;
661 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
662 TRACE("%d\n", i);
663 if(glyphsetCache[i].count == -1) break; /* reached free list so stop */
665 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
666 glyphsetCache[i].count++;
667 if(prev_i >= 0) {
668 glyphsetCache[prev_i].next = glyphsetCache[i].next;
669 glyphsetCache[i].next = mru;
670 mru = i;
672 TRACE("found font in cache %d\n", i);
673 return i;
675 prev_i = i;
677 TRACE("font not in cache\n");
678 return -1;
681 static void FreeEntry(int entry)
683 int i, format;
685 for(format = 0; format < AA_MAXVALUE; format++) {
686 gsCacheEntryFormat * formatEntry;
688 if( !glyphsetCache[entry].format[format] )
689 continue;
691 formatEntry = glyphsetCache[entry].format[format];
693 if(formatEntry->glyphset) {
694 wine_tsx11_lock();
695 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
696 wine_tsx11_unlock();
697 formatEntry->glyphset = 0;
699 if(formatEntry->nrealized) {
700 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
701 formatEntry->realized = NULL;
702 if(formatEntry->bitmaps) {
703 for(i = 0; i < formatEntry->nrealized; i++)
704 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
705 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
706 formatEntry->bitmaps = NULL;
708 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
709 formatEntry->gis = NULL;
710 formatEntry->nrealized = 0;
713 HeapFree(GetProcessHeap(), 0, formatEntry);
714 glyphsetCache[entry].format[format] = NULL;
718 static int AllocEntry(void)
720 int best = -1, prev_best = -1, i, prev_i = -1;
722 if(lastfree >= 0) {
723 assert(glyphsetCache[lastfree].count == -1);
724 glyphsetCache[lastfree].count = 1;
725 best = lastfree;
726 lastfree = glyphsetCache[lastfree].next;
727 assert(best != mru);
728 glyphsetCache[best].next = mru;
729 mru = best;
731 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
732 return mru;
735 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
736 if(glyphsetCache[i].count == 0) {
737 best = i;
738 prev_best = prev_i;
740 prev_i = i;
743 if(best >= 0) {
744 TRACE("freeing unused glyphset at cache %d\n", best);
745 FreeEntry(best);
746 glyphsetCache[best].count = 1;
747 if(prev_best >= 0) {
748 glyphsetCache[prev_best].next = glyphsetCache[best].next;
749 glyphsetCache[best].next = mru;
750 mru = best;
751 } else {
752 assert(mru == best);
754 return mru;
757 TRACE("Growing cache\n");
759 if (glyphsetCache)
760 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
761 glyphsetCache,
762 (glyphsetCacheSize + INIT_CACHE_SIZE)
763 * sizeof(*glyphsetCache));
764 else
765 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
766 (glyphsetCacheSize + INIT_CACHE_SIZE)
767 * sizeof(*glyphsetCache));
769 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
770 i++) {
771 glyphsetCache[i].next = i + 1;
772 glyphsetCache[i].count = -1;
774 glyphsetCache[i-1].next = -1;
775 glyphsetCacheSize += INIT_CACHE_SIZE;
777 lastfree = glyphsetCache[best].next;
778 glyphsetCache[best].count = 1;
779 glyphsetCache[best].next = mru;
780 mru = best;
781 TRACE("new free cache slot at %d\n", mru);
782 return mru;
785 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
787 DWORD size;
788 WORD *gasp, *buffer;
789 WORD num_recs;
790 DWORD ppem;
791 TEXTMETRICW tm;
793 *flags = 0;
795 size = GetFontData(physDev->dev.hdc, MS_GASP_TAG, 0, NULL, 0);
796 if(size == GDI_ERROR)
797 return FALSE;
799 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
800 GetFontData(physDev->dev.hdc, MS_GASP_TAG, 0, gasp, size);
802 GetTextMetricsW(physDev->dev.hdc, &tm);
803 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
805 gasp++;
806 num_recs = get_be_word(*gasp);
807 gasp++;
808 while(num_recs--)
810 *flags = get_be_word(*(gasp + 1));
811 if(ppem <= get_be_word(*gasp))
812 break;
813 gasp += 2;
815 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
817 HeapFree(GetProcessHeap(), 0, buffer);
818 return TRUE;
821 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
823 AA_Type ret;
824 WORD flags;
825 UINT font_smoothing_type, font_smoothing_orientation;
827 if (X11DRV_XRender_Installed && subpixel &&
828 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
829 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
831 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
832 &font_smoothing_orientation, 0) &&
833 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
835 ret = AA_BGR;
837 else
838 ret = AA_RGB;
839 /*FIXME
840 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
841 But, Wine's subpixel rendering can support the portrait mode.
844 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
845 ret = AA_Grey;
846 else
847 ret = AA_None;
849 return ret;
852 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
854 int ret;
855 int format;
856 gsCacheEntry *entry;
857 static int hinter = -1;
858 static int subpixel = -1;
859 BOOL font_smoothing;
861 if((ret = LookupEntry(plfsz)) != -1) return ret;
863 ret = AllocEntry();
864 entry = glyphsetCache + ret;
865 entry->lfsz = *plfsz;
866 for( format = 0; format < AA_MAXVALUE; format++ ) {
867 assert( !entry->format[format] );
870 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
872 if(hinter == -1 || subpixel == -1)
874 RASTERIZER_STATUS status;
875 GetRasterizerCaps(&status, sizeof(status));
876 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
877 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
880 switch (plfsz->lf.lfQuality)
882 case ANTIALIASED_QUALITY:
883 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
884 return ret; /* ignore further configuration */
885 case CLEARTYPE_QUALITY:
886 case CLEARTYPE_NATURAL_QUALITY:
887 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
888 break;
889 case DEFAULT_QUALITY:
890 case DRAFT_QUALITY:
891 case PROOF_QUALITY:
892 default:
893 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
894 font_smoothing)
896 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
898 else
899 entry->aa_default = AA_None;
900 break;
903 font_smoothing = TRUE; /* default to enabled */
904 #ifdef SONAME_LIBFONTCONFIG
905 if (fontconfig_installed)
907 FcPattern *match, *pattern = pFcPatternCreate();
908 FcResult result;
909 char family[LF_FACESIZE * 4];
911 WideCharToMultiByte( CP_UTF8, 0, plfsz->lf.lfFaceName, -1, family, sizeof(family), NULL, NULL );
912 pFcPatternAddString( pattern, FC_FAMILY, (FcChar8 *)family );
913 if (plfsz->lf.lfWeight != FW_DONTCARE)
915 int weight;
916 switch (plfsz->lf.lfWeight)
918 case FW_THIN: weight = FC_WEIGHT_THIN; break;
919 case FW_EXTRALIGHT: weight = FC_WEIGHT_EXTRALIGHT; break;
920 case FW_LIGHT: weight = FC_WEIGHT_LIGHT; break;
921 case FW_NORMAL: weight = FC_WEIGHT_NORMAL; break;
922 case FW_MEDIUM: weight = FC_WEIGHT_MEDIUM; break;
923 case FW_SEMIBOLD: weight = FC_WEIGHT_SEMIBOLD; break;
924 case FW_BOLD: weight = FC_WEIGHT_BOLD; break;
925 case FW_EXTRABOLD: weight = FC_WEIGHT_EXTRABOLD; break;
926 case FW_HEAVY: weight = FC_WEIGHT_HEAVY; break;
927 default: weight = (plfsz->lf.lfWeight - 80) / 4; break;
929 pFcPatternAddInteger( pattern, FC_WEIGHT, weight );
931 pFcPatternAddInteger( pattern, FC_SLANT, plfsz->lf.lfItalic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
932 pFcConfigSubstitute( NULL, pattern, FcMatchPattern );
933 pFcDefaultSubstitute( pattern );
934 if ((match = pFcFontMatch( NULL, pattern, &result )))
936 int rgba;
937 FcBool antialias;
939 if (pFcPatternGetBool( match, FC_ANTIALIAS, 0, &antialias ) != FcResultMatch)
940 antialias = TRUE;
941 if (pFcPatternGetInteger( match, FC_RGBA, 0, &rgba ) == FcResultMatch)
943 FcChar8 *file;
944 if (pFcPatternGetString( match, FC_FILE, 0, &file ) != FcResultMatch) file = NULL;
946 TRACE( "fontconfig returned rgba %u antialias %u for font %s file %s\n",
947 rgba, antialias, debugstr_w(plfsz->lf.lfFaceName), debugstr_a((char *)file) );
949 switch (rgba)
951 case FC_RGBA_RGB: entry->aa_default = AA_RGB; break;
952 case FC_RGBA_BGR: entry->aa_default = AA_BGR; break;
953 case FC_RGBA_VRGB: entry->aa_default = AA_VRGB; break;
954 case FC_RGBA_VBGR: entry->aa_default = AA_VBGR; break;
955 case FC_RGBA_NONE: entry->aa_default = AA_Grey; break;
958 if (!antialias) font_smoothing = FALSE;
959 pFcPatternDestroy( match );
961 pFcPatternDestroy( pattern );
963 #endif /* SONAME_LIBFONTCONFIG */
965 /* now check Xft resources */
967 char *value;
968 BOOL antialias = TRUE;
970 wine_tsx11_lock();
971 if ((value = XGetDefault( gdi_display, "Xft", "antialias" )))
973 if (tolower(value[0]) == 'f' || tolower(value[0]) == 'n' ||
974 value[0] == '0' || !strcasecmp( value, "off" ))
975 antialias = FALSE;
977 if ((value = XGetDefault( gdi_display, "Xft", "rgba" )))
979 TRACE( "Xft resource returned rgba '%s' antialias %u\n", value, antialias );
980 if (!strcmp( value, "rgb" )) entry->aa_default = AA_RGB;
981 else if (!strcmp( value, "bgr" )) entry->aa_default = AA_BGR;
982 else if (!strcmp( value, "vrgb" )) entry->aa_default = AA_VRGB;
983 else if (!strcmp( value, "vbgr" )) entry->aa_default = AA_VBGR;
984 else if (!strcmp( value, "none" )) entry->aa_default = AA_Grey;
986 wine_tsx11_unlock();
987 if (!antialias) font_smoothing = FALSE;
990 if (!font_smoothing) entry->aa_default = AA_None;
992 /* we can't support subpixel without xrender */
993 if (!X11DRV_XRender_Installed && entry->aa_default > AA_Grey) entry->aa_default = AA_Grey;
995 else
996 entry->aa_default = AA_None;
998 return ret;
1001 static void dec_ref_cache(int index)
1003 assert(index >= 0);
1004 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
1005 assert(glyphsetCache[index].count > 0);
1006 glyphsetCache[index].count--;
1009 static void lfsz_calc_hash(LFANDSIZE *plfsz)
1011 DWORD hash = 0, *ptr, two_chars;
1012 WORD *pwc;
1013 int i;
1015 hash ^= plfsz->devsize.cx;
1016 hash ^= plfsz->devsize.cy;
1017 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
1018 hash ^= *ptr;
1019 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
1020 hash ^= *ptr;
1021 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
1022 two_chars = *ptr;
1023 pwc = (WCHAR *)&two_chars;
1024 if(!*pwc) break;
1025 *pwc = toupperW(*pwc);
1026 pwc++;
1027 *pwc = toupperW(*pwc);
1028 hash ^= two_chars;
1029 if(!*pwc) break;
1031 plfsz->hash = hash;
1032 return;
1035 /***********************************************************************
1036 * X11DRV_XRender_Finalize
1038 void X11DRV_XRender_Finalize(void)
1040 int i;
1042 EnterCriticalSection(&xrender_cs);
1043 for(i = mru; i >= 0; i = glyphsetCache[i].next)
1044 FreeEntry(i);
1045 LeaveCriticalSection(&xrender_cs);
1049 /***********************************************************************
1050 * X11DRV_XRender_SelectFont
1052 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1054 LFANDSIZE lfsz;
1055 struct xrender_info *info;
1057 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
1058 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
1059 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
1060 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
1061 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
1062 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
1063 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
1065 GetTransform( physDev->dev.hdc, 0x204, &lfsz.xform );
1066 TRACE("font transform %f %f %f %f\n", lfsz.xform.eM11, lfsz.xform.eM12,
1067 lfsz.xform.eM21, lfsz.xform.eM22);
1069 /* Not used fields, would break hashing */
1070 lfsz.xform.eDx = lfsz.xform.eDy = 0;
1072 lfsz_calc_hash(&lfsz);
1074 info = get_xrender_info(physDev);
1075 if (!info) return 0;
1077 EnterCriticalSection(&xrender_cs);
1078 if(info->cache_index != -1)
1079 dec_ref_cache(info->cache_index);
1080 info->cache_index = GetCacheEntry(physDev, &lfsz);
1081 LeaveCriticalSection(&xrender_cs);
1082 return 0;
1085 /***********************************************************************
1086 * X11DRV_XRender_SetDeviceClipping
1088 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
1090 if (physDev->xrender->pict)
1092 wine_tsx11_lock();
1093 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1094 physDev->dc_rect.left, physDev->dc_rect.top,
1095 (XRectangle *)data->Buffer, data->rdh.nCount );
1096 wine_tsx11_unlock();
1100 /***********************************************************************
1101 * X11DRV_XRender_DeleteDC
1103 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1105 X11DRV_XRender_UpdateDrawable(physDev);
1107 EnterCriticalSection(&xrender_cs);
1108 if(physDev->xrender->cache_index != -1)
1109 dec_ref_cache(physDev->xrender->cache_index);
1110 LeaveCriticalSection(&xrender_cs);
1112 HeapFree(GetProcessHeap(), 0, physDev->xrender);
1113 physDev->xrender = NULL;
1114 return;
1117 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
1119 const WineXRenderFormat *fmt;
1120 ColorShifts shifts;
1122 /* When XRender is not around we can only use the screen_depth and when needed we perform depth conversion
1123 * in software. Further we also return the screen depth for paletted formats or TrueColor formats with a low
1124 * number of bits because XRender can't handle paletted formats and 8-bit TrueColor does not exist for XRender. */
1125 if (!X11DRV_XRender_Installed || bits_pixel <= 8)
1126 return FALSE;
1128 if (dib)
1130 const DWORD *bitfields;
1131 static const DWORD bitfields_32[3] = {0xff0000, 0x00ff00, 0x0000ff};
1132 static const DWORD bitfields_16[3] = {0x7c00, 0x03e0, 0x001f};
1134 if(dib->dsBmih.biCompression == BI_BITFIELDS)
1135 bitfields = dib->dsBitfields;
1136 else if(bits_pixel == 24 || bits_pixel == 32)
1137 bitfields = bitfields_32;
1138 else
1139 bitfields = bitfields_16;
1141 X11DRV_PALETTE_ComputeColorShifts(&shifts, bitfields[0], bitfields[1], bitfields[2]);
1142 fmt = get_xrender_format_from_color_shifts(dib->dsBm.bmBitsPixel, &shifts);
1144 /* Common formats should be in our picture format table. */
1145 if (!fmt)
1147 TRACE("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n",
1148 dib->dsBm.bmBitsPixel, bitfields[0], bitfields[1], bitfields[2]);
1149 return FALSE;
1152 else
1154 int red_mask, green_mask, blue_mask;
1156 /* We are dealing with a DDB */
1157 switch (bits_pixel)
1159 case 16:
1160 fmt = get_xrender_format(WXR_FORMAT_R5G6B5);
1161 break;
1162 case 24:
1163 fmt = get_xrender_format(WXR_FORMAT_R8G8B8);
1164 break;
1165 case 32:
1166 fmt = get_xrender_format(WXR_FORMAT_A8R8G8B8);
1167 break;
1168 default:
1169 fmt = NULL;
1172 if (!fmt)
1174 TRACE("Unhandled DDB bits_pixel=%d\n", bits_pixel);
1175 return FALSE;
1178 red_mask = fmt->pict_format->direct.redMask << fmt->pict_format->direct.red;
1179 green_mask = fmt->pict_format->direct.greenMask << fmt->pict_format->direct.green;
1180 blue_mask = fmt->pict_format->direct.blueMask << fmt->pict_format->direct.blue;
1181 X11DRV_PALETTE_ComputeColorShifts(&shifts, red_mask, green_mask, blue_mask);
1184 physBitmap->pixmap_depth = fmt->pict_format->depth;
1185 physBitmap->trueColor = TRUE;
1186 physBitmap->pixmap_color_shifts = shifts;
1187 return TRUE;
1190 /***********************************************************************
1191 * X11DRV_XRender_UpdateDrawable
1193 * Deletes the pict and tile when the drawable changes.
1195 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1197 struct xrender_info *info = physDev->xrender;
1199 if (info->pict || info->pict_src)
1201 wine_tsx11_lock();
1202 XFlush( gdi_display );
1203 if (info->pict)
1205 TRACE("freeing pict = %lx dc = %p\n", info->pict, physDev->dev.hdc);
1206 pXRenderFreePicture(gdi_display, info->pict);
1207 info->pict = 0;
1209 if(info->pict_src)
1211 TRACE("freeing pict = %lx dc = %p\n", info->pict_src, physDev->dev.hdc);
1212 pXRenderFreePicture(gdi_display, info->pict_src);
1213 info->pict_src = 0;
1215 wine_tsx11_unlock();
1218 info->format = NULL;
1221 /************************************************************************
1222 * UploadGlyph
1224 * Helper to ExtTextOut. Must be called inside xrender_cs
1226 static void UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
1228 unsigned int buflen;
1229 char *buf;
1230 Glyph gid;
1231 GLYPHMETRICS gm;
1232 XGlyphInfo gi;
1233 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
1234 gsCacheEntryFormat *formatEntry;
1235 UINT ggo_format = GGO_GLYPH_INDEX;
1236 WXRFormat wxr_format;
1237 static const char zero[4];
1238 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1240 switch(format) {
1241 case AA_Grey:
1242 ggo_format |= WINE_GGO_GRAY16_BITMAP;
1243 break;
1244 case AA_RGB:
1245 ggo_format |= WINE_GGO_HRGB_BITMAP;
1246 break;
1247 case AA_BGR:
1248 ggo_format |= WINE_GGO_HBGR_BITMAP;
1249 break;
1250 case AA_VRGB:
1251 ggo_format |= WINE_GGO_VRGB_BITMAP;
1252 break;
1253 case AA_VBGR:
1254 ggo_format |= WINE_GGO_VBGR_BITMAP;
1255 break;
1257 default:
1258 ERR("aa = %d - not implemented\n", format);
1259 case AA_None:
1260 ggo_format |= GGO_BITMAP;
1261 break;
1264 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1265 if(buflen == GDI_ERROR) {
1266 if(format != AA_None) {
1267 format = AA_None;
1268 entry->aa_default = AA_None;
1269 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
1270 buflen = GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1272 if(buflen == GDI_ERROR) {
1273 WARN("GetGlyphOutlineW failed using default glyph\n");
1274 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0, ggo_format, &gm, 0, NULL, &identity);
1275 if(buflen == GDI_ERROR) {
1276 WARN("GetGlyphOutlineW failed for default glyph trying for space\n");
1277 buflen = GetGlyphOutlineW(physDev->dev.hdc, 0x20, ggo_format, &gm, 0, NULL, &identity);
1278 if(buflen == GDI_ERROR) {
1279 ERR("GetGlyphOutlineW for all attempts unable to upload a glyph\n");
1280 return;
1284 TRACE("Turning off antialiasing for this monochrome font\n");
1287 /* If there is nothing for the current type, we create the entry. */
1288 if( !entry->format[format] ) {
1289 entry->format[format] = HeapAlloc(GetProcessHeap(),
1290 HEAP_ZERO_MEMORY,
1291 sizeof(gsCacheEntryFormat));
1293 formatEntry = entry->format[format];
1295 if(formatEntry->nrealized <= glyph) {
1296 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1298 if (formatEntry->realized)
1299 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1300 HEAP_ZERO_MEMORY,
1301 formatEntry->realized,
1302 formatEntry->nrealized * sizeof(BOOL));
1303 else
1304 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1305 HEAP_ZERO_MEMORY,
1306 formatEntry->nrealized * sizeof(BOOL));
1308 if(!X11DRV_XRender_Installed) {
1309 if (formatEntry->bitmaps)
1310 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
1311 HEAP_ZERO_MEMORY,
1312 formatEntry->bitmaps,
1313 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1314 else
1315 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
1316 HEAP_ZERO_MEMORY,
1317 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1319 if (formatEntry->gis)
1320 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1321 HEAP_ZERO_MEMORY,
1322 formatEntry->gis,
1323 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1324 else
1325 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1326 HEAP_ZERO_MEMORY,
1327 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1331 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
1332 switch(format) {
1333 case AA_Grey:
1334 wxr_format = WXR_FORMAT_GRAY;
1335 break;
1337 case AA_RGB:
1338 case AA_BGR:
1339 case AA_VRGB:
1340 case AA_VBGR:
1341 wxr_format = WXR_FORMAT_A8R8G8B8;
1342 break;
1344 default:
1345 ERR("aa = %d - not implemented\n", format);
1346 case AA_None:
1347 wxr_format = WXR_FORMAT_MONO;
1348 break;
1351 wine_tsx11_lock();
1352 formatEntry->font_format = get_xrender_format(wxr_format);
1353 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
1354 wine_tsx11_unlock();
1358 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1359 GetGlyphOutlineW(physDev->dev.hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1360 formatEntry->realized[glyph] = TRUE;
1362 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1363 buflen,
1364 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1365 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1367 gi.width = gm.gmBlackBoxX;
1368 gi.height = gm.gmBlackBoxY;
1369 gi.x = -gm.gmptGlyphOrigin.x;
1370 gi.y = gm.gmptGlyphOrigin.y;
1371 gi.xOff = gm.gmCellIncX;
1372 gi.yOff = gm.gmCellIncY;
1374 if(TRACE_ON(xrender)) {
1375 int pitch, i, j;
1376 char output[300];
1377 unsigned char *line;
1379 if(format == AA_None) {
1380 pitch = ((gi.width + 31) / 32) * 4;
1381 for(i = 0; i < gi.height; i++) {
1382 line = (unsigned char*) buf + i * pitch;
1383 output[0] = '\0';
1384 for(j = 0; j < pitch * 8; j++) {
1385 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1387 TRACE("%s\n", output);
1389 } else {
1390 static const char blks[] = " .:;!o*#";
1391 char str[2];
1393 str[1] = '\0';
1394 pitch = ((gi.width + 3) / 4) * 4;
1395 for(i = 0; i < gi.height; i++) {
1396 line = (unsigned char*) buf + i * pitch;
1397 output[0] = '\0';
1398 for(j = 0; j < pitch; j++) {
1399 str[0] = blks[line[j] >> 5];
1400 strcat(output, str);
1402 TRACE("%s\n", output);
1408 if(formatEntry->glyphset) {
1409 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1410 unsigned char *byte = (unsigned char*) buf, c;
1411 int i = buflen;
1413 while(i--) {
1414 c = *byte;
1416 /* magic to flip bit order */
1417 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1418 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1419 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1421 *byte++ = c;
1424 else if ( format != AA_Grey &&
1425 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1427 unsigned int i, *data = (unsigned int *)buf;
1428 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1430 gid = glyph;
1433 XRenderCompositeText seems to ignore 0x0 glyphs when
1434 AA_None, which means we lose the advance width of glyphs
1435 like the space. We'll pretend that such glyphs are 1x1
1436 bitmaps.
1439 if(buflen == 0)
1440 gi.width = gi.height = 1;
1442 wine_tsx11_lock();
1443 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1444 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1445 wine_tsx11_unlock();
1446 HeapFree(GetProcessHeap(), 0, buf);
1447 } else {
1448 formatEntry->bitmaps[glyph] = buf;
1451 formatEntry->gis[glyph] = gi;
1454 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1455 void *bitmap, XGlyphInfo *gi)
1457 unsigned char *srcLine = bitmap, *src;
1458 unsigned char bits, bitsMask;
1459 int width = gi->width;
1460 int stride = ((width + 31) & ~31) >> 3;
1461 int height = gi->height;
1462 int w;
1463 int xspan, lenspan;
1465 TRACE("%d, %d\n", x, y);
1466 x -= gi->x;
1467 y -= gi->y;
1468 while (height--)
1470 src = srcLine;
1471 srcLine += stride;
1472 w = width;
1474 bitsMask = 0x80; /* FreeType is always MSB first */
1475 bits = *src++;
1477 xspan = x;
1478 while (w)
1480 if (bits & bitsMask)
1482 lenspan = 0;
1485 lenspan++;
1486 if (lenspan == w)
1487 break;
1488 bitsMask = bitsMask >> 1;
1489 if (!bitsMask)
1491 bits = *src++;
1492 bitsMask = 0x80;
1494 } while (bits & bitsMask);
1495 XFillRectangle (gdi_display, physDev->drawable,
1496 physDev->gc, xspan, y, lenspan, 1);
1497 xspan += lenspan;
1498 w -= lenspan;
1500 else
1504 w--;
1505 xspan++;
1506 if (!w)
1507 break;
1508 bitsMask = bitsMask >> 1;
1509 if (!bitsMask)
1511 bits = *src++;
1512 bitsMask = 0x80;
1514 } while (!(bits & bitsMask));
1517 y++;
1521 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1522 void *bitmap, XGlyphInfo *gi)
1524 unsigned char *srcLine = bitmap, *src, bits;
1525 int width = gi->width;
1526 int stride = ((width + 3) & ~3);
1527 int height = gi->height;
1528 int w;
1529 int xspan, lenspan;
1531 x -= gi->x;
1532 y -= gi->y;
1533 while (height--)
1535 src = srcLine;
1536 srcLine += stride;
1537 w = width;
1539 bits = *src++;
1540 xspan = x;
1541 while (w)
1543 if (bits >= 0x80)
1545 lenspan = 0;
1548 lenspan++;
1549 if (lenspan == w)
1550 break;
1551 bits = *src++;
1552 } while (bits >= 0x80);
1553 XFillRectangle (gdi_display, physDev->drawable,
1554 physDev->gc, xspan, y, lenspan, 1);
1555 xspan += lenspan;
1556 w -= lenspan;
1558 else
1562 w--;
1563 xspan++;
1564 if (!w)
1565 break;
1566 bits = *src++;
1567 } while (bits < 0x80);
1570 y++;
1575 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1577 int s, l;
1579 s = 0;
1580 while ((mask & 1) == 0)
1582 mask >>= 1;
1583 s++;
1585 l = 0;
1586 while ((mask & 1) == 1)
1588 mask >>= 1;
1589 l++;
1591 *shift = s;
1592 *len = l;
1595 static DWORD GetField (DWORD pixel, int shift, int len)
1597 pixel = pixel & (((1 << (len)) - 1) << shift);
1598 pixel = pixel << (32 - (shift + len)) >> 24;
1599 while (len < 8)
1601 pixel |= (pixel >> len);
1602 len <<= 1;
1604 return pixel;
1608 static DWORD PutField (DWORD pixel, int shift, int len)
1610 shift = shift - (8 - len);
1611 if (len <= 8)
1612 pixel &= (((1 << len) - 1) << (8 - len));
1613 if (shift < 0)
1614 pixel >>= -shift;
1615 else
1616 pixel <<= shift;
1617 return pixel;
1620 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1621 int color)
1623 int r_shift, r_len;
1624 int g_shift, g_len;
1625 int b_shift, b_len;
1626 BYTE *maskLine, *mask, m;
1627 int maskStride;
1628 DWORD pixel;
1629 int width, height;
1630 int w, tx;
1631 BYTE src_r, src_g, src_b;
1633 x -= gi->x;
1634 y -= gi->y;
1635 width = gi->width;
1636 height = gi->height;
1638 maskLine = bitmap;
1639 maskStride = (width + 3) & ~3;
1641 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1642 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1643 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1645 src_r = GetField(color, r_shift, r_len);
1646 src_g = GetField(color, g_shift, g_len);
1647 src_b = GetField(color, b_shift, b_len);
1649 for(; height--; y++)
1651 mask = maskLine;
1652 maskLine += maskStride;
1653 w = width;
1654 tx = x;
1656 if(y < 0) continue;
1657 if(y >= image->height) break;
1659 for(; w--; tx++)
1661 if(tx >= image->width) break;
1663 m = *mask++;
1664 if(tx < 0) continue;
1666 if (m == 0xff)
1667 XPutPixel (image, tx, y, color);
1668 else if (m)
1670 BYTE r, g, b;
1672 pixel = XGetPixel (image, tx, y);
1674 r = GetField(pixel, r_shift, r_len);
1675 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1676 g = GetField(pixel, g_shift, g_len);
1677 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1678 b = GetField(pixel, b_shift, b_len);
1679 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1681 pixel = (PutField (r, r_shift, r_len) |
1682 PutField (g, g_shift, g_len) |
1683 PutField (b, b_shift, b_len));
1684 XPutPixel (image, tx, y, pixel);
1690 /*************************************************************
1691 * get_tile_pict
1693 * Returns an appropriate Picture for tiling the text colour.
1694 * Call and use result within the xrender_cs
1696 static Picture get_tile_pict(const WineXRenderFormat *wxr_format, int text_pixel)
1698 static struct
1700 Pixmap xpm;
1701 Picture pict;
1702 int current_color;
1703 } tiles[WXR_NB_FORMATS], *tile;
1704 XRenderColor col;
1706 tile = &tiles[wxr_format->format];
1708 if(!tile->xpm)
1710 XRenderPictureAttributes pa;
1712 wine_tsx11_lock();
1713 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1715 pa.repeat = RepeatNormal;
1716 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1717 wine_tsx11_unlock();
1719 /* init current_color to something different from text_pixel */
1720 tile->current_color = ~text_pixel;
1722 if(wxr_format->format == WXR_FORMAT_MONO)
1724 /* for a 1bpp bitmap we always need a 1 in the tile */
1725 col.red = col.green = col.blue = 0;
1726 col.alpha = 0xffff;
1727 wine_tsx11_lock();
1728 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1729 wine_tsx11_unlock();
1733 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1735 get_xrender_color(wxr_format, text_pixel, &col);
1736 wine_tsx11_lock();
1737 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1738 wine_tsx11_unlock();
1739 tile->current_color = text_pixel;
1741 return tile->pict;
1744 /*************************************************************
1745 * get_mask_pict
1747 * Returns an appropriate Picture for masking with the specified alpha.
1748 * Call and use result within the xrender_cs
1750 static Picture get_mask_pict( int alpha )
1752 static Pixmap pixmap;
1753 static Picture pict;
1754 static int current_alpha;
1756 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1758 if (!pixmap)
1760 const WineXRenderFormat *fmt = get_xrender_format( WXR_FORMAT_A8R8G8B8 );
1761 XRenderPictureAttributes pa;
1763 wine_tsx11_lock();
1764 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1765 pa.repeat = RepeatNormal;
1766 pict = pXRenderCreatePicture( gdi_display, pixmap, fmt->pict_format, CPRepeat, &pa );
1767 wine_tsx11_unlock();
1768 current_alpha = -1;
1771 if (alpha != current_alpha)
1773 XRenderColor col;
1774 col.red = col.green = col.blue = 0;
1775 col.alpha = current_alpha = alpha;
1776 wine_tsx11_lock();
1777 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1778 wine_tsx11_unlock();
1780 return pict;
1783 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1785 return 1;
1788 /********************************************************************
1789 * is_dib_with_colortable
1791 * Return TRUE if physdev is backed by a dibsection with <= 8 bits per pixel
1793 static inline BOOL is_dib_with_colortable( X11DRV_PDEVICE *physDev )
1795 DIBSECTION dib;
1797 if( physDev->bitmap && GetObjectW( physDev->bitmap->hbitmap, sizeof(dib), &dib ) == sizeof(dib) &&
1798 dib.dsBmih.biBitCount <= 8 )
1799 return TRUE;
1801 return FALSE;
1804 /***********************************************************************
1805 * X11DRV_XRender_ExtTextOut
1807 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1808 const RECT *lprect, LPCWSTR wstr, UINT count,
1809 const INT *lpDx )
1811 XGCValues xgcval;
1812 gsCacheEntry *entry;
1813 gsCacheEntryFormat *formatEntry;
1814 BOOL retv = FALSE;
1815 int textPixel, backgroundPixel;
1816 HRGN saved_region = 0;
1817 BOOL disable_antialias = FALSE;
1818 AA_Type aa_type = AA_None;
1819 unsigned int idx;
1820 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1821 Picture tile_pict = 0;
1823 if(is_dib_with_colortable( physDev ))
1825 TRACE("Disabling antialiasing\n");
1826 disable_antialias = TRUE;
1829 xgcval.function = GXcopy;
1830 xgcval.background = physDev->backgroundPixel;
1831 xgcval.fill_style = FillSolid;
1832 wine_tsx11_lock();
1833 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1834 wine_tsx11_unlock();
1836 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1838 if(physDev->depth == 1) {
1839 if((physDev->textPixel & 0xffffff) == 0) {
1840 textPixel = 0;
1841 backgroundPixel = 1;
1842 } else {
1843 textPixel = 1;
1844 backgroundPixel = 0;
1846 } else {
1847 textPixel = physDev->textPixel;
1848 backgroundPixel = physDev->backgroundPixel;
1851 if(flags & ETO_OPAQUE)
1853 wine_tsx11_lock();
1854 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1855 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1856 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1857 lprect->right - lprect->left, lprect->bottom - lprect->top );
1858 wine_tsx11_unlock();
1861 if(count == 0)
1863 retv = TRUE;
1864 goto done_unlock;
1867 if (flags & ETO_CLIPPED)
1869 HRGN clip_region = CreateRectRgnIndirect( lprect );
1870 saved_region = add_extra_clipping_region( physDev, clip_region );
1871 DeleteObject( clip_region );
1874 EnterCriticalSection(&xrender_cs);
1876 entry = glyphsetCache + physDev->xrender->cache_index;
1877 if( disable_antialias == FALSE )
1878 aa_type = entry->aa_default;
1879 formatEntry = entry->format[aa_type];
1881 for(idx = 0; idx < count; idx++) {
1882 if( !formatEntry ) {
1883 UploadGlyph(physDev, wstr[idx], aa_type);
1884 /* re-evaluate antialias since aa_default may have changed */
1885 if( disable_antialias == FALSE )
1886 aa_type = entry->aa_default;
1887 formatEntry = entry->format[aa_type];
1888 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1889 UploadGlyph(physDev, wstr[idx], aa_type);
1892 if (!formatEntry)
1894 WARN("could not upload requested glyphs\n");
1895 LeaveCriticalSection(&xrender_cs);
1896 goto done_unlock;
1899 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1900 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1902 if(X11DRV_XRender_Installed)
1904 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1905 POINT offset = {0, 0};
1906 POINT desired, current;
1907 int render_op = PictOpOver;
1908 Picture pict = get_xrender_picture(physDev);
1910 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1911 So we pass zeros to the function and move to our starting position using the first
1912 element of the elts array. */
1914 desired.x = physDev->dc_rect.left + x;
1915 desired.y = physDev->dc_rect.top + y;
1916 current.x = current.y = 0;
1918 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1920 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1922 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1923 render_op = PictOpOutReverse; /* This gives us 'black' text */
1925 for(idx = 0; idx < count; idx++)
1927 elts[idx].glyphset = formatEntry->glyphset;
1928 elts[idx].chars = wstr + idx;
1929 elts[idx].nchars = 1;
1930 elts[idx].xOff = desired.x - current.x;
1931 elts[idx].yOff = desired.y - current.y;
1933 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1934 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1936 if(!lpDx)
1938 desired.x += formatEntry->gis[wstr[idx]].xOff;
1939 desired.y += formatEntry->gis[wstr[idx]].yOff;
1941 else
1943 if(flags & ETO_PDY)
1945 offset.x += lpDx[idx * 2];
1946 offset.y += lpDx[idx * 2 + 1];
1948 else
1949 offset.x += lpDx[idx];
1950 desired.x = physDev->dc_rect.left + x + offset.x;
1951 desired.y = physDev->dc_rect.top + y + offset.y;
1954 wine_tsx11_lock();
1955 /* Make sure we don't have any transforms set from a previous call */
1956 set_xrender_transformation(pict, 1, 1, 0, 0);
1957 pXRenderCompositeText16(gdi_display, render_op,
1958 tile_pict,
1959 pict,
1960 formatEntry->font_format->pict_format,
1961 0, 0, 0, 0, elts, count);
1962 wine_tsx11_unlock();
1963 HeapFree(GetProcessHeap(), 0, elts);
1964 } else {
1965 POINT offset = {0, 0};
1966 wine_tsx11_lock();
1967 XSetForeground( gdi_display, physDev->gc, textPixel );
1969 if(aa_type == AA_None || physDev->depth == 1)
1971 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1973 if(aa_type == AA_None)
1974 sharp_glyph_fn = SharpGlyphMono;
1975 else
1976 sharp_glyph_fn = SharpGlyphGray;
1978 for(idx = 0; idx < count; idx++) {
1979 sharp_glyph_fn(physDev,
1980 physDev->dc_rect.left + x + offset.x,
1981 physDev->dc_rect.top + y + offset.y,
1982 formatEntry->bitmaps[wstr[idx]],
1983 &formatEntry->gis[wstr[idx]]);
1984 if(lpDx)
1986 if(flags & ETO_PDY)
1988 offset.x += lpDx[idx * 2];
1989 offset.y += lpDx[idx * 2 + 1];
1991 else
1992 offset.x += lpDx[idx];
1994 else
1996 offset.x += formatEntry->gis[wstr[idx]].xOff;
1997 offset.y += formatEntry->gis[wstr[idx]].yOff;
2000 } else {
2001 XImage *image;
2002 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
2003 RECT extents = {0, 0, 0, 0};
2004 POINT cur = {0, 0};
2005 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
2006 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
2008 TRACE("drawable %dx%d\n", w, h);
2010 for(idx = 0; idx < count; idx++) {
2011 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
2012 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
2013 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
2014 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
2015 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
2016 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
2017 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
2018 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
2020 if(lpDx)
2022 if(flags & ETO_PDY)
2024 cur.x += lpDx[idx * 2];
2025 cur.y += lpDx[idx * 2 + 1];
2027 else
2028 cur.x += lpDx[idx];
2030 else
2032 cur.x += formatEntry->gis[wstr[idx]].xOff;
2033 cur.y += formatEntry->gis[wstr[idx]].yOff;
2036 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
2037 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
2039 if(physDev->dc_rect.left + x + extents.left >= 0) {
2040 image_x = physDev->dc_rect.left + x + extents.left;
2041 image_off_x = 0;
2042 } else {
2043 image_x = 0;
2044 image_off_x = physDev->dc_rect.left + x + extents.left;
2046 if(physDev->dc_rect.top + y + extents.top >= 0) {
2047 image_y = physDev->dc_rect.top + y + extents.top;
2048 image_off_y = 0;
2049 } else {
2050 image_y = 0;
2051 image_off_y = physDev->dc_rect.top + y + extents.top;
2053 if(physDev->dc_rect.left + x + extents.right < w)
2054 image_w = physDev->dc_rect.left + x + extents.right - image_x;
2055 else
2056 image_w = w - image_x;
2057 if(physDev->dc_rect.top + y + extents.bottom < h)
2058 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
2059 else
2060 image_h = h - image_y;
2062 if(image_w <= 0 || image_h <= 0) goto no_image;
2064 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
2065 image = XGetImage(gdi_display, physDev->drawable,
2066 image_x, image_y, image_w, image_h,
2067 AllPlanes, ZPixmap);
2068 X11DRV_check_error();
2070 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
2071 gdi_display, (int)physDev->drawable, image_x, image_y,
2072 image_w, image_h, AllPlanes, ZPixmap,
2073 physDev->depth, image);
2074 if(!image) {
2075 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
2076 physDev->depth);
2077 GC gc;
2078 XGCValues gcv;
2080 gcv.graphics_exposures = False;
2081 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
2082 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
2083 image_w, image_h, 0, 0);
2084 XFreeGC(gdi_display, gc);
2085 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
2086 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
2087 ZPixmap);
2088 X11DRV_check_error();
2089 XFreePixmap(gdi_display, xpm);
2091 if(!image) goto no_image;
2093 image->red_mask = visual->red_mask;
2094 image->green_mask = visual->green_mask;
2095 image->blue_mask = visual->blue_mask;
2097 for(idx = 0; idx < count; idx++) {
2098 SmoothGlyphGray(image,
2099 offset.x + image_off_x - extents.left,
2100 offset.y + image_off_y - extents.top,
2101 formatEntry->bitmaps[wstr[idx]],
2102 &formatEntry->gis[wstr[idx]],
2103 physDev->textPixel);
2104 if(lpDx)
2106 if(flags & ETO_PDY)
2108 offset.x += lpDx[idx * 2];
2109 offset.y += lpDx[idx * 2 + 1];
2111 else
2112 offset.x += lpDx[idx];
2114 else
2116 offset.x += formatEntry->gis[wstr[idx]].xOff;
2117 offset.y += formatEntry->gis[wstr[idx]].yOff;
2120 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
2121 image_x, image_y, image_w, image_h);
2122 XDestroyImage(image);
2124 no_image:
2125 wine_tsx11_unlock();
2127 LeaveCriticalSection(&xrender_cs);
2129 if (saved_region) restore_clipping_region( physDev, saved_region );
2131 retv = TRUE;
2133 done_unlock:
2134 X11DRV_UnlockDIBSection( physDev, TRUE );
2135 return retv;
2138 /* Helper function for (stretched) blitting using xrender */
2139 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
2140 int x_src, int y_src, int x_dst, int y_dst,
2141 double xscale, double yscale, int width, int height )
2143 int x_offset, y_offset;
2145 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
2146 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
2147 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
2148 if(xscale != 1.0 || yscale != 1.0)
2150 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2151 * in the wrong quadrant of the x-y plane.
2153 x_offset = (xscale < 0) ? -width : 0;
2154 y_offset = (yscale < 0) ? -height : 0;
2155 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
2157 else
2159 x_offset = x_src;
2160 y_offset = y_src;
2161 set_xrender_transformation(src_pict, 1, 1, 0, 0);
2163 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
2164 x_offset, y_offset, 0, 0, x_dst, y_dst, width, height );
2167 /* Helper function for (stretched) mono->color blitting using xrender */
2168 static void xrender_mono_blit( Picture src_pict, Picture mask_pict, Picture dst_pict,
2169 int x_src, int y_src, double xscale, double yscale, int width, int height )
2171 int x_offset, y_offset;
2173 /* When doing a mono->color blit, 'src_pict' contains a 1x1 picture for tiling, the actual
2174 * source data is in mask_pict. The 'src_pict' data effectively acts as an alpha channel to the
2175 * tile data. We need PictOpOver for correct rendering.
2176 * Note since the 'source data' is in the mask picture, we have to pass x_src / y_src using
2177 * mask_x / mask_y
2179 if (xscale != 1.0 || yscale != 1.0)
2181 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2182 * in the wrong quadrant of the x-y plane.
2184 x_offset = (xscale < 0) ? -width : 0;
2185 y_offset = (yscale < 0) ? -height : 0;
2186 set_xrender_transformation(mask_pict, xscale, yscale, x_src, y_src);
2188 else
2190 x_offset = x_src;
2191 y_offset = y_src;
2192 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
2194 pXRenderComposite(gdi_display, PictOpOver, src_pict, mask_pict, dst_pict,
2195 0, 0, x_offset, y_offset, 0, 0, width, height);
2198 /******************************************************************************
2199 * AlphaBlend
2201 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, struct bitblt_coords *dst,
2202 X11DRV_PDEVICE *devSrc, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2204 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
2205 struct xrender_info *src_info = get_xrender_info( devSrc );
2206 double xscale, yscale;
2207 BOOL use_repeat;
2209 if(!X11DRV_XRender_Installed) {
2210 FIXME("Unable to AlphaBlend without Xrender\n");
2211 return FALSE;
2214 if (devSrc != devDst) X11DRV_LockDIBSection( devSrc, DIB_Status_GdiMod );
2215 X11DRV_LockDIBSection( devDst, DIB_Status_GdiMod );
2217 dst_pict = get_xrender_picture( devDst );
2219 use_repeat = use_source_repeat( devSrc );
2220 if (!use_repeat)
2222 xscale = src->width / (double)dst->width;
2223 yscale = src->height / (double)dst->height;
2225 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2227 if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && src_info->format)
2229 /* we need a source picture with no alpha */
2230 WXRFormat format = get_format_without_alpha( src_info->format->format );
2231 if (format != src_info->format->format)
2233 XRenderPictureAttributes pa;
2234 const WineXRenderFormat *fmt = get_xrender_format( format );
2236 wine_tsx11_lock();
2237 pa.subwindow_mode = IncludeInferiors;
2238 pa.repeat = use_repeat ? RepeatNormal : RepeatNone;
2239 tmp_pict = pXRenderCreatePicture( gdi_display, devSrc->drawable, fmt->pict_format,
2240 CPSubwindowMode|CPRepeat, &pa );
2241 wine_tsx11_unlock();
2242 src_pict = tmp_pict;
2246 if (!src_pict) src_pict = get_xrender_picture_source( devSrc, use_repeat );
2248 EnterCriticalSection( &xrender_cs );
2249 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2251 wine_tsx11_lock();
2252 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2253 devSrc->dc_rect.left + src->visrect.left, devSrc->dc_rect.top + src->visrect.top,
2254 devDst->dc_rect.left + dst->visrect.left, devDst->dc_rect.top + dst->visrect.top,
2255 xscale, yscale,
2256 dst->visrect.right - dst->visrect.left, dst->visrect.bottom - dst->visrect.top );
2257 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2258 wine_tsx11_unlock();
2260 LeaveCriticalSection( &xrender_cs );
2261 if (devSrc != devDst) X11DRV_UnlockDIBSection( devSrc, FALSE );
2262 X11DRV_UnlockDIBSection( devDst, TRUE );
2263 return TRUE;
2267 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2269 /* 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 */
2270 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2271 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2272 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2274 wine_tsx11_lock();
2275 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2277 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2278 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2279 (src_format->format == dst_format->format) )
2281 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2282 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2284 else /* We need depth conversion */
2286 Picture src_pict, dst_pict;
2287 XRenderPictureAttributes pa;
2288 pa.subwindow_mode = IncludeInferiors;
2289 pa.repeat = RepeatNone;
2291 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2292 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2294 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, 0, 0, 0, 0, 1.0, 1.0, width, height);
2295 pXRenderFreePicture(gdi_display, src_pict);
2296 pXRenderFreePicture(gdi_display, dst_pict);
2298 wine_tsx11_unlock();
2301 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2302 Pixmap pixmap, GC gc,
2303 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2305 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
2306 int width = dst->visrect.right - dst->visrect.left;
2307 int height = dst->visrect.bottom - dst->visrect.top;
2308 int x_src = physDevSrc->dc_rect.left + src->visrect.left;
2309 int y_src = physDevSrc->dc_rect.top + src->visrect.top;
2310 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2311 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2312 Picture src_pict=0, dst_pict=0, mask_pict=0;
2313 BOOL use_repeat;
2314 double xscale, yscale;
2316 XRenderPictureAttributes pa;
2317 pa.subwindow_mode = IncludeInferiors;
2318 pa.repeat = RepeatNone;
2320 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n",
2321 physDevSrc->depth, src->width, src->height, x_src, y_src);
2322 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, dst->width, dst->height);
2324 if(!X11DRV_XRender_Installed)
2326 TRACE("Not using XRender since it is not available or disabled\n");
2327 return FALSE;
2330 /* XRender can't handle palettes, so abort */
2331 if(X11DRV_PALETTE_XPixelToPalette)
2332 return FALSE;
2334 /* XRender is of no use in this case */
2335 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2336 return FALSE;
2338 /* Just use traditional X copy when the formats match and we don't need stretching */
2339 if((src_info->format->format == dst_format->format) && !stretch)
2341 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2342 wine_tsx11_lock();
2343 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2344 wine_tsx11_unlock();
2345 return TRUE;
2348 use_repeat = use_source_repeat( physDevSrc );
2349 if (!use_repeat)
2351 xscale = src->width / (double)dst->width;
2352 yscale = src->height / (double)dst->height;
2354 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2356 /* mono -> color */
2357 if(physDevSrc->depth == 1 && physDevDst->depth > 1)
2359 XRenderColor col;
2360 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2362 /* We use the source drawable as a mask */
2363 mask_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2365 /* Use backgroundPixel as the foreground color */
2366 EnterCriticalSection( &xrender_cs );
2367 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2369 /* Create a destination picture and fill it with textPixel color as the background color */
2370 wine_tsx11_lock();
2371 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2372 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2374 xrender_mono_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2376 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2377 wine_tsx11_unlock();
2378 LeaveCriticalSection( &xrender_cs );
2380 else /* color -> color (can be at different depths) or mono -> mono */
2382 if (physDevDst->depth == 32 && physDevSrc->depth < 32) mask_pict = get_no_alpha_mask();
2383 src_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2385 wine_tsx11_lock();
2386 dst_pict = pXRenderCreatePicture(gdi_display,
2387 pixmap, dst_format->pict_format,
2388 CPSubwindowMode|CPRepeat, &pa);
2390 xrender_blit(PictOpSrc, src_pict, mask_pict, dst_pict,
2391 x_src, y_src, 0, 0, xscale, yscale, width, height);
2393 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2394 wine_tsx11_unlock();
2396 return TRUE;
2399 #else /* SONAME_LIBXRENDER */
2401 void X11DRV_XRender_Init(void)
2403 TRACE("XRender support not compiled in.\n");
2404 return;
2407 void X11DRV_XRender_Finalize(void)
2411 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2413 assert(0);
2414 return FALSE;
2417 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2419 assert(0);
2420 return;
2423 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2425 assert(0);
2426 return;
2429 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2430 const RECT *lprect, LPCWSTR wstr, UINT count,
2431 const INT *lpDx )
2433 assert(0);
2434 return FALSE;
2437 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2439 assert(0);
2440 return;
2443 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2444 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2446 FIXME("not supported - XRENDER headers were missing at compile time\n");
2447 return FALSE;
2450 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2452 wine_tsx11_lock();
2453 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2455 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2456 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2457 wine_tsx11_unlock();
2460 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
2462 return FALSE;
2465 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2466 Pixmap pixmap, GC gc,
2467 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2469 return FALSE;
2471 #endif /* SONAME_LIBXRENDER */