usp10: Handle Ligature Substitution Subtable from GSUB.
[wine/testsucceed.git] / dlls / winex11.drv / xrender.c
blobcc0b2b5086496aa6b69f0a5c5fec78f5f39ac3b2
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(FcPatternGetInteger)
197 MAKE_FUNCPTR(FcPatternGetString)
198 static void *fontconfig_handle;
199 static BOOL fontconfig_installed;
200 #endif
202 #undef MAKE_FUNCPTR
204 static CRITICAL_SECTION xrender_cs;
205 static CRITICAL_SECTION_DEBUG critsect_debug =
207 0, 0, &xrender_cs,
208 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
209 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
211 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
213 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
214 ( ( (ULONG)_x4 << 24 ) | \
215 ( (ULONG)_x3 << 16 ) | \
216 ( (ULONG)_x2 << 8 ) | \
217 (ULONG)_x1 )
219 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
221 #define GASP_GRIDFIT 0x01
222 #define GASP_DOGRAY 0x02
224 #ifdef WORDS_BIGENDIAN
225 #define get_be_word(x) (x)
226 #define NATIVE_BYTE_ORDER MSBFirst
227 #else
228 #define get_be_word(x) RtlUshortByteSwap(x)
229 #define NATIVE_BYTE_ORDER LSBFirst
230 #endif
232 static WXRFormat get_format_without_alpha( WXRFormat format )
234 switch (format)
236 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
237 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
238 default: return format;
242 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
244 templ->id = 0;
245 templ->type = PictTypeDirect;
246 templ->depth = fmt->depth;
247 templ->direct.alpha = fmt->alpha;
248 templ->direct.alphaMask = fmt->alphaMask;
249 templ->direct.red = fmt->red;
250 templ->direct.redMask = fmt->redMask;
251 templ->direct.green = fmt->green;
252 templ->direct.greenMask = fmt->greenMask;
253 templ->direct.blue = fmt->blue;
254 templ->direct.blueMask = fmt->blueMask;
255 templ->colormap = 0;
257 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
259 return TRUE;
262 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
264 if(fmt->depth != screen_depth)
265 return FALSE;
266 if( (fmt->redMask << fmt->red) != visual->red_mask)
267 return FALSE;
268 if( (fmt->greenMask << fmt->green) != visual->green_mask)
269 return FALSE;
270 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
271 return FALSE;
273 /* We never select a default ARGB visual */
274 if(fmt->alphaMask)
275 return FALSE;
277 return TRUE;
280 static int load_xrender_formats(void)
282 unsigned int i;
283 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
285 XRenderPictFormat templ, *pict_format;
287 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
289 wine_tsx11_lock();
290 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
291 if(!pict_format)
293 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
294 if (visual->class == DirectColor)
296 XVisualInfo info;
297 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
298 screen_depth, TrueColor, &info ))
300 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
301 if (pict_format) visual = info.visual;
305 wine_tsx11_unlock();
307 if(pict_format)
309 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
310 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
311 default_format = &wxr_formats[WineXRenderFormatsListSize];
312 WineXRenderFormatsListSize++;
313 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
316 else
318 unsigned long mask = 0;
319 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
321 wine_tsx11_lock();
322 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
323 wine_tsx11_unlock();
325 if(pict_format)
327 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
328 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
329 WineXRenderFormatsListSize++;
330 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
334 return WineXRenderFormatsListSize;
337 /***********************************************************************
338 * X11DRV_XRender_Init
340 * Let's see if our XServer has the extension available
343 void X11DRV_XRender_Init(void)
345 int event_base, i;
347 if (client_side_with_render &&
348 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
349 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
350 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
353 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
354 LOAD_FUNCPTR(XRenderAddGlyphs)
355 LOAD_FUNCPTR(XRenderComposite)
356 LOAD_FUNCPTR(XRenderCompositeString8)
357 LOAD_FUNCPTR(XRenderCompositeString16)
358 LOAD_FUNCPTR(XRenderCompositeString32)
359 LOAD_FUNCPTR(XRenderCompositeText16)
360 LOAD_FUNCPTR(XRenderCreateGlyphSet)
361 LOAD_FUNCPTR(XRenderCreatePicture)
362 LOAD_FUNCPTR(XRenderFillRectangle)
363 LOAD_FUNCPTR(XRenderFindFormat)
364 LOAD_FUNCPTR(XRenderFindVisualFormat)
365 LOAD_FUNCPTR(XRenderFreeGlyphSet)
366 LOAD_FUNCPTR(XRenderFreePicture)
367 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
368 LOAD_FUNCPTR(XRenderQueryExtension)
369 #undef LOAD_FUNCPTR
370 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
371 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
372 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
373 #undef LOAD_OPTIONAL_FUNCPTR
374 #endif
376 wine_tsx11_lock();
377 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
378 wine_tsx11_unlock();
379 if(X11DRV_XRender_Installed) {
380 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
381 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
383 wine_tsx11_unlock();
384 WINE_MESSAGE(
385 "Wine has detected that you probably have a buggy version\n"
386 "of libXrender.so . Because of this client side font rendering\n"
387 "will be disabled. Please upgrade this library.\n");
388 X11DRV_XRender_Installed = FALSE;
389 return;
392 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
393 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
394 X11DRV_XRender_Installed = FALSE;
399 #ifdef SONAME_LIBFONTCONFIG
400 if ((fontconfig_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0)))
402 #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;}
403 LOAD_FUNCPTR(FcConfigSubstitute);
404 LOAD_FUNCPTR(FcDefaultSubstitute);
405 LOAD_FUNCPTR(FcFontMatch);
406 LOAD_FUNCPTR(FcInit);
407 LOAD_FUNCPTR(FcPatternCreate);
408 LOAD_FUNCPTR(FcPatternDestroy);
409 LOAD_FUNCPTR(FcPatternAddInteger);
410 LOAD_FUNCPTR(FcPatternAddString);
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->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->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) { /* reached free list so stop */
664 i = -1;
665 break;
668 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
669 glyphsetCache[i].count++;
670 if(prev_i >= 0) {
671 glyphsetCache[prev_i].next = glyphsetCache[i].next;
672 glyphsetCache[i].next = mru;
673 mru = i;
675 TRACE("found font in cache %d\n", i);
676 return i;
678 prev_i = i;
680 TRACE("font not in cache\n");
681 return -1;
684 static void FreeEntry(int entry)
686 int i, format;
688 for(format = 0; format < AA_MAXVALUE; format++) {
689 gsCacheEntryFormat * formatEntry;
691 if( !glyphsetCache[entry].format[format] )
692 continue;
694 formatEntry = glyphsetCache[entry].format[format];
696 if(formatEntry->glyphset) {
697 wine_tsx11_lock();
698 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
699 wine_tsx11_unlock();
700 formatEntry->glyphset = 0;
702 if(formatEntry->nrealized) {
703 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
704 formatEntry->realized = NULL;
705 if(formatEntry->bitmaps) {
706 for(i = 0; i < formatEntry->nrealized; i++)
707 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
708 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
709 formatEntry->bitmaps = NULL;
711 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
712 formatEntry->gis = NULL;
713 formatEntry->nrealized = 0;
716 HeapFree(GetProcessHeap(), 0, formatEntry);
717 glyphsetCache[entry].format[format] = NULL;
721 static int AllocEntry(void)
723 int best = -1, prev_best = -1, i, prev_i = -1;
725 if(lastfree >= 0) {
726 assert(glyphsetCache[lastfree].count == -1);
727 glyphsetCache[lastfree].count = 1;
728 best = lastfree;
729 lastfree = glyphsetCache[lastfree].next;
730 assert(best != mru);
731 glyphsetCache[best].next = mru;
732 mru = best;
734 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
735 return mru;
738 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
739 if(glyphsetCache[i].count == 0) {
740 best = i;
741 prev_best = prev_i;
743 prev_i = i;
746 if(best >= 0) {
747 TRACE("freeing unused glyphset at cache %d\n", best);
748 FreeEntry(best);
749 glyphsetCache[best].count = 1;
750 if(prev_best >= 0) {
751 glyphsetCache[prev_best].next = glyphsetCache[best].next;
752 glyphsetCache[best].next = mru;
753 mru = best;
754 } else {
755 assert(mru == best);
757 return mru;
760 TRACE("Growing cache\n");
762 if (glyphsetCache)
763 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
764 glyphsetCache,
765 (glyphsetCacheSize + INIT_CACHE_SIZE)
766 * sizeof(*glyphsetCache));
767 else
768 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
769 (glyphsetCacheSize + INIT_CACHE_SIZE)
770 * sizeof(*glyphsetCache));
772 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
773 i++) {
774 glyphsetCache[i].next = i + 1;
775 glyphsetCache[i].count = -1;
777 glyphsetCache[i-1].next = -1;
778 glyphsetCacheSize += INIT_CACHE_SIZE;
780 lastfree = glyphsetCache[best].next;
781 glyphsetCache[best].count = 1;
782 glyphsetCache[best].next = mru;
783 mru = best;
784 TRACE("new free cache slot at %d\n", mru);
785 return mru;
788 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
790 DWORD size;
791 WORD *gasp, *buffer;
792 WORD num_recs;
793 DWORD ppem;
794 TEXTMETRICW tm;
796 *flags = 0;
798 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
799 if(size == GDI_ERROR)
800 return FALSE;
802 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
803 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
805 GetTextMetricsW(physDev->hdc, &tm);
806 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
808 gasp++;
809 num_recs = get_be_word(*gasp);
810 gasp++;
811 while(num_recs--)
813 *flags = get_be_word(*(gasp + 1));
814 if(ppem <= get_be_word(*gasp))
815 break;
816 gasp += 2;
818 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
820 HeapFree(GetProcessHeap(), 0, buffer);
821 return TRUE;
824 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
826 AA_Type ret;
827 WORD flags;
828 UINT font_smoothing_type, font_smoothing_orientation;
830 if (X11DRV_XRender_Installed && subpixel &&
831 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
832 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
834 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
835 &font_smoothing_orientation, 0) &&
836 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
838 ret = AA_BGR;
840 else
841 ret = AA_RGB;
842 /*FIXME
843 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
844 But, Wine's subpixel rendering can support the portrait mode.
847 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
848 ret = AA_Grey;
849 else
850 ret = AA_None;
852 return ret;
855 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
857 int ret;
858 int format;
859 gsCacheEntry *entry;
860 static int hinter = -1;
861 static int subpixel = -1;
862 BOOL font_smoothing;
864 if((ret = LookupEntry(plfsz)) != -1) return ret;
866 ret = AllocEntry();
867 entry = glyphsetCache + ret;
868 entry->lfsz = *plfsz;
869 for( format = 0; format < AA_MAXVALUE; format++ ) {
870 assert( !entry->format[format] );
873 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
875 if(hinter == -1 || subpixel == -1)
877 RASTERIZER_STATUS status;
878 GetRasterizerCaps(&status, sizeof(status));
879 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
880 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
883 switch (plfsz->lf.lfQuality)
885 case ANTIALIASED_QUALITY:
886 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
887 break;
888 case CLEARTYPE_QUALITY:
889 case CLEARTYPE_NATURAL_QUALITY:
890 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
891 break;
892 case DEFAULT_QUALITY:
893 case DRAFT_QUALITY:
894 case PROOF_QUALITY:
895 default:
896 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
897 font_smoothing)
899 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
901 else
902 entry->aa_default = AA_None;
903 break;
906 #ifdef SONAME_LIBFONTCONFIG
907 if (fontconfig_installed)
909 FcPattern *match, *pattern = pFcPatternCreate();
910 FcResult result;
911 char family[LF_FACESIZE * 4];
913 WideCharToMultiByte( CP_UTF8, 0, plfsz->lf.lfFaceName, -1, family, sizeof(family), NULL, NULL );
914 pFcPatternAddString( pattern, FC_FAMILY, (FcChar8 *)family );
915 if (plfsz->lf.lfWeight != FW_DONTCARE)
917 int weight;
918 switch (plfsz->lf.lfWeight)
920 case FW_THIN: weight = FC_WEIGHT_THIN; break;
921 case FW_EXTRALIGHT: weight = FC_WEIGHT_EXTRALIGHT; break;
922 case FW_LIGHT: weight = FC_WEIGHT_LIGHT; break;
923 case FW_NORMAL: weight = FC_WEIGHT_NORMAL; break;
924 case FW_MEDIUM: weight = FC_WEIGHT_MEDIUM; break;
925 case FW_SEMIBOLD: weight = FC_WEIGHT_SEMIBOLD; break;
926 case FW_BOLD: weight = FC_WEIGHT_BOLD; break;
927 case FW_EXTRABOLD: weight = FC_WEIGHT_EXTRABOLD; break;
928 case FW_HEAVY: weight = FC_WEIGHT_HEAVY; break;
929 default: weight = (plfsz->lf.lfWeight - 80) / 4; break;
931 pFcPatternAddInteger( pattern, FC_WEIGHT, weight );
933 pFcPatternAddInteger( pattern, FC_SLANT, plfsz->lf.lfItalic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
934 pFcConfigSubstitute( NULL, pattern, FcMatchPattern );
935 pFcDefaultSubstitute( pattern );
936 if ((match = pFcFontMatch( NULL, pattern, &result )))
938 int rgba;
940 if (pFcPatternGetInteger( match, FC_RGBA, 0, &rgba ) == FcResultMatch)
942 FcChar8 *file;
943 if (pFcPatternGetString( match, FC_FILE, 0, &file ) != FcResultMatch) file = NULL;
945 TRACE( "fontconfig returned rgba %u for font %s file %s\n",
946 rgba, debugstr_w(plfsz->lf.lfFaceName), debugstr_a((char *)file) );
948 switch (rgba)
950 case FC_RGBA_RGB: entry->aa_default = AA_RGB; break;
951 case FC_RGBA_BGR: entry->aa_default = AA_BGR; break;
952 case FC_RGBA_VRGB: entry->aa_default = AA_VRGB; break;
953 case FC_RGBA_VBGR: entry->aa_default = AA_VBGR; break;
954 case FC_RGBA_NONE: entry->aa_default = AA_None; break;
957 pFcPatternDestroy( match );
959 pFcPatternDestroy( pattern );
961 #endif /* SONAME_LIBFONTCONFIG */
963 else
964 entry->aa_default = AA_None;
966 return ret;
969 static void dec_ref_cache(int index)
971 assert(index >= 0);
972 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
973 assert(glyphsetCache[index].count > 0);
974 glyphsetCache[index].count--;
977 static void lfsz_calc_hash(LFANDSIZE *plfsz)
979 DWORD hash = 0, *ptr, two_chars;
980 WORD *pwc;
981 int i;
983 hash ^= plfsz->devsize.cx;
984 hash ^= plfsz->devsize.cy;
985 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
986 hash ^= *ptr;
987 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
988 hash ^= *ptr;
989 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
990 two_chars = *ptr;
991 pwc = (WCHAR *)&two_chars;
992 if(!*pwc) break;
993 *pwc = toupperW(*pwc);
994 pwc++;
995 *pwc = toupperW(*pwc);
996 hash ^= two_chars;
997 if(!*pwc) break;
999 plfsz->hash = hash;
1000 return;
1003 /***********************************************************************
1004 * X11DRV_XRender_Finalize
1006 void X11DRV_XRender_Finalize(void)
1008 int i;
1010 EnterCriticalSection(&xrender_cs);
1011 for(i = mru; i >= 0; i = glyphsetCache[i].next)
1012 FreeEntry(i);
1013 LeaveCriticalSection(&xrender_cs);
1017 /***********************************************************************
1018 * X11DRV_XRender_SelectFont
1020 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
1022 LFANDSIZE lfsz;
1023 struct xrender_info *info;
1025 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
1026 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
1027 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
1028 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
1029 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
1030 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
1031 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
1032 GetWorldTransform( physDev->hdc, &lfsz.xform );
1033 lfsz_calc_hash(&lfsz);
1035 info = get_xrender_info(physDev);
1036 if (!info) return 0;
1038 EnterCriticalSection(&xrender_cs);
1039 if(info->cache_index != -1)
1040 dec_ref_cache(info->cache_index);
1041 info->cache_index = GetCacheEntry(physDev, &lfsz);
1042 LeaveCriticalSection(&xrender_cs);
1043 return 0;
1046 /***********************************************************************
1047 * X11DRV_XRender_SetDeviceClipping
1049 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
1051 if (physDev->xrender->pict)
1053 wine_tsx11_lock();
1054 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1055 physDev->dc_rect.left, physDev->dc_rect.top,
1056 (XRectangle *)data->Buffer, data->rdh.nCount );
1057 wine_tsx11_unlock();
1061 /***********************************************************************
1062 * X11DRV_XRender_DeleteDC
1064 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
1066 X11DRV_XRender_UpdateDrawable(physDev);
1068 EnterCriticalSection(&xrender_cs);
1069 if(physDev->xrender->cache_index != -1)
1070 dec_ref_cache(physDev->xrender->cache_index);
1071 LeaveCriticalSection(&xrender_cs);
1073 HeapFree(GetProcessHeap(), 0, physDev->xrender);
1074 physDev->xrender = NULL;
1075 return;
1078 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
1080 const WineXRenderFormat *fmt;
1081 ColorShifts shifts;
1083 /* When XRender is not around we can only use the screen_depth and when needed we perform depth conversion
1084 * in software. Further we also return the screen depth for paletted formats or TrueColor formats with a low
1085 * number of bits because XRender can't handle paletted formats and 8-bit TrueColor does not exist for XRender. */
1086 if (!X11DRV_XRender_Installed || bits_pixel <= 8)
1087 return FALSE;
1089 if (dib)
1091 X11DRV_PALETTE_ComputeColorShifts(&shifts, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
1092 fmt = get_xrender_format_from_color_shifts(dib->dsBm.bmBitsPixel, &shifts);
1094 /* Common formats should be in our picture format table. */
1095 if (!fmt)
1097 TRACE("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n",
1098 dib->dsBm.bmBitsPixel, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
1099 return FALSE;
1102 else
1104 int red_mask, green_mask, blue_mask;
1106 /* We are dealing with a DDB */
1107 switch (bits_pixel)
1109 case 16:
1110 fmt = get_xrender_format(WXR_FORMAT_R5G6B5);
1111 break;
1112 case 24:
1113 fmt = get_xrender_format(WXR_FORMAT_R8G8B8);
1114 break;
1115 case 32:
1116 fmt = get_xrender_format(WXR_FORMAT_A8R8G8B8);
1117 break;
1118 default:
1119 fmt = NULL;
1122 if (!fmt)
1124 TRACE("Unhandled DDB bits_pixel=%d\n", bits_pixel);
1125 return FALSE;
1128 red_mask = fmt->pict_format->direct.redMask << fmt->pict_format->direct.red;
1129 green_mask = fmt->pict_format->direct.greenMask << fmt->pict_format->direct.green;
1130 blue_mask = fmt->pict_format->direct.blueMask << fmt->pict_format->direct.blue;
1131 X11DRV_PALETTE_ComputeColorShifts(&shifts, red_mask, green_mask, blue_mask);
1134 physBitmap->pixmap_depth = fmt->pict_format->depth;
1135 physBitmap->trueColor = TRUE;
1136 physBitmap->pixmap_color_shifts = shifts;
1137 return TRUE;
1140 /***********************************************************************
1141 * X11DRV_XRender_UpdateDrawable
1143 * Deletes the pict and tile when the drawable changes.
1145 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1147 struct xrender_info *info = physDev->xrender;
1149 if (info->pict || info->pict_src)
1151 wine_tsx11_lock();
1152 XFlush( gdi_display );
1153 if (info->pict)
1155 TRACE("freeing pict = %lx dc = %p\n", info->pict, physDev->hdc);
1156 pXRenderFreePicture(gdi_display, info->pict);
1157 info->pict = 0;
1159 if(info->pict_src)
1161 TRACE("freeing pict = %lx dc = %p\n", info->pict_src, physDev->hdc);
1162 pXRenderFreePicture(gdi_display, info->pict_src);
1163 info->pict_src = 0;
1165 wine_tsx11_unlock();
1168 info->format = NULL;
1171 /************************************************************************
1172 * UploadGlyph
1174 * Helper to ExtTextOut. Must be called inside xrender_cs
1176 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
1178 unsigned int buflen;
1179 char *buf;
1180 Glyph gid;
1181 GLYPHMETRICS gm;
1182 XGlyphInfo gi;
1183 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
1184 gsCacheEntryFormat *formatEntry;
1185 UINT ggo_format = GGO_GLYPH_INDEX;
1186 WXRFormat wxr_format;
1187 static const char zero[4];
1188 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1190 switch(format) {
1191 case AA_Grey:
1192 ggo_format |= WINE_GGO_GRAY16_BITMAP;
1193 break;
1194 case AA_RGB:
1195 ggo_format |= WINE_GGO_HRGB_BITMAP;
1196 break;
1197 case AA_BGR:
1198 ggo_format |= WINE_GGO_HBGR_BITMAP;
1199 break;
1200 case AA_VRGB:
1201 ggo_format |= WINE_GGO_VRGB_BITMAP;
1202 break;
1203 case AA_VBGR:
1204 ggo_format |= WINE_GGO_VBGR_BITMAP;
1205 break;
1207 default:
1208 ERR("aa = %d - not implemented\n", format);
1209 case AA_None:
1210 ggo_format |= GGO_BITMAP;
1211 break;
1214 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1215 if(buflen == GDI_ERROR) {
1216 if(format != AA_None) {
1217 format = AA_None;
1218 entry->aa_default = AA_None;
1219 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
1220 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1222 if(buflen == GDI_ERROR) {
1223 WARN("GetGlyphOutlineW failed\n");
1224 return FALSE;
1226 TRACE("Turning off antialiasing for this monochrome font\n");
1229 /* If there is nothing for the current type, we create the entry. */
1230 if( !entry->format[format] ) {
1231 entry->format[format] = HeapAlloc(GetProcessHeap(),
1232 HEAP_ZERO_MEMORY,
1233 sizeof(gsCacheEntryFormat));
1235 formatEntry = entry->format[format];
1237 if(formatEntry->nrealized <= glyph) {
1238 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1240 if (formatEntry->realized)
1241 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1242 HEAP_ZERO_MEMORY,
1243 formatEntry->realized,
1244 formatEntry->nrealized * sizeof(BOOL));
1245 else
1246 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1247 HEAP_ZERO_MEMORY,
1248 formatEntry->nrealized * sizeof(BOOL));
1250 if(!X11DRV_XRender_Installed) {
1251 if (formatEntry->bitmaps)
1252 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
1253 HEAP_ZERO_MEMORY,
1254 formatEntry->bitmaps,
1255 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1256 else
1257 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
1258 HEAP_ZERO_MEMORY,
1259 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1261 if (formatEntry->gis)
1262 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1263 HEAP_ZERO_MEMORY,
1264 formatEntry->gis,
1265 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1266 else
1267 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1268 HEAP_ZERO_MEMORY,
1269 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1273 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
1274 switch(format) {
1275 case AA_Grey:
1276 wxr_format = WXR_FORMAT_GRAY;
1277 break;
1279 case AA_RGB:
1280 case AA_BGR:
1281 case AA_VRGB:
1282 case AA_VBGR:
1283 wxr_format = WXR_FORMAT_A8R8G8B8;
1284 break;
1286 default:
1287 ERR("aa = %d - not implemented\n", format);
1288 case AA_None:
1289 wxr_format = WXR_FORMAT_MONO;
1290 break;
1293 wine_tsx11_lock();
1294 formatEntry->font_format = get_xrender_format(wxr_format);
1295 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
1296 wine_tsx11_unlock();
1300 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1301 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1302 formatEntry->realized[glyph] = TRUE;
1304 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1305 buflen,
1306 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1307 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1309 gi.width = gm.gmBlackBoxX;
1310 gi.height = gm.gmBlackBoxY;
1311 gi.x = -gm.gmptGlyphOrigin.x;
1312 gi.y = gm.gmptGlyphOrigin.y;
1313 gi.xOff = gm.gmCellIncX;
1314 gi.yOff = gm.gmCellIncY;
1316 if(TRACE_ON(xrender)) {
1317 int pitch, i, j;
1318 char output[300];
1319 unsigned char *line;
1321 if(format == AA_None) {
1322 pitch = ((gi.width + 31) / 32) * 4;
1323 for(i = 0; i < gi.height; i++) {
1324 line = (unsigned char*) buf + i * pitch;
1325 output[0] = '\0';
1326 for(j = 0; j < pitch * 8; j++) {
1327 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1329 TRACE("%s\n", output);
1331 } else {
1332 static const char blks[] = " .:;!o*#";
1333 char str[2];
1335 str[1] = '\0';
1336 pitch = ((gi.width + 3) / 4) * 4;
1337 for(i = 0; i < gi.height; i++) {
1338 line = (unsigned char*) buf + i * pitch;
1339 output[0] = '\0';
1340 for(j = 0; j < pitch; j++) {
1341 str[0] = blks[line[j] >> 5];
1342 strcat(output, str);
1344 TRACE("%s\n", output);
1350 if(formatEntry->glyphset) {
1351 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1352 unsigned char *byte = (unsigned char*) buf, c;
1353 int i = buflen;
1355 while(i--) {
1356 c = *byte;
1358 /* magic to flip bit order */
1359 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1360 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1361 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1363 *byte++ = c;
1366 else if ( format != AA_Grey &&
1367 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1369 unsigned int i, *data = (unsigned int *)buf;
1370 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1372 gid = glyph;
1375 XRenderCompositeText seems to ignore 0x0 glyphs when
1376 AA_None, which means we lose the advance width of glyphs
1377 like the space. We'll pretend that such glyphs are 1x1
1378 bitmaps.
1381 if(buflen == 0)
1382 gi.width = gi.height = 1;
1384 wine_tsx11_lock();
1385 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1386 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1387 wine_tsx11_unlock();
1388 HeapFree(GetProcessHeap(), 0, buf);
1389 } else {
1390 formatEntry->bitmaps[glyph] = buf;
1393 formatEntry->gis[glyph] = gi;
1395 return TRUE;
1398 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1399 void *bitmap, XGlyphInfo *gi)
1401 unsigned char *srcLine = bitmap, *src;
1402 unsigned char bits, bitsMask;
1403 int width = gi->width;
1404 int stride = ((width + 31) & ~31) >> 3;
1405 int height = gi->height;
1406 int w;
1407 int xspan, lenspan;
1409 TRACE("%d, %d\n", x, y);
1410 x -= gi->x;
1411 y -= gi->y;
1412 while (height--)
1414 src = srcLine;
1415 srcLine += stride;
1416 w = width;
1418 bitsMask = 0x80; /* FreeType is always MSB first */
1419 bits = *src++;
1421 xspan = x;
1422 while (w)
1424 if (bits & bitsMask)
1426 lenspan = 0;
1429 lenspan++;
1430 if (lenspan == w)
1431 break;
1432 bitsMask = bitsMask >> 1;
1433 if (!bitsMask)
1435 bits = *src++;
1436 bitsMask = 0x80;
1438 } while (bits & bitsMask);
1439 XFillRectangle (gdi_display, physDev->drawable,
1440 physDev->gc, xspan, y, lenspan, 1);
1441 xspan += lenspan;
1442 w -= lenspan;
1444 else
1448 w--;
1449 xspan++;
1450 if (!w)
1451 break;
1452 bitsMask = bitsMask >> 1;
1453 if (!bitsMask)
1455 bits = *src++;
1456 bitsMask = 0x80;
1458 } while (!(bits & bitsMask));
1461 y++;
1465 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1466 void *bitmap, XGlyphInfo *gi)
1468 unsigned char *srcLine = bitmap, *src, bits;
1469 int width = gi->width;
1470 int stride = ((width + 3) & ~3);
1471 int height = gi->height;
1472 int w;
1473 int xspan, lenspan;
1475 x -= gi->x;
1476 y -= gi->y;
1477 while (height--)
1479 src = srcLine;
1480 srcLine += stride;
1481 w = width;
1483 bits = *src++;
1484 xspan = x;
1485 while (w)
1487 if (bits >= 0x80)
1489 lenspan = 0;
1492 lenspan++;
1493 if (lenspan == w)
1494 break;
1495 bits = *src++;
1496 } while (bits >= 0x80);
1497 XFillRectangle (gdi_display, physDev->drawable,
1498 physDev->gc, xspan, y, lenspan, 1);
1499 xspan += lenspan;
1500 w -= lenspan;
1502 else
1506 w--;
1507 xspan++;
1508 if (!w)
1509 break;
1510 bits = *src++;
1511 } while (bits < 0x80);
1514 y++;
1519 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1521 int s, l;
1523 s = 0;
1524 while ((mask & 1) == 0)
1526 mask >>= 1;
1527 s++;
1529 l = 0;
1530 while ((mask & 1) == 1)
1532 mask >>= 1;
1533 l++;
1535 *shift = s;
1536 *len = l;
1539 static DWORD GetField (DWORD pixel, int shift, int len)
1541 pixel = pixel & (((1 << (len)) - 1) << shift);
1542 pixel = pixel << (32 - (shift + len)) >> 24;
1543 while (len < 8)
1545 pixel |= (pixel >> len);
1546 len <<= 1;
1548 return pixel;
1552 static DWORD PutField (DWORD pixel, int shift, int len)
1554 shift = shift - (8 - len);
1555 if (len <= 8)
1556 pixel &= (((1 << len) - 1) << (8 - len));
1557 if (shift < 0)
1558 pixel >>= -shift;
1559 else
1560 pixel <<= shift;
1561 return pixel;
1564 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1565 int color)
1567 int r_shift, r_len;
1568 int g_shift, g_len;
1569 int b_shift, b_len;
1570 BYTE *maskLine, *mask, m;
1571 int maskStride;
1572 DWORD pixel;
1573 int width, height;
1574 int w, tx;
1575 BYTE src_r, src_g, src_b;
1577 x -= gi->x;
1578 y -= gi->y;
1579 width = gi->width;
1580 height = gi->height;
1582 maskLine = bitmap;
1583 maskStride = (width + 3) & ~3;
1585 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1586 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1587 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1589 src_r = GetField(color, r_shift, r_len);
1590 src_g = GetField(color, g_shift, g_len);
1591 src_b = GetField(color, b_shift, b_len);
1593 for(; height--; y++)
1595 mask = maskLine;
1596 maskLine += maskStride;
1597 w = width;
1598 tx = x;
1600 if(y < 0) continue;
1601 if(y >= image->height) break;
1603 for(; w--; tx++)
1605 if(tx >= image->width) break;
1607 m = *mask++;
1608 if(tx < 0) continue;
1610 if (m == 0xff)
1611 XPutPixel (image, tx, y, color);
1612 else if (m)
1614 BYTE r, g, b;
1616 pixel = XGetPixel (image, tx, y);
1618 r = GetField(pixel, r_shift, r_len);
1619 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1620 g = GetField(pixel, g_shift, g_len);
1621 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1622 b = GetField(pixel, b_shift, b_len);
1623 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1625 pixel = (PutField (r, r_shift, r_len) |
1626 PutField (g, g_shift, g_len) |
1627 PutField (b, b_shift, b_len));
1628 XPutPixel (image, tx, y, pixel);
1634 /*************************************************************
1635 * get_tile_pict
1637 * Returns an appropriate Picture for tiling the text colour.
1638 * Call and use result within the xrender_cs
1640 static Picture get_tile_pict(const WineXRenderFormat *wxr_format, int text_pixel)
1642 static struct
1644 Pixmap xpm;
1645 Picture pict;
1646 int current_color;
1647 } tiles[WXR_NB_FORMATS], *tile;
1648 XRenderColor col;
1650 tile = &tiles[wxr_format->format];
1652 if(!tile->xpm)
1654 XRenderPictureAttributes pa;
1656 wine_tsx11_lock();
1657 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1659 pa.repeat = RepeatNormal;
1660 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1661 wine_tsx11_unlock();
1663 /* init current_color to something different from text_pixel */
1664 tile->current_color = ~text_pixel;
1666 if(wxr_format->format == WXR_FORMAT_MONO)
1668 /* for a 1bpp bitmap we always need a 1 in the tile */
1669 col.red = col.green = col.blue = 0;
1670 col.alpha = 0xffff;
1671 wine_tsx11_lock();
1672 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1673 wine_tsx11_unlock();
1677 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1679 get_xrender_color(wxr_format, text_pixel, &col);
1680 wine_tsx11_lock();
1681 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1682 wine_tsx11_unlock();
1683 tile->current_color = text_pixel;
1685 return tile->pict;
1688 /*************************************************************
1689 * get_mask_pict
1691 * Returns an appropriate Picture for masking with the specified alpha.
1692 * Call and use result within the xrender_cs
1694 static Picture get_mask_pict( int alpha )
1696 static Pixmap pixmap;
1697 static Picture pict;
1698 static int current_alpha;
1700 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1702 if (!pixmap)
1704 const WineXRenderFormat *fmt = get_xrender_format( WXR_FORMAT_A8R8G8B8 );
1705 XRenderPictureAttributes pa;
1707 wine_tsx11_lock();
1708 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1709 pa.repeat = RepeatNormal;
1710 pict = pXRenderCreatePicture( gdi_display, pixmap, fmt->pict_format, CPRepeat, &pa );
1711 wine_tsx11_unlock();
1712 current_alpha = -1;
1715 if (alpha != current_alpha)
1717 XRenderColor col;
1718 col.red = col.green = col.blue = 0;
1719 col.alpha = current_alpha = alpha;
1720 wine_tsx11_lock();
1721 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1722 wine_tsx11_unlock();
1724 return pict;
1727 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1729 return 1;
1732 /***********************************************************************
1733 * X11DRV_XRender_ExtTextOut
1735 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1736 const RECT *lprect, LPCWSTR wstr, UINT count,
1737 const INT *lpDx )
1739 XGCValues xgcval;
1740 gsCacheEntry *entry;
1741 gsCacheEntryFormat *formatEntry;
1742 BOOL retv = FALSE;
1743 int textPixel, backgroundPixel;
1744 HRGN saved_region = 0;
1745 BOOL disable_antialias = FALSE;
1746 AA_Type aa_type = AA_None;
1747 DIBSECTION bmp;
1748 unsigned int idx;
1749 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1750 Picture tile_pict = 0;
1752 /* Do we need to disable antialiasing because of palette mode? */
1753 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1754 TRACE("bitmap is not a DIB\n");
1756 else if (bmp.dsBmih.biBitCount <= 8) {
1757 TRACE("Disabling antialiasing\n");
1758 disable_antialias = TRUE;
1761 xgcval.function = GXcopy;
1762 xgcval.background = physDev->backgroundPixel;
1763 xgcval.fill_style = FillSolid;
1764 wine_tsx11_lock();
1765 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1766 wine_tsx11_unlock();
1768 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1770 if(physDev->depth == 1) {
1771 if((physDev->textPixel & 0xffffff) == 0) {
1772 textPixel = 0;
1773 backgroundPixel = 1;
1774 } else {
1775 textPixel = 1;
1776 backgroundPixel = 0;
1778 } else {
1779 textPixel = physDev->textPixel;
1780 backgroundPixel = physDev->backgroundPixel;
1783 if(flags & ETO_OPAQUE)
1785 wine_tsx11_lock();
1786 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1787 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1788 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1789 lprect->right - lprect->left, lprect->bottom - lprect->top );
1790 wine_tsx11_unlock();
1793 if(count == 0)
1795 retv = TRUE;
1796 goto done_unlock;
1799 if (flags & ETO_CLIPPED)
1801 HRGN clip_region;
1803 clip_region = CreateRectRgnIndirect( lprect );
1804 /* make a copy of the current device region */
1805 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1806 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1807 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1808 DeleteObject( clip_region );
1811 EnterCriticalSection(&xrender_cs);
1813 entry = glyphsetCache + physDev->xrender->cache_index;
1814 if( disable_antialias == FALSE )
1815 aa_type = entry->aa_default;
1816 formatEntry = entry->format[aa_type];
1818 for(idx = 0; idx < count; idx++) {
1819 if( !formatEntry ) {
1820 UploadGlyph(physDev, wstr[idx], aa_type);
1821 /* re-evaluate antialias since aa_default may have changed */
1822 if( disable_antialias == FALSE )
1823 aa_type = entry->aa_default;
1824 formatEntry = entry->format[aa_type];
1825 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1826 UploadGlyph(physDev, wstr[idx], aa_type);
1829 if (!formatEntry)
1831 WARN("could not upload requested glyphs\n");
1832 LeaveCriticalSection(&xrender_cs);
1833 goto done_unlock;
1836 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1837 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1839 if(X11DRV_XRender_Installed)
1841 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1842 POINT offset = {0, 0};
1843 POINT desired, current;
1844 int render_op = PictOpOver;
1845 Picture pict = get_xrender_picture(physDev);
1847 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1848 So we pass zeros to the function and move to our starting position using the first
1849 element of the elts array. */
1851 desired.x = physDev->dc_rect.left + x;
1852 desired.y = physDev->dc_rect.top + y;
1853 current.x = current.y = 0;
1855 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1857 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1859 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1860 render_op = PictOpOutReverse; /* This gives us 'black' text */
1862 for(idx = 0; idx < count; idx++)
1864 elts[idx].glyphset = formatEntry->glyphset;
1865 elts[idx].chars = wstr + idx;
1866 elts[idx].nchars = 1;
1867 elts[idx].xOff = desired.x - current.x;
1868 elts[idx].yOff = desired.y - current.y;
1870 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1871 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1873 if(!lpDx)
1875 desired.x += formatEntry->gis[wstr[idx]].xOff;
1876 desired.y += formatEntry->gis[wstr[idx]].yOff;
1878 else
1880 if(flags & ETO_PDY)
1882 offset.x += lpDx[idx * 2];
1883 offset.y += lpDx[idx * 2 + 1];
1885 else
1886 offset.x += lpDx[idx];
1887 desired.x = physDev->dc_rect.left + x + offset.x;
1888 desired.y = physDev->dc_rect.top + y + offset.y;
1891 wine_tsx11_lock();
1892 /* Make sure we don't have any transforms set from a previous call */
1893 set_xrender_transformation(pict, 1, 1, 0, 0);
1894 pXRenderCompositeText16(gdi_display, render_op,
1895 tile_pict,
1896 pict,
1897 formatEntry->font_format->pict_format,
1898 0, 0, 0, 0, elts, count);
1899 wine_tsx11_unlock();
1900 HeapFree(GetProcessHeap(), 0, elts);
1901 } else {
1902 POINT offset = {0, 0};
1903 wine_tsx11_lock();
1904 XSetForeground( gdi_display, physDev->gc, textPixel );
1906 if(aa_type == AA_None || physDev->depth == 1)
1908 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1910 if(aa_type == AA_None)
1911 sharp_glyph_fn = SharpGlyphMono;
1912 else
1913 sharp_glyph_fn = SharpGlyphGray;
1915 for(idx = 0; idx < count; idx++) {
1916 sharp_glyph_fn(physDev,
1917 physDev->dc_rect.left + x + offset.x,
1918 physDev->dc_rect.top + y + offset.y,
1919 formatEntry->bitmaps[wstr[idx]],
1920 &formatEntry->gis[wstr[idx]]);
1921 if(lpDx)
1923 if(flags & ETO_PDY)
1925 offset.x += lpDx[idx * 2];
1926 offset.y += lpDx[idx * 2 + 1];
1928 else
1929 offset.x += lpDx[idx];
1931 else
1933 offset.x += formatEntry->gis[wstr[idx]].xOff;
1934 offset.y += formatEntry->gis[wstr[idx]].yOff;
1937 } else {
1938 XImage *image;
1939 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1940 RECT extents = {0, 0, 0, 0};
1941 POINT cur = {0, 0};
1942 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1943 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1945 TRACE("drawable %dx%d\n", w, h);
1947 for(idx = 0; idx < count; idx++) {
1948 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1949 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1950 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1951 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1952 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1953 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1954 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1955 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1957 if(lpDx)
1959 if(flags & ETO_PDY)
1961 cur.x += lpDx[idx * 2];
1962 cur.y += lpDx[idx * 2 + 1];
1964 else
1965 cur.x += lpDx[idx];
1967 else
1969 cur.x += formatEntry->gis[wstr[idx]].xOff;
1970 cur.y += formatEntry->gis[wstr[idx]].yOff;
1973 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1974 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1976 if(physDev->dc_rect.left + x + extents.left >= 0) {
1977 image_x = physDev->dc_rect.left + x + extents.left;
1978 image_off_x = 0;
1979 } else {
1980 image_x = 0;
1981 image_off_x = physDev->dc_rect.left + x + extents.left;
1983 if(physDev->dc_rect.top + y + extents.top >= 0) {
1984 image_y = physDev->dc_rect.top + y + extents.top;
1985 image_off_y = 0;
1986 } else {
1987 image_y = 0;
1988 image_off_y = physDev->dc_rect.top + y + extents.top;
1990 if(physDev->dc_rect.left + x + extents.right < w)
1991 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1992 else
1993 image_w = w - image_x;
1994 if(physDev->dc_rect.top + y + extents.bottom < h)
1995 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1996 else
1997 image_h = h - image_y;
1999 if(image_w <= 0 || image_h <= 0) goto no_image;
2001 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
2002 image = XGetImage(gdi_display, physDev->drawable,
2003 image_x, image_y, image_w, image_h,
2004 AllPlanes, ZPixmap);
2005 X11DRV_check_error();
2007 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
2008 gdi_display, (int)physDev->drawable, image_x, image_y,
2009 image_w, image_h, AllPlanes, ZPixmap,
2010 physDev->depth, image);
2011 if(!image) {
2012 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
2013 physDev->depth);
2014 GC gc;
2015 XGCValues gcv;
2017 gcv.graphics_exposures = False;
2018 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
2019 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
2020 image_w, image_h, 0, 0);
2021 XFreeGC(gdi_display, gc);
2022 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
2023 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
2024 ZPixmap);
2025 X11DRV_check_error();
2026 XFreePixmap(gdi_display, xpm);
2028 if(!image) goto no_image;
2030 image->red_mask = visual->red_mask;
2031 image->green_mask = visual->green_mask;
2032 image->blue_mask = visual->blue_mask;
2034 for(idx = 0; idx < count; idx++) {
2035 SmoothGlyphGray(image,
2036 offset.x + image_off_x - extents.left,
2037 offset.y + image_off_y - extents.top,
2038 formatEntry->bitmaps[wstr[idx]],
2039 &formatEntry->gis[wstr[idx]],
2040 physDev->textPixel);
2041 if(lpDx)
2043 if(flags & ETO_PDY)
2045 offset.x += lpDx[idx * 2];
2046 offset.y += lpDx[idx * 2 + 1];
2048 else
2049 offset.x += lpDx[idx];
2051 else
2053 offset.x += formatEntry->gis[wstr[idx]].xOff;
2054 offset.y += formatEntry->gis[wstr[idx]].yOff;
2057 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
2058 image_x, image_y, image_w, image_h);
2059 XDestroyImage(image);
2061 no_image:
2062 wine_tsx11_unlock();
2064 LeaveCriticalSection(&xrender_cs);
2066 if (flags & ETO_CLIPPED)
2068 /* restore the device region */
2069 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
2070 DeleteObject( saved_region );
2073 retv = TRUE;
2075 done_unlock:
2076 X11DRV_UnlockDIBSection( physDev, TRUE );
2077 return retv;
2080 /* Helper function for (stretched) blitting using xrender */
2081 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
2082 int x_src, int y_src, int x_dst, int y_dst,
2083 double xscale, double yscale, int width, int height )
2085 int x_offset, y_offset;
2087 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
2088 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
2089 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
2090 if(xscale != 1.0 || yscale != 1.0)
2092 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2093 * in the wrong quadrant of the x-y plane.
2095 x_offset = (xscale < 0) ? -width : 0;
2096 y_offset = (yscale < 0) ? -height : 0;
2097 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
2099 else
2101 x_offset = x_src;
2102 y_offset = y_src;
2103 set_xrender_transformation(src_pict, 1, 1, 0, 0);
2105 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
2106 x_offset, y_offset, 0, 0, x_dst, y_dst, width, height );
2109 /* Helper function for (stretched) mono->color blitting using xrender */
2110 static void xrender_mono_blit( Picture src_pict, Picture mask_pict, Picture dst_pict,
2111 int x_src, int y_src, double xscale, double yscale, int width, int height )
2113 int x_offset, y_offset;
2115 /* When doing a mono->color blit, 'src_pict' contains a 1x1 picture for tiling, the actual
2116 * source data is in mask_pict. The 'src_pict' data effectively acts as an alpha channel to the
2117 * tile data. We need PictOpOver for correct rendering.
2118 * Note since the 'source data' is in the mask picture, we have to pass x_src / y_src using
2119 * mask_x / mask_y
2121 if (xscale != 1.0 || yscale != 1.0)
2123 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2124 * in the wrong quadrant of the x-y plane.
2126 x_offset = (xscale < 0) ? -width : 0;
2127 y_offset = (yscale < 0) ? -height : 0;
2128 set_xrender_transformation(mask_pict, xscale, yscale, x_src, y_src);
2130 else
2132 x_offset = x_src;
2133 y_offset = y_src;
2134 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
2136 pXRenderComposite(gdi_display, PictOpOver, src_pict, mask_pict, dst_pict,
2137 0, 0, x_offset, y_offset, 0, 0, width, height);
2140 /******************************************************************************
2141 * AlphaBlend
2143 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2144 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2146 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
2147 struct xrender_info *src_info = get_xrender_info( devSrc );
2148 double xscale, yscale;
2149 BOOL use_repeat;
2151 if(!X11DRV_XRender_Installed) {
2152 FIXME("Unable to AlphaBlend without Xrender\n");
2153 return FALSE;
2156 if (devSrc != devDst) X11DRV_LockDIBSection( devSrc, DIB_Status_GdiMod );
2157 X11DRV_LockDIBSection( devDst, DIB_Status_GdiMod );
2159 dst_pict = get_xrender_picture( devDst );
2161 use_repeat = use_source_repeat( devSrc );
2162 if (!use_repeat)
2164 xscale = src->width / (double)dst->width;
2165 yscale = src->height / (double)dst->height;
2167 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2169 if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && src_info->format)
2171 /* we need a source picture with no alpha */
2172 WXRFormat format = get_format_without_alpha( src_info->format->format );
2173 if (format != src_info->format->format)
2175 XRenderPictureAttributes pa;
2176 const WineXRenderFormat *fmt = get_xrender_format( format );
2178 wine_tsx11_lock();
2179 pa.subwindow_mode = IncludeInferiors;
2180 pa.repeat = use_repeat ? RepeatNormal : RepeatNone;
2181 tmp_pict = pXRenderCreatePicture( gdi_display, devSrc->drawable, fmt->pict_format,
2182 CPSubwindowMode|CPRepeat, &pa );
2183 wine_tsx11_unlock();
2184 src_pict = tmp_pict;
2188 if (!src_pict) src_pict = get_xrender_picture_source( devSrc, use_repeat );
2190 EnterCriticalSection( &xrender_cs );
2191 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2193 wine_tsx11_lock();
2194 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2195 devSrc->dc_rect.left + src->visrect.left, devSrc->dc_rect.top + src->visrect.top,
2196 devDst->dc_rect.left + dst->visrect.left, devDst->dc_rect.top + dst->visrect.top,
2197 xscale, yscale,
2198 dst->visrect.right - dst->visrect.left, dst->visrect.bottom - dst->visrect.top );
2199 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2200 wine_tsx11_unlock();
2202 LeaveCriticalSection( &xrender_cs );
2203 if (devSrc != devDst) X11DRV_UnlockDIBSection( devSrc, FALSE );
2204 X11DRV_UnlockDIBSection( devDst, TRUE );
2205 return TRUE;
2209 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2211 /* 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 */
2212 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2213 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2214 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2216 wine_tsx11_lock();
2217 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2219 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2220 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2221 (src_format->format == dst_format->format) )
2223 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2224 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2226 else /* We need depth conversion */
2228 Picture src_pict, dst_pict;
2229 XRenderPictureAttributes pa;
2230 pa.subwindow_mode = IncludeInferiors;
2231 pa.repeat = RepeatNone;
2233 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2234 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2236 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, 0, 0, 0, 0, 1.0, 1.0, width, height);
2237 pXRenderFreePicture(gdi_display, src_pict);
2238 pXRenderFreePicture(gdi_display, dst_pict);
2240 wine_tsx11_unlock();
2243 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2244 Pixmap pixmap, GC gc,
2245 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2247 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
2248 int width = dst->visrect.right - dst->visrect.left;
2249 int height = dst->visrect.bottom - dst->visrect.top;
2250 int x_src = physDevSrc->dc_rect.left + src->visrect.left;
2251 int y_src = physDevSrc->dc_rect.top + src->visrect.top;
2252 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2253 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2254 Picture src_pict=0, dst_pict=0, mask_pict=0;
2255 BOOL use_repeat;
2256 double xscale, yscale;
2258 XRenderPictureAttributes pa;
2259 pa.subwindow_mode = IncludeInferiors;
2260 pa.repeat = RepeatNone;
2262 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n",
2263 physDevSrc->depth, src->width, src->height, x_src, y_src);
2264 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, dst->width, dst->height);
2266 if(!X11DRV_XRender_Installed)
2268 TRACE("Not using XRender since it is not available or disabled\n");
2269 return FALSE;
2272 /* XRender can't handle palettes, so abort */
2273 if(X11DRV_PALETTE_XPixelToPalette)
2274 return FALSE;
2276 /* XRender is of no use in this case */
2277 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2278 return FALSE;
2280 /* Just use traditional X copy when the formats match and we don't need stretching */
2281 if((src_info->format->format == dst_format->format) && !stretch)
2283 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2284 wine_tsx11_lock();
2285 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2286 wine_tsx11_unlock();
2287 return TRUE;
2290 use_repeat = use_source_repeat( physDevSrc );
2291 if (!use_repeat)
2293 xscale = src->width / (double)dst->width;
2294 yscale = src->height / (double)dst->height;
2296 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2298 /* mono -> color */
2299 if(physDevSrc->depth == 1 && physDevDst->depth > 1)
2301 XRenderColor col;
2302 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2304 /* We use the source drawable as a mask */
2305 mask_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2307 /* Use backgroundPixel as the foreground color */
2308 EnterCriticalSection( &xrender_cs );
2309 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2311 /* Create a destination picture and fill it with textPixel color as the background color */
2312 wine_tsx11_lock();
2313 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2314 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2316 xrender_mono_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2318 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2319 wine_tsx11_unlock();
2320 LeaveCriticalSection( &xrender_cs );
2322 else /* color -> color (can be at different depths) or mono -> mono */
2324 if (physDevDst->depth == 32 && physDevSrc->depth < 32) mask_pict = get_no_alpha_mask();
2325 src_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2327 wine_tsx11_lock();
2328 dst_pict = pXRenderCreatePicture(gdi_display,
2329 pixmap, dst_format->pict_format,
2330 CPSubwindowMode|CPRepeat, &pa);
2332 xrender_blit(PictOpSrc, src_pict, mask_pict, dst_pict,
2333 x_src, y_src, 0, 0, xscale, yscale, width, height);
2335 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2336 wine_tsx11_unlock();
2338 return TRUE;
2341 #else /* SONAME_LIBXRENDER */
2343 void X11DRV_XRender_Init(void)
2345 TRACE("XRender support not compiled in.\n");
2346 return;
2349 void X11DRV_XRender_Finalize(void)
2353 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2355 assert(0);
2356 return FALSE;
2359 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2361 assert(0);
2362 return;
2365 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2367 assert(0);
2368 return;
2371 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2372 const RECT *lprect, LPCWSTR wstr, UINT count,
2373 const INT *lpDx )
2375 assert(0);
2376 return FALSE;
2379 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2381 assert(0);
2382 return;
2385 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2386 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2388 FIXME("not supported - XRENDER headers were missing at compile time\n");
2389 return FALSE;
2392 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2394 wine_tsx11_lock();
2395 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2397 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2398 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2399 wine_tsx11_unlock();
2402 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
2404 return FALSE;
2407 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2408 Pixmap pixmap, GC gc,
2409 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2411 return FALSE;
2413 #endif /* SONAME_LIBXRENDER */