2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
5 * Copyright 2009 Roderick Colenbrander
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
25 #include "wine/port.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;
49 #include <X11/extensions/Xrender.h>
51 #ifndef RepeatNone /* added in 0.10 */
53 #define RepeatNormal 1
55 #define RepeatReflect 3
58 #define MAX_FORMATS 10
59 typedef enum wine_xrformat
73 typedef struct wine_xrender_format_template
78 unsigned int alphaMask;
82 unsigned int greenMask;
84 unsigned int blueMask;
85 } WineXRenderFormatTemplate;
87 static const WineXRenderFormatTemplate wxr_formats_template[] =
89 /* Format depth alpha mask red mask green mask blue mask*/
90 {WXR_FORMAT_MONO, 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
91 {WXR_FORMAT_GRAY, 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
92 {WXR_FORMAT_X1R5G5B5, 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
93 {WXR_FORMAT_X1B5G5R5, 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
94 {WXR_FORMAT_R5G6B5, 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
95 {WXR_FORMAT_B5G6R5, 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
96 {WXR_FORMAT_R8G8B8, 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
97 {WXR_FORMAT_B8G8R8, 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
98 {WXR_FORMAT_A8R8G8B8, 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
99 {WXR_FORMAT_X8R8G8B8, 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff }
102 typedef struct wine_xrender_format
105 XRenderPictFormat *pict_format;
108 static WineXRenderFormat wxr_formats[MAX_FORMATS];
109 static int WineXRenderFormatsListSize = 0;
110 static WineXRenderFormat *default_format = NULL;
116 SIZE devsize; /* size in device coords */
120 #define INITIAL_REALIZED_BUF_SIZE 128
122 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
127 const WineXRenderFormat *font_format;
132 } gsCacheEntryFormat;
138 gsCacheEntryFormat * format[AA_MAXVALUE];
148 const WineXRenderFormat *format;
151 static gsCacheEntry *glyphsetCache = NULL;
152 static DWORD glyphsetCacheSize = 0;
153 static INT lastfree = -1;
156 #define INIT_CACHE_SIZE 10
158 static int antialias = 1;
160 static void *xrender_handle;
162 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
163 MAKE_FUNCPTR(XRenderAddGlyphs)
164 MAKE_FUNCPTR(XRenderComposite)
165 MAKE_FUNCPTR(XRenderCompositeString8)
166 MAKE_FUNCPTR(XRenderCompositeString16)
167 MAKE_FUNCPTR(XRenderCompositeString32)
168 MAKE_FUNCPTR(XRenderCompositeText16)
169 MAKE_FUNCPTR(XRenderCreateGlyphSet)
170 MAKE_FUNCPTR(XRenderCreatePicture)
171 MAKE_FUNCPTR(XRenderFillRectangle)
172 MAKE_FUNCPTR(XRenderFindFormat)
173 MAKE_FUNCPTR(XRenderFindVisualFormat)
174 MAKE_FUNCPTR(XRenderFreeGlyphSet)
175 MAKE_FUNCPTR(XRenderFreePicture)
176 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
177 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
178 MAKE_FUNCPTR(XRenderSetPictureTransform)
180 MAKE_FUNCPTR(XRenderQueryExtension)
183 static CRITICAL_SECTION xrender_cs;
184 static CRITICAL_SECTION_DEBUG critsect_debug =
187 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
188 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
190 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
192 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
193 ( ( (ULONG)_x4 << 24 ) | \
194 ( (ULONG)_x3 << 16 ) | \
195 ( (ULONG)_x2 << 8 ) | \
198 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
200 #define GASP_GRIDFIT 0x01
201 #define GASP_DOGRAY 0x02
203 #ifdef WORDS_BIGENDIAN
204 #define get_be_word(x) (x)
205 #define NATIVE_BYTE_ORDER MSBFirst
207 #define get_be_word(x) RtlUshortByteSwap(x)
208 #define NATIVE_BYTE_ORDER LSBFirst
211 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
214 templ->type = PictTypeDirect;
215 templ->depth = fmt->depth;
216 templ->direct.alpha = fmt->alpha;
217 templ->direct.alphaMask = fmt->alphaMask;
218 templ->direct.red = fmt->red;
219 templ->direct.redMask = fmt->redMask;
220 templ->direct.green = fmt->green;
221 templ->direct.greenMask = fmt->greenMask;
222 templ->direct.blue = fmt->blue;
223 templ->direct.blueMask = fmt->blueMask;
226 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
231 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
233 if(fmt->depth != screen_depth)
235 if( (fmt->redMask << fmt->red) != visual->red_mask)
237 if( (fmt->greenMask << fmt->green) != visual->green_mask)
239 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
242 /* We never select a default ARGB visual */
249 static int load_xrender_formats(void)
252 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
254 XRenderPictFormat templ, *pict_format;
256 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
259 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
262 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
263 if (visual->class == DirectColor)
266 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
267 screen_depth, TrueColor, &info ))
269 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
270 if (pict_format) visual = info.visual;
278 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
279 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
280 default_format = &wxr_formats[WineXRenderFormatsListSize];
281 WineXRenderFormatsListSize++;
282 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
287 unsigned long mask = 0;
288 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
291 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
296 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
297 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
298 WineXRenderFormatsListSize++;
299 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
303 return WineXRenderFormatsListSize;
306 /***********************************************************************
307 * X11DRV_XRender_Init
309 * Let's see if our XServer has the extension available
312 void X11DRV_XRender_Init(void)
316 if (client_side_with_render &&
317 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
318 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
319 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
322 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
323 LOAD_FUNCPTR(XRenderAddGlyphs)
324 LOAD_FUNCPTR(XRenderComposite)
325 LOAD_FUNCPTR(XRenderCompositeString8)
326 LOAD_FUNCPTR(XRenderCompositeString16)
327 LOAD_FUNCPTR(XRenderCompositeString32)
328 LOAD_FUNCPTR(XRenderCompositeText16)
329 LOAD_FUNCPTR(XRenderCreateGlyphSet)
330 LOAD_FUNCPTR(XRenderCreatePicture)
331 LOAD_FUNCPTR(XRenderFillRectangle)
332 LOAD_FUNCPTR(XRenderFindFormat)
333 LOAD_FUNCPTR(XRenderFindVisualFormat)
334 LOAD_FUNCPTR(XRenderFreeGlyphSet)
335 LOAD_FUNCPTR(XRenderFreePicture)
336 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
337 LOAD_FUNCPTR(XRenderQueryExtension)
339 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
340 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
341 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
342 #undef LOAD_OPTIONAL_FUNCPTR
346 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
348 if(X11DRV_XRender_Installed) {
349 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
350 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
354 "Wine has detected that you probably have a buggy version\n"
355 "of libXrender.so . Because of this client side font rendering\n"
356 "will be disabled. Please upgrade this library.\n");
357 X11DRV_XRender_Installed = FALSE;
361 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
362 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
363 X11DRV_XRender_Installed = FALSE;
369 if(X11DRV_XRender_Installed || client_side_with_core)
371 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
372 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
374 glyphsetCacheSize = INIT_CACHE_SIZE;
376 for(i = 0; i < INIT_CACHE_SIZE; i++) {
377 glyphsetCache[i].next = i + 1;
378 glyphsetCache[i].count = -1;
380 glyphsetCache[i-1].next = -1;
381 using_client_side_fonts = 1;
383 if(!X11DRV_XRender_Installed) {
384 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
385 if(screen_depth <= 8 || !client_side_antialias_with_core)
388 if(screen_depth <= 8 || !client_side_antialias_with_render)
392 else TRACE("Using X11 core fonts\n");
395 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
396 static void get_xrender_color(const WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
398 XRenderPictFormat *pf = wxr_format->pict_format;
400 if(pf->direct.redMask)
401 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
405 if(pf->direct.greenMask)
406 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
408 dst_color->green = 0;
410 if(pf->direct.blueMask)
411 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
415 dst_color->alpha = 0xffff;
418 static const WineXRenderFormat *get_xrender_format(WXRFormat format)
421 for(i=0; i<WineXRenderFormatsListSize; i++)
423 if(wxr_formats[i].format == format)
425 TRACE("Returning wxr_format=%#x\n", format);
426 return &wxr_formats[i];
432 static const WineXRenderFormat *get_xrender_format_from_color_shifts(int depth, ColorShifts *shifts)
434 int redMask, greenMask, blueMask;
438 return get_xrender_format(WXR_FORMAT_MONO);
440 /* physDevs of a depth <=8, don't have color_shifts set and XRender can't handle those except for 1-bit */
442 return default_format;
444 redMask = shifts->physicalRed.max << shifts->physicalRed.shift;
445 greenMask = shifts->physicalGreen.max << shifts->physicalGreen.shift;
446 blueMask = shifts->physicalBlue.max << shifts->physicalBlue.shift;
448 /* Try to locate a format which matches the specification of the dibsection. */
449 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
451 if( depth == wxr_formats_template[i].depth &&
452 redMask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
453 greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
454 blueMask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
457 /* When we reach this stage the format was found in our template table but this doesn't mean that
458 * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
460 return get_xrender_format(wxr_formats_template[i].wxr_format);
464 /* This should not happen because when we reach 'shifts' must have been set and we only allows shifts which are backed by X */
465 ERR("No XRender format found!\n");
469 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
470 static void set_xrender_transformation(Picture src_pict, float xscale, float yscale, int xoffset, int yoffset)
472 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
473 XTransform xform = {{
474 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
475 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
476 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
479 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
483 static struct xrender_info *get_xrender_info(X11DRV_PDEVICE *physDev)
485 if(!physDev->xrender)
487 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physDev->xrender));
489 if(!physDev->xrender)
491 ERR("Unable to allocate XRENDERINFO!\n");
494 physDev->xrender->cache_index = -1;
496 if (!physDev->xrender->format)
497 physDev->xrender->format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
499 return physDev->xrender;
502 static Picture get_xrender_picture(X11DRV_PDEVICE *physDev)
504 struct xrender_info *info = get_xrender_info(physDev);
507 if (!info->pict && info->format)
509 XRenderPictureAttributes pa;
510 RGNDATA *clip = X11DRV_GetRegionData( physDev->region, 0 );
513 pa.subwindow_mode = IncludeInferiors;
514 info->pict = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
515 CPSubwindowMode, &pa);
516 if (info->pict && clip)
517 pXRenderSetPictureClipRectangles( gdi_display, info->pict,
518 physDev->dc_rect.left, physDev->dc_rect.top,
519 (XRectangle *)clip->Buffer, clip->rdh.nCount );
521 TRACE("Allocing pict=%lx dc=%p drawable=%08lx\n", info->pict, physDev->hdc, physDev->drawable);
522 HeapFree( GetProcessHeap(), 0, clip );
528 static Picture get_xrender_picture_source(X11DRV_PDEVICE *physDev)
530 struct xrender_info *info = get_xrender_info(physDev);
533 if (!info->pict_src && info->format)
535 XRenderPictureAttributes pa;
538 pa.subwindow_mode = IncludeInferiors;
539 info->pict_src = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
540 CPSubwindowMode, &pa);
543 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx\n", info->pict_src, physDev->hdc, physDev->drawable);
546 return info->pict_src;
549 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
551 if(p1->hash != p2->hash) return TRUE;
552 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
553 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
554 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
555 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
559 static void walk_cache(void)
563 EnterCriticalSection(&xrender_cs);
564 for(i=mru; i >= 0; i = glyphsetCache[i].next)
565 TRACE("item %d\n", i);
566 LeaveCriticalSection(&xrender_cs);
570 static int LookupEntry(LFANDSIZE *plfsz)
574 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
576 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
581 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
582 glyphsetCache[i].count++;
584 glyphsetCache[prev_i].next = glyphsetCache[i].next;
585 glyphsetCache[i].next = mru;
588 TRACE("found font in cache %d\n", i);
593 TRACE("font not in cache\n");
597 static void FreeEntry(int entry)
601 for(format = 0; format < AA_MAXVALUE; format++) {
602 gsCacheEntryFormat * formatEntry;
604 if( !glyphsetCache[entry].format[format] )
607 formatEntry = glyphsetCache[entry].format[format];
609 if(formatEntry->glyphset) {
611 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
613 formatEntry->glyphset = 0;
615 if(formatEntry->nrealized) {
616 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
617 formatEntry->realized = NULL;
618 if(formatEntry->bitmaps) {
619 for(i = 0; i < formatEntry->nrealized; i++)
620 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
621 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
622 formatEntry->bitmaps = NULL;
624 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
625 formatEntry->gis = NULL;
626 formatEntry->nrealized = 0;
629 HeapFree(GetProcessHeap(), 0, formatEntry);
630 glyphsetCache[entry].format[format] = NULL;
634 static int AllocEntry(void)
636 int best = -1, prev_best = -1, i, prev_i = -1;
639 assert(glyphsetCache[lastfree].count == -1);
640 glyphsetCache[lastfree].count = 1;
642 lastfree = glyphsetCache[lastfree].next;
644 glyphsetCache[best].next = mru;
647 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
651 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
652 if(glyphsetCache[i].count == 0) {
660 TRACE("freeing unused glyphset at cache %d\n", best);
662 glyphsetCache[best].count = 1;
664 glyphsetCache[prev_best].next = glyphsetCache[best].next;
665 glyphsetCache[best].next = mru;
673 TRACE("Growing cache\n");
676 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
678 (glyphsetCacheSize + INIT_CACHE_SIZE)
679 * sizeof(*glyphsetCache));
681 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
682 (glyphsetCacheSize + INIT_CACHE_SIZE)
683 * sizeof(*glyphsetCache));
685 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
687 glyphsetCache[i].next = i + 1;
688 glyphsetCache[i].count = -1;
690 glyphsetCache[i-1].next = -1;
691 glyphsetCacheSize += INIT_CACHE_SIZE;
693 lastfree = glyphsetCache[best].next;
694 glyphsetCache[best].count = 1;
695 glyphsetCache[best].next = mru;
697 TRACE("new free cache slot at %d\n", mru);
701 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
711 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
712 if(size == GDI_ERROR)
715 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
716 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
718 GetTextMetricsW(physDev->hdc, &tm);
719 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
722 num_recs = get_be_word(*gasp);
726 *flags = get_be_word(*(gasp + 1));
727 if(ppem <= get_be_word(*gasp))
731 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
733 HeapFree(GetProcessHeap(), 0, buffer);
737 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
741 UINT font_smoothing_type, font_smoothing_orientation;
743 if (X11DRV_XRender_Installed && subpixel &&
744 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
745 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
747 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
748 &font_smoothing_orientation, 0) &&
749 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
756 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
757 But, Wine's subpixel rendering can support the portrait mode.
760 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
768 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
773 static int hinter = -1;
774 static int subpixel = -1;
777 if((ret = LookupEntry(plfsz)) != -1) return ret;
780 entry = glyphsetCache + ret;
781 entry->lfsz = *plfsz;
782 for( format = 0; format < AA_MAXVALUE; format++ ) {
783 assert( !entry->format[format] );
786 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
788 if(hinter == -1 || subpixel == -1)
790 RASTERIZER_STATUS status;
791 GetRasterizerCaps(&status, sizeof(status));
792 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
793 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
796 switch (plfsz->lf.lfQuality)
798 case ANTIALIASED_QUALITY:
799 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
801 case CLEARTYPE_QUALITY:
802 case CLEARTYPE_NATURAL_QUALITY:
803 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
805 case DEFAULT_QUALITY:
809 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
812 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
815 entry->aa_default = AA_None;
820 entry->aa_default = AA_None;
825 static void dec_ref_cache(int index)
828 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
829 assert(glyphsetCache[index].count > 0);
830 glyphsetCache[index].count--;
833 static void lfsz_calc_hash(LFANDSIZE *plfsz)
835 DWORD hash = 0, *ptr, two_chars;
839 hash ^= plfsz->devsize.cx;
840 hash ^= plfsz->devsize.cy;
841 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
843 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
845 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
847 pwc = (WCHAR *)&two_chars;
849 *pwc = toupperW(*pwc);
851 *pwc = toupperW(*pwc);
859 /***********************************************************************
860 * X11DRV_XRender_Finalize
862 void X11DRV_XRender_Finalize(void)
866 EnterCriticalSection(&xrender_cs);
867 for(i = mru; i >= 0; i = glyphsetCache[i].next)
869 LeaveCriticalSection(&xrender_cs);
873 /***********************************************************************
874 * X11DRV_XRender_SelectFont
876 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
879 struct xrender_info *info;
881 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
882 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
883 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
884 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
885 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
886 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
887 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
888 GetWorldTransform( physDev->hdc, &lfsz.xform );
889 lfsz_calc_hash(&lfsz);
891 info = get_xrender_info(physDev);
894 EnterCriticalSection(&xrender_cs);
895 if(info->cache_index != -1)
896 dec_ref_cache(info->cache_index);
897 info->cache_index = GetCacheEntry(physDev, &lfsz);
898 LeaveCriticalSection(&xrender_cs);
902 /***********************************************************************
903 * X11DRV_XRender_SetDeviceClipping
905 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
907 if (physDev->xrender->pict)
910 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
911 physDev->dc_rect.left, physDev->dc_rect.top,
912 (XRectangle *)data->Buffer, data->rdh.nCount );
917 /***********************************************************************
918 * X11DRV_XRender_DeleteDC
920 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
922 X11DRV_XRender_UpdateDrawable(physDev);
924 EnterCriticalSection(&xrender_cs);
925 if(physDev->xrender->cache_index != -1)
926 dec_ref_cache(physDev->xrender->cache_index);
927 LeaveCriticalSection(&xrender_cs);
929 HeapFree(GetProcessHeap(), 0, physDev->xrender);
930 physDev->xrender = NULL;
934 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, const DIBSECTION *dib)
936 const WineXRenderFormat *fmt;
939 /* When XRender is not around we can only use the screen_depth and when needed we perform depth conversion
940 * in software. Further we also return the screen depth for paletted formats or TrueColor formats with a low
941 * number of bits because XRender can't handle paletted formats and 8-bit TrueColor does not exist for XRender. */
942 if(!X11DRV_XRender_Installed || dib->dsBm.bmBitsPixel <= 8)
945 X11DRV_PALETTE_ComputeColorShifts(&shifts, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
947 /* Common formats should be in our picture format table. */
948 fmt = get_xrender_format_from_color_shifts(dib->dsBm.bmBitsPixel, &shifts);
951 physBitmap->pixmap_depth = fmt->pict_format->depth;
952 physBitmap->trueColor = TRUE;
953 physBitmap->pixmap_color_shifts = shifts;
956 TRACE("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n",
957 dib->dsBm.bmBitsPixel, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
961 /***********************************************************************
962 * X11DRV_XRender_UpdateDrawable
964 * Deletes the pict and tile when the drawable changes.
966 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
968 struct xrender_info *info = physDev->xrender;
970 if (info->pict || info->pict_src)
973 XFlush( gdi_display );
976 TRACE("freeing pict = %lx dc = %p\n", info->pict, physDev->hdc);
977 pXRenderFreePicture(gdi_display, info->pict);
982 TRACE("freeing pict = %lx dc = %p\n", info->pict_src, physDev->hdc);
983 pXRenderFreePicture(gdi_display, info->pict_src);
992 /************************************************************************
995 * Helper to ExtTextOut. Must be called inside xrender_cs
997 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
1004 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
1005 gsCacheEntryFormat *formatEntry;
1006 UINT ggo_format = GGO_GLYPH_INDEX;
1007 WXRFormat wxr_format;
1008 static const char zero[4];
1009 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1013 ggo_format |= WINE_GGO_GRAY16_BITMAP;
1016 ggo_format |= WINE_GGO_HRGB_BITMAP;
1019 ggo_format |= WINE_GGO_HBGR_BITMAP;
1022 ggo_format |= WINE_GGO_VRGB_BITMAP;
1025 ggo_format |= WINE_GGO_VBGR_BITMAP;
1029 ERR("aa = %d - not implemented\n", format);
1031 ggo_format |= GGO_BITMAP;
1035 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1036 if(buflen == GDI_ERROR) {
1037 if(format != AA_None) {
1039 entry->aa_default = AA_None;
1040 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
1041 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1043 if(buflen == GDI_ERROR) {
1044 WARN("GetGlyphOutlineW failed\n");
1047 TRACE("Turning off antialiasing for this monochrome font\n");
1050 /* If there is nothing for the current type, we create the entry. */
1051 if( !entry->format[format] ) {
1052 entry->format[format] = HeapAlloc(GetProcessHeap(),
1054 sizeof(gsCacheEntryFormat));
1056 formatEntry = entry->format[format];
1058 if(formatEntry->nrealized <= glyph) {
1059 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1061 if (formatEntry->realized)
1062 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1064 formatEntry->realized,
1065 formatEntry->nrealized * sizeof(BOOL));
1067 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1069 formatEntry->nrealized * sizeof(BOOL));
1071 if(!X11DRV_XRender_Installed) {
1072 if (formatEntry->bitmaps)
1073 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
1075 formatEntry->bitmaps,
1076 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1078 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
1080 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1082 if (formatEntry->gis)
1083 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1086 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1088 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1090 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1094 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
1097 wxr_format = WXR_FORMAT_GRAY;
1104 wxr_format = WXR_FORMAT_A8R8G8B8;
1108 ERR("aa = %d - not implemented\n", format);
1110 wxr_format = WXR_FORMAT_MONO;
1115 formatEntry->font_format = get_xrender_format(wxr_format);
1116 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
1117 wine_tsx11_unlock();
1121 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1122 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1123 formatEntry->realized[glyph] = TRUE;
1125 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1127 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1128 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1130 gi.width = gm.gmBlackBoxX;
1131 gi.height = gm.gmBlackBoxY;
1132 gi.x = -gm.gmptGlyphOrigin.x;
1133 gi.y = gm.gmptGlyphOrigin.y;
1134 gi.xOff = gm.gmCellIncX;
1135 gi.yOff = gm.gmCellIncY;
1137 if(TRACE_ON(xrender)) {
1140 unsigned char *line;
1142 if(format == AA_None) {
1143 pitch = ((gi.width + 31) / 32) * 4;
1144 for(i = 0; i < gi.height; i++) {
1145 line = (unsigned char*) buf + i * pitch;
1147 for(j = 0; j < pitch * 8; j++) {
1148 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1150 TRACE("%s\n", output);
1153 static const char blks[] = " .:;!o*#";
1157 pitch = ((gi.width + 3) / 4) * 4;
1158 for(i = 0; i < gi.height; i++) {
1159 line = (unsigned char*) buf + i * pitch;
1161 for(j = 0; j < pitch; j++) {
1162 str[0] = blks[line[j] >> 5];
1163 strcat(output, str);
1165 TRACE("%s\n", output);
1171 if(formatEntry->glyphset) {
1172 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1173 unsigned char *byte = (unsigned char*) buf, c;
1179 /* magic to flip bit order */
1180 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1181 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1182 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1187 else if ( format != AA_Grey &&
1188 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1190 unsigned int i, *data = (unsigned int *)buf;
1191 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1196 XRenderCompositeText seems to ignore 0x0 glyphs when
1197 AA_None, which means we lose the advance width of glyphs
1198 like the space. We'll pretend that such glyphs are 1x1
1203 gi.width = gi.height = 1;
1206 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1207 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1208 wine_tsx11_unlock();
1209 HeapFree(GetProcessHeap(), 0, buf);
1211 formatEntry->bitmaps[glyph] = buf;
1214 formatEntry->gis[glyph] = gi;
1219 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1220 void *bitmap, XGlyphInfo *gi)
1222 unsigned char *srcLine = bitmap, *src;
1223 unsigned char bits, bitsMask;
1224 int width = gi->width;
1225 int stride = ((width + 31) & ~31) >> 3;
1226 int height = gi->height;
1230 TRACE("%d, %d\n", x, y);
1239 bitsMask = 0x80; /* FreeType is always MSB first */
1245 if (bits & bitsMask)
1253 bitsMask = bitsMask >> 1;
1259 } while (bits & bitsMask);
1260 XFillRectangle (gdi_display, physDev->drawable,
1261 physDev->gc, xspan, y, lenspan, 1);
1273 bitsMask = bitsMask >> 1;
1279 } while (!(bits & bitsMask));
1286 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1287 void *bitmap, XGlyphInfo *gi)
1289 unsigned char *srcLine = bitmap, *src, bits;
1290 int width = gi->width;
1291 int stride = ((width + 3) & ~3);
1292 int height = gi->height;
1317 } while (bits >= 0x80);
1318 XFillRectangle (gdi_display, physDev->drawable,
1319 physDev->gc, xspan, y, lenspan, 1);
1332 } while (bits < 0x80);
1340 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1345 while ((mask & 1) == 0)
1351 while ((mask & 1) == 1)
1360 static DWORD GetField (DWORD pixel, int shift, int len)
1362 pixel = pixel & (((1 << (len)) - 1) << shift);
1363 pixel = pixel << (32 - (shift + len)) >> 24;
1366 pixel |= (pixel >> len);
1373 static DWORD PutField (DWORD pixel, int shift, int len)
1375 shift = shift - (8 - len);
1377 pixel &= (((1 << len) - 1) << (8 - len));
1385 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1391 BYTE *maskLine, *mask, m;
1396 BYTE src_r, src_g, src_b;
1401 height = gi->height;
1404 maskStride = (width + 3) & ~3;
1406 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1407 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1408 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1410 src_r = GetField(color, r_shift, r_len);
1411 src_g = GetField(color, g_shift, g_len);
1412 src_b = GetField(color, b_shift, b_len);
1414 for(; height--; y++)
1417 maskLine += maskStride;
1422 if(y >= image->height) break;
1426 if(tx >= image->width) break;
1429 if(tx < 0) continue;
1432 XPutPixel (image, tx, y, color);
1437 pixel = XGetPixel (image, tx, y);
1439 r = GetField(pixel, r_shift, r_len);
1440 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1441 g = GetField(pixel, g_shift, g_len);
1442 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1443 b = GetField(pixel, b_shift, b_len);
1444 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1446 pixel = (PutField (r, r_shift, r_len) |
1447 PutField (g, g_shift, g_len) |
1448 PutField (b, b_shift, b_len));
1449 XPutPixel (image, tx, y, pixel);
1455 /*************************************************************
1458 * Returns an appropriate Picture for tiling the text colour.
1459 * Call and use result within the xrender_cs
1461 static Picture get_tile_pict(const WineXRenderFormat *wxr_format, int text_pixel)
1468 } tiles[MAX_FORMATS], *tile;
1471 tile = &tiles[wxr_format->format];
1475 XRenderPictureAttributes pa;
1478 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1480 pa.repeat = RepeatNormal;
1481 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1482 wine_tsx11_unlock();
1484 /* init current_color to something different from text_pixel */
1485 tile->current_color = ~text_pixel;
1487 if(wxr_format->format == WXR_FORMAT_MONO)
1489 /* for a 1bpp bitmap we always need a 1 in the tile */
1490 col.red = col.green = col.blue = 0;
1493 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1494 wine_tsx11_unlock();
1498 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1500 get_xrender_color(wxr_format, text_pixel, &col);
1502 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1503 wine_tsx11_unlock();
1504 tile->current_color = text_pixel;
1509 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1514 /***********************************************************************
1515 * X11DRV_XRender_ExtTextOut
1517 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1518 const RECT *lprect, LPCWSTR wstr, UINT count,
1522 gsCacheEntry *entry;
1523 gsCacheEntryFormat *formatEntry;
1525 int textPixel, backgroundPixel;
1526 HRGN saved_region = 0;
1527 BOOL disable_antialias = FALSE;
1528 AA_Type aa_type = AA_None;
1531 double cosEsc, sinEsc;
1533 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1534 Picture tile_pict = 0;
1536 /* Do we need to disable antialiasing because of palette mode? */
1537 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1538 TRACE("bitmap is not a DIB\n");
1540 else if (bmp.dsBmih.biBitCount <= 8) {
1541 TRACE("Disabling antialiasing\n");
1542 disable_antialias = TRUE;
1545 xgcval.function = GXcopy;
1546 xgcval.background = physDev->backgroundPixel;
1547 xgcval.fill_style = FillSolid;
1549 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1550 wine_tsx11_unlock();
1552 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1554 if(physDev->depth == 1) {
1555 if((physDev->textPixel & 0xffffff) == 0) {
1557 backgroundPixel = 1;
1560 backgroundPixel = 0;
1563 textPixel = physDev->textPixel;
1564 backgroundPixel = physDev->backgroundPixel;
1567 if(flags & ETO_OPAQUE)
1570 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1571 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1572 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1573 lprect->right - lprect->left, lprect->bottom - lprect->top );
1574 wine_tsx11_unlock();
1584 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1585 if(lf.lfEscapement != 0) {
1586 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1587 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1593 if (flags & ETO_CLIPPED)
1597 clip_region = CreateRectRgnIndirect( lprect );
1598 /* make a copy of the current device region */
1599 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1600 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1601 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1602 DeleteObject( clip_region );
1605 EnterCriticalSection(&xrender_cs);
1607 entry = glyphsetCache + physDev->xrender->cache_index;
1608 if( disable_antialias == FALSE )
1609 aa_type = entry->aa_default;
1610 formatEntry = entry->format[aa_type];
1612 for(idx = 0; idx < count; idx++) {
1613 if( !formatEntry ) {
1614 UploadGlyph(physDev, wstr[idx], aa_type);
1615 /* re-evaluate antialias since aa_default may have changed */
1616 if( disable_antialias == FALSE )
1617 aa_type = entry->aa_default;
1618 formatEntry = entry->format[aa_type];
1619 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1620 UploadGlyph(physDev, wstr[idx], aa_type);
1625 WARN("could not upload requested glyphs\n");
1626 LeaveCriticalSection(&xrender_cs);
1630 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1631 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1633 if(X11DRV_XRender_Installed)
1635 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1637 POINT desired, current;
1638 int render_op = PictOpOver;
1639 Picture pict = get_xrender_picture(physDev);
1641 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1642 So we pass zeros to the function and move to our starting position using the first
1643 element of the elts array. */
1645 desired.x = physDev->dc_rect.left + x;
1646 desired.y = physDev->dc_rect.top + y;
1647 current.x = current.y = 0;
1649 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1651 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1653 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1654 render_op = PictOpOutReverse; /* This gives us 'black' text */
1656 for(idx = 0; idx < count; idx++)
1658 elts[idx].glyphset = formatEntry->glyphset;
1659 elts[idx].chars = wstr + idx;
1660 elts[idx].nchars = 1;
1661 elts[idx].xOff = desired.x - current.x;
1662 elts[idx].yOff = desired.y - current.y;
1664 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1665 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1669 desired.x += formatEntry->gis[wstr[idx]].xOff;
1670 desired.y += formatEntry->gis[wstr[idx]].yOff;
1674 offset += lpDx[idx];
1675 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1676 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1680 /* Make sure we don't have any transforms set from a previous call */
1681 set_xrender_transformation(pict, 1, 1, 0, 0);
1682 pXRenderCompositeText16(gdi_display, render_op,
1685 formatEntry->font_format->pict_format,
1686 0, 0, 0, 0, elts, count);
1687 wine_tsx11_unlock();
1688 HeapFree(GetProcessHeap(), 0, elts);
1690 INT offset = 0, xoff = 0, yoff = 0;
1692 XSetForeground( gdi_display, physDev->gc, textPixel );
1694 if(aa_type == AA_None || physDev->depth == 1)
1696 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1698 if(aa_type == AA_None)
1699 sharp_glyph_fn = SharpGlyphMono;
1701 sharp_glyph_fn = SharpGlyphGray;
1703 for(idx = 0; idx < count; idx++) {
1704 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1705 physDev->dc_rect.top + y + yoff,
1706 formatEntry->bitmaps[wstr[idx]],
1707 &formatEntry->gis[wstr[idx]]);
1709 offset += lpDx[idx];
1710 xoff = offset * cosEsc;
1711 yoff = offset * -sinEsc;
1713 xoff += formatEntry->gis[wstr[idx]].xOff;
1714 yoff += formatEntry->gis[wstr[idx]].yOff;
1719 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1720 RECT extents = {0, 0, 0, 0};
1722 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1723 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1725 TRACE("drawable %dx%d\n", w, h);
1727 for(idx = 0; idx < count; idx++) {
1728 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1729 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1730 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1731 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1732 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1733 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1734 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1735 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1737 offset += lpDx[idx];
1738 cur.x = offset * cosEsc;
1739 cur.y = offset * -sinEsc;
1741 cur.x += formatEntry->gis[wstr[idx]].xOff;
1742 cur.y += formatEntry->gis[wstr[idx]].yOff;
1745 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1746 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1748 if(physDev->dc_rect.left + x + extents.left >= 0) {
1749 image_x = physDev->dc_rect.left + x + extents.left;
1753 image_off_x = physDev->dc_rect.left + x + extents.left;
1755 if(physDev->dc_rect.top + y + extents.top >= 0) {
1756 image_y = physDev->dc_rect.top + y + extents.top;
1760 image_off_y = physDev->dc_rect.top + y + extents.top;
1762 if(physDev->dc_rect.left + x + extents.right < w)
1763 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1765 image_w = w - image_x;
1766 if(physDev->dc_rect.top + y + extents.bottom < h)
1767 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1769 image_h = h - image_y;
1771 if(image_w <= 0 || image_h <= 0) goto no_image;
1773 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1774 image = XGetImage(gdi_display, physDev->drawable,
1775 image_x, image_y, image_w, image_h,
1776 AllPlanes, ZPixmap);
1777 X11DRV_check_error();
1779 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1780 gdi_display, (int)physDev->drawable, image_x, image_y,
1781 image_w, image_h, AllPlanes, ZPixmap,
1782 physDev->depth, image);
1784 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1789 gcv.graphics_exposures = False;
1790 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1791 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1792 image_w, image_h, 0, 0);
1793 XFreeGC(gdi_display, gc);
1794 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1795 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1797 X11DRV_check_error();
1798 XFreePixmap(gdi_display, xpm);
1800 if(!image) goto no_image;
1802 image->red_mask = visual->red_mask;
1803 image->green_mask = visual->green_mask;
1804 image->blue_mask = visual->blue_mask;
1806 offset = xoff = yoff = 0;
1807 for(idx = 0; idx < count; idx++) {
1808 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1809 yoff + image_off_y - extents.top,
1810 formatEntry->bitmaps[wstr[idx]],
1811 &formatEntry->gis[wstr[idx]],
1812 physDev->textPixel);
1814 offset += lpDx[idx];
1815 xoff = offset * cosEsc;
1816 yoff = offset * -sinEsc;
1818 xoff += formatEntry->gis[wstr[idx]].xOff;
1819 yoff += formatEntry->gis[wstr[idx]].yOff;
1822 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1823 image_x, image_y, image_w, image_h);
1824 XDestroyImage(image);
1827 wine_tsx11_unlock();
1829 LeaveCriticalSection(&xrender_cs);
1831 if (flags & ETO_CLIPPED)
1833 /* restore the device region */
1834 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1835 DeleteObject( saved_region );
1841 X11DRV_UnlockDIBSection( physDev, TRUE );
1845 /* Helper function for (stretched) blitting using xrender */
1846 static void xrender_blit(Picture src_pict, Picture mask_pict, Picture dst_pict, int x_src, int y_src, float xscale, float yscale, int width, int height)
1848 /* Further down a transformation matrix is used for stretching and mirroring the source data.
1849 * xscale/yscale contain the scaling factors for the width and height. In case of mirroring
1850 * we also need a x- and y-offset because without the pixels will be in the wrong quadrant of the x-y plane.
1852 int x_offset = (xscale<0) ? width : 0;
1853 int y_offset = (yscale<0) ? height : 0;
1855 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1856 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1857 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1858 if(xscale != 1.0 || yscale != 1.0)
1860 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1862 set_xrender_transformation(mask_pict, xscale, yscale, x_offset, y_offset);
1864 set_xrender_transformation(src_pict, xscale, yscale, x_src + x_offset, y_src + y_offset);
1866 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, 0, 0, 0, 0, 0, 0, width, height);
1870 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1872 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
1874 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1876 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, x_src, y_src, 0, 0, 0, 0, width, height);
1880 /******************************************************************************
1881 * AlphaBlend (x11drv.@)
1883 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1884 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1885 BLENDFUNCTION blendfn)
1887 XRenderPictureAttributes pa;
1888 Picture dst_pict, src_pict;
1894 DWORD *dstbits, *data;
1897 BOOL top_down = FALSE;
1898 const WineXRenderFormat *src_format;
1901 if(!X11DRV_XRender_Installed) {
1902 FIXME("Unable to AlphaBlend without Xrender\n");
1907 pts[1].x = xDst + widthDst;
1908 pts[1].y = yDst + heightDst;
1909 LPtoDP(devDst->hdc, pts, 2);
1912 widthDst = pts[1].x - pts[0].x;
1913 heightDst = pts[1].y - pts[0].y;
1917 pts[1].x = xSrc + widthSrc;
1918 pts[1].y = ySrc + heightSrc;
1919 LPtoDP(devSrc->hdc, pts, 2);
1922 widthSrc = pts[1].x - pts[0].x;
1923 heightSrc = pts[1].y - pts[0].y;
1924 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1926 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1927 if(widthDst != widthSrc || heightDst != heightSrc)
1929 if(!pXRenderSetPictureTransform)
1932 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1936 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1938 static BOOL out = FALSE;
1941 FIXME("not a dibsection\n");
1947 /* If the source is a 1x1 bitmap, tiling is equivalent to stretching, but
1948 tiling is much faster. Therefore, we do no stretching in this case. */
1949 repeat_src = dib.dsBmih.biWidth == 1 && abs(dib.dsBmih.biHeight) == 1;
1951 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1952 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1954 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1955 SetLastError(ERROR_INVALID_PARAMETER);
1959 if(dib.dsBm.bmBitsPixel != 32) {
1960 FIXME("not a 32 bpp dibsection\n");
1963 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1965 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1967 dstbits += widthSrc * (heightSrc - 1);
1969 y = y2 + heightSrc - 1;
1973 y = dib.dsBmih.biHeight - ySrc - 1;
1974 y2 = y - heightSrc + 1;
1977 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1979 if (blendfn.SourceConstantAlpha == 0xff)
1981 for (; y >= y2; y--)
1983 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1985 dstbits += (top_down ? -1 : 1) * widthSrc;
1990 /* SourceConstantAlpha combined with source alpha */
1991 for (; y >= y2; y--)
1994 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1995 for (x = 0; x < widthSrc; x++)
1997 DWORD argb = *srcbits++;
1998 BYTE *s = (BYTE *) &argb;
1999 s[0] = (s[0] * blendfn.SourceConstantAlpha) / 255;
2000 s[1] = (s[1] * blendfn.SourceConstantAlpha) / 255;
2001 s[2] = (s[2] * blendfn.SourceConstantAlpha) / 255;
2002 s[3] = (s[3] * blendfn.SourceConstantAlpha) / 255;
2005 if (top_down) /* we traversed the row forward so we should go back by two rows */
2006 dstbits -= 2 * widthSrc;
2012 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
2017 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
2018 for (x = 0; x < widthSrc; x++)
2020 DWORD argb = *srcbits++;
2021 argb = (argb & 0xffffff) | source_alpha;
2024 if (top_down) /* we traversed the row forward so we should go back by two rows */
2025 dstbits -= 2 * widthSrc;
2030 dst_pict = get_xrender_picture(devDst);
2033 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
2034 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
2036 src_format = get_xrender_format(WXR_FORMAT_A8R8G8B8);
2037 TRACE("src_format %p\n", src_format);
2040 WARN("Unable to find a picture format supporting alpha, make sure X is running at 24-bit\n");
2044 TRACE("src_drawable = %08lx\n", devSrc->drawable);
2045 xpm = XCreatePixmap(gdi_display,
2047 widthSrc, heightSrc, 32);
2048 gcv.graphics_exposures = False;
2049 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
2050 TRACE("xpm = %08lx\n", xpm);
2051 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
2053 pa.subwindow_mode = IncludeInferiors;
2054 pa.repeat = repeat_src ? RepeatNormal : RepeatNone;
2055 src_pict = pXRenderCreatePicture(gdi_display,
2056 xpm, src_format->pict_format,
2057 CPSubwindowMode|CPRepeat, &pa);
2058 TRACE("src_pict %08lx\n", src_pict);
2060 /* Make sure we ALWAYS set the transformation matrix even if we don't need to scale. The reason is
2061 * that later on we want to reuse pictures (it can bring a lot of extra performance) and each time
2062 * a different transformation matrix might have been used. */
2064 set_xrender_transformation(src_pict, 1.0, 1.0, 0, 0);
2066 set_xrender_transformation(src_pict, widthSrc/(double)widthDst, heightSrc/(double)heightDst, 0, 0);
2067 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
2069 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
2072 pXRenderFreePicture(gdi_display, src_pict);
2073 XFreePixmap(gdi_display, xpm);
2074 XFreeGC(gdi_display, gc);
2076 XDestroyImage(image);
2078 wine_tsx11_unlock();
2079 HeapFree(GetProcessHeap(), 0, data);
2083 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2085 /* 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 */
2086 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2087 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2088 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2091 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2093 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2094 if(physBitmap->pixmap_depth == 1 || src_format->format == dst_format->format)
2096 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2097 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2099 else /* We need depth conversion */
2101 Picture src_pict, dst_pict;
2102 XRenderPictureAttributes pa;
2103 pa.subwindow_mode = IncludeInferiors;
2104 pa.repeat = RepeatNone;
2106 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2107 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2109 xrender_blit(src_pict, 0, dst_pict, 0, 0, 1.0, 1.0, width, height);
2110 pXRenderFreePicture(gdi_display, src_pict);
2111 pXRenderFreePicture(gdi_display, dst_pict);
2113 wine_tsx11_unlock();
2116 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2117 Pixmap pixmap, GC gc,
2118 INT widthSrc, INT heightSrc,
2119 INT widthDst, INT heightDst,
2120 RECT *visRectSrc, RECT *visRectDst )
2122 BOOL stretch = (widthSrc != widthDst) || (heightSrc != heightDst);
2123 int width = visRectDst->right - visRectDst->left;
2124 int height = visRectDst->bottom - visRectDst->top;
2125 int x_src = physDevSrc->dc_rect.left + visRectSrc->left;
2126 int y_src = physDevSrc->dc_rect.top + visRectSrc->top;
2127 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2128 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2129 Picture src_pict=0, dst_pict=0, mask_pict=0;
2131 double xscale = widthSrc/(double)widthDst;
2132 double yscale = heightSrc/(double)heightDst;
2134 XRenderPictureAttributes pa;
2135 pa.subwindow_mode = IncludeInferiors;
2136 pa.repeat = RepeatNone;
2138 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n", physDevSrc->depth, widthSrc, heightSrc, x_src, y_src);
2139 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, widthDst, heightDst);
2141 if(!X11DRV_XRender_Installed)
2143 TRACE("Not using XRender since it is not available or disabled\n");
2147 /* XRender can't handle palettes, so abort */
2148 if(X11DRV_PALETTE_XPixelToPalette)
2151 /* XRender is of no use in this case */
2152 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2155 /* Just use traditional X copy when the formats match and we don't need stretching */
2156 if((src_info->format->format == dst_format->format) && !stretch)
2158 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2160 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2161 wine_tsx11_unlock();
2166 if(physDevSrc->depth == 1)
2169 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2171 /* We use the source drawable as a mask */
2172 mask_pict = get_xrender_picture_source(physDevSrc);
2174 /* Use backgroundPixel as the foreground color */
2175 EnterCriticalSection( &xrender_cs );
2176 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2178 /* Create a destination picture and fill it with textPixel color as the background color */
2180 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2181 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2183 xrender_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2185 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2186 wine_tsx11_unlock();
2187 LeaveCriticalSection( &xrender_cs );
2189 else /* color -> color but with different depths */
2191 src_pict = get_xrender_picture_source(physDevSrc);
2194 dst_pict = pXRenderCreatePicture(gdi_display,
2195 pixmap, dst_format->pict_format,
2196 CPSubwindowMode|CPRepeat, &pa);
2198 xrender_blit(src_pict, 0, dst_pict, x_src, y_src, xscale, yscale, width, height);
2200 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2201 wine_tsx11_unlock();
2206 #else /* SONAME_LIBXRENDER */
2208 void X11DRV_XRender_Init(void)
2210 TRACE("XRender support not compiled in.\n");
2214 void X11DRV_XRender_Finalize(void)
2218 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2224 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2230 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2236 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2237 const RECT *lprect, LPCWSTR wstr, UINT count,
2244 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2250 /******************************************************************************
2251 * AlphaBlend (x11drv.@)
2253 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
2254 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
2255 BLENDFUNCTION blendfn)
2257 FIXME("not supported - XRENDER headers were missing at compile time\n");
2261 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2264 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2266 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2267 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2268 wine_tsx11_unlock();
2271 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, const DIBSECTION *dib)
2276 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2277 Pixmap pixmap, GC gc,
2278 INT widthSrc, INT heightSrc,
2279 INT widthDst, INT heightDst,
2280 RECT *visRectSrc, RECT *visRectDst )
2284 #endif /* SONAME_LIBXRENDER */