2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
7 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/port.h"
35 #include "wine/library.h"
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
39 int using_client_side_fonts = FALSE;
41 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
43 #ifdef SONAME_LIBXRENDER
45 static BOOL X11DRV_XRender_Installed = FALSE;
48 #include <X11/extensions/Xrender.h>
50 #ifndef RepeatNone /* added in 0.10 */
52 #define RepeatNormal 1
54 #define RepeatReflect 3
57 #define MAX_FORMATS 10
58 typedef enum wine_xrformat
72 typedef struct wine_xrender_format_template
77 unsigned int alphaMask;
81 unsigned int greenMask;
83 unsigned int blueMask;
84 } WineXRenderFormatTemplate;
86 static const WineXRenderFormatTemplate wxr_formats_template[] =
88 /* Format depth alpha mask red mask green mask blue mask*/
89 {WXR_FORMAT_MONO, 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
90 {WXR_FORMAT_GRAY, 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
91 {WXR_FORMAT_X1R5G5B5, 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
92 {WXR_FORMAT_X1B5G5R5, 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
93 {WXR_FORMAT_R5G6B5, 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
94 {WXR_FORMAT_B5G6R5, 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
95 {WXR_FORMAT_R8G8B8, 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
96 {WXR_FORMAT_B8G8R8, 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
97 {WXR_FORMAT_A8R8G8B8, 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
98 {WXR_FORMAT_X8R8G8B8, 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff }
101 typedef struct wine_xrender_format
104 XRenderPictFormat *pict_format;
107 static WineXRenderFormat wxr_formats[MAX_FORMATS];
108 static int WineXRenderFormatsListSize = 0;
109 static WineXRenderFormat *default_format = NULL;
115 SIZE devsize; /* size in device coords */
119 #define INITIAL_REALIZED_BUF_SIZE 128
121 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
126 WineXRenderFormat *font_format;
131 } gsCacheEntryFormat;
137 gsCacheEntryFormat * format[AA_MAXVALUE];
142 struct tagXRENDERINFO
149 static gsCacheEntry *glyphsetCache = NULL;
150 static DWORD glyphsetCacheSize = 0;
151 static INT lastfree = -1;
154 #define INIT_CACHE_SIZE 10
156 static int antialias = 1;
158 static void *xrender_handle;
160 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
161 MAKE_FUNCPTR(XRenderAddGlyphs)
162 MAKE_FUNCPTR(XRenderComposite)
163 MAKE_FUNCPTR(XRenderCompositeString8)
164 MAKE_FUNCPTR(XRenderCompositeString16)
165 MAKE_FUNCPTR(XRenderCompositeString32)
166 MAKE_FUNCPTR(XRenderCompositeText16)
167 MAKE_FUNCPTR(XRenderCreateGlyphSet)
168 MAKE_FUNCPTR(XRenderCreatePicture)
169 MAKE_FUNCPTR(XRenderFillRectangle)
170 MAKE_FUNCPTR(XRenderFindFormat)
171 MAKE_FUNCPTR(XRenderFindVisualFormat)
172 MAKE_FUNCPTR(XRenderFreeGlyphSet)
173 MAKE_FUNCPTR(XRenderFreePicture)
174 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
175 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
176 MAKE_FUNCPTR(XRenderSetPictureTransform)
178 MAKE_FUNCPTR(XRenderQueryExtension)
181 static CRITICAL_SECTION xrender_cs;
182 static CRITICAL_SECTION_DEBUG critsect_debug =
185 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
186 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
188 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
190 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
191 ( ( (ULONG)_x4 << 24 ) | \
192 ( (ULONG)_x3 << 16 ) | \
193 ( (ULONG)_x2 << 8 ) | \
196 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
198 #define GASP_GRIDFIT 0x01
199 #define GASP_DOGRAY 0x02
201 #ifdef WORDS_BIGENDIAN
202 #define get_be_word(x) (x)
203 #define NATIVE_BYTE_ORDER MSBFirst
205 #define get_be_word(x) RtlUshortByteSwap(x)
206 #define NATIVE_BYTE_ORDER LSBFirst
209 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
212 templ->type = PictTypeDirect;
213 templ->depth = fmt->depth;
214 templ->direct.alpha = fmt->alpha;
215 templ->direct.alphaMask = fmt->alphaMask;
216 templ->direct.red = fmt->red;
217 templ->direct.redMask = fmt->redMask;
218 templ->direct.green = fmt->green;
219 templ->direct.greenMask = fmt->greenMask;
220 templ->direct.blue = fmt->blue;
221 templ->direct.blueMask = fmt->blueMask;
224 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
229 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
231 if(fmt->depth != screen_depth)
233 if( (fmt->redMask << fmt->red) != visual->red_mask)
235 if( (fmt->greenMask << fmt->green) != visual->green_mask)
237 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
240 /* We never select a default ARGB visual */
247 static int load_xrender_formats(void)
250 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
252 XRenderPictFormat templ, *pict_format;
254 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
257 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
260 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
261 if (visual->class == DirectColor)
264 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
265 screen_depth, TrueColor, &info ))
267 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
268 if (pict_format) visual = info.visual;
276 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
277 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
278 default_format = &wxr_formats[WineXRenderFormatsListSize];
279 WineXRenderFormatsListSize++;
280 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
285 unsigned long mask = 0;
286 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
289 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
294 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
295 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
296 WineXRenderFormatsListSize++;
297 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
301 return WineXRenderFormatsListSize;
304 /***********************************************************************
305 * X11DRV_XRender_Init
307 * Let's see if our XServer has the extension available
310 void X11DRV_XRender_Init(void)
314 if (client_side_with_render &&
315 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
316 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
317 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
320 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
321 LOAD_FUNCPTR(XRenderAddGlyphs)
322 LOAD_FUNCPTR(XRenderComposite)
323 LOAD_FUNCPTR(XRenderCompositeString8)
324 LOAD_FUNCPTR(XRenderCompositeString16)
325 LOAD_FUNCPTR(XRenderCompositeString32)
326 LOAD_FUNCPTR(XRenderCompositeText16)
327 LOAD_FUNCPTR(XRenderCreateGlyphSet)
328 LOAD_FUNCPTR(XRenderCreatePicture)
329 LOAD_FUNCPTR(XRenderFillRectangle)
330 LOAD_FUNCPTR(XRenderFindFormat)
331 LOAD_FUNCPTR(XRenderFindVisualFormat)
332 LOAD_FUNCPTR(XRenderFreeGlyphSet)
333 LOAD_FUNCPTR(XRenderFreePicture)
334 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
335 LOAD_FUNCPTR(XRenderQueryExtension)
337 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
338 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
339 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
340 #undef LOAD_OPTIONAL_FUNCPTR
344 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
346 if(X11DRV_XRender_Installed) {
347 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
348 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
352 "Wine has detected that you probably have a buggy version\n"
353 "of libXrender.so . Because of this client side font rendering\n"
354 "will be disabled. Please upgrade this library.\n");
355 X11DRV_XRender_Installed = FALSE;
359 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
360 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
361 X11DRV_XRender_Installed = FALSE;
367 if(X11DRV_XRender_Installed || client_side_with_core)
369 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
370 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
372 glyphsetCacheSize = INIT_CACHE_SIZE;
374 for(i = 0; i < INIT_CACHE_SIZE; i++) {
375 glyphsetCache[i].next = i + 1;
376 glyphsetCache[i].count = -1;
378 glyphsetCache[i-1].next = -1;
379 using_client_side_fonts = 1;
381 if(!X11DRV_XRender_Installed) {
382 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
383 if(screen_depth <= 8 || !client_side_antialias_with_core)
386 if(screen_depth <= 8 || !client_side_antialias_with_render)
390 else TRACE("Using X11 core fonts\n");
393 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
394 static void get_xrender_color(WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
396 XRenderPictFormat *pf = wxr_format->pict_format;
398 if(pf->direct.redMask)
399 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
403 if(pf->direct.greenMask)
404 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
406 dst_color->green = 0;
408 if(pf->direct.blueMask)
409 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
413 dst_color->alpha = 0xffff;
416 static WineXRenderFormat *get_xrender_format(WXRFormat format)
419 for(i=0; i<WineXRenderFormatsListSize; i++)
421 if(wxr_formats[i].format == format)
423 TRACE("Returning wxr_format=%#x\n", format);
424 return &wxr_formats[i];
430 static WineXRenderFormat *get_xrender_format_from_pdevice(X11DRV_PDEVICE *physDev)
434 switch(physDev->depth)
437 format = WXR_FORMAT_MONO;
440 /* For now fall back to the format of the default visual.
441 In the future we should check if we are using a DDB/DIB and what exact format we need.
443 return default_format;
446 return get_xrender_format(format);
449 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
451 if(p1->hash != p2->hash) return TRUE;
452 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
453 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
454 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
455 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
459 static void walk_cache(void)
463 EnterCriticalSection(&xrender_cs);
464 for(i=mru; i >= 0; i = glyphsetCache[i].next)
465 TRACE("item %d\n", i);
466 LeaveCriticalSection(&xrender_cs);
470 static int LookupEntry(LFANDSIZE *plfsz)
474 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
476 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
481 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
482 glyphsetCache[i].count++;
484 glyphsetCache[prev_i].next = glyphsetCache[i].next;
485 glyphsetCache[i].next = mru;
488 TRACE("found font in cache %d\n", i);
493 TRACE("font not in cache\n");
497 static void FreeEntry(int entry)
501 for(format = 0; format < AA_MAXVALUE; format++) {
502 gsCacheEntryFormat * formatEntry;
504 if( !glyphsetCache[entry].format[format] )
507 formatEntry = glyphsetCache[entry].format[format];
509 if(formatEntry->glyphset) {
511 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
513 formatEntry->glyphset = 0;
515 if(formatEntry->nrealized) {
516 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
517 formatEntry->realized = NULL;
518 if(formatEntry->bitmaps) {
519 for(i = 0; i < formatEntry->nrealized; i++)
520 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
521 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
522 formatEntry->bitmaps = NULL;
524 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
525 formatEntry->gis = NULL;
526 formatEntry->nrealized = 0;
529 HeapFree(GetProcessHeap(), 0, formatEntry);
530 glyphsetCache[entry].format[format] = NULL;
534 static int AllocEntry(void)
536 int best = -1, prev_best = -1, i, prev_i = -1;
539 assert(glyphsetCache[lastfree].count == -1);
540 glyphsetCache[lastfree].count = 1;
542 lastfree = glyphsetCache[lastfree].next;
544 glyphsetCache[best].next = mru;
547 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
551 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
552 if(glyphsetCache[i].count == 0) {
560 TRACE("freeing unused glyphset at cache %d\n", best);
562 glyphsetCache[best].count = 1;
564 glyphsetCache[prev_best].next = glyphsetCache[best].next;
565 glyphsetCache[best].next = mru;
573 TRACE("Growing cache\n");
576 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
578 (glyphsetCacheSize + INIT_CACHE_SIZE)
579 * sizeof(*glyphsetCache));
581 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
582 (glyphsetCacheSize + INIT_CACHE_SIZE)
583 * sizeof(*glyphsetCache));
585 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
587 glyphsetCache[i].next = i + 1;
588 glyphsetCache[i].count = -1;
590 glyphsetCache[i-1].next = -1;
591 glyphsetCacheSize += INIT_CACHE_SIZE;
593 lastfree = glyphsetCache[best].next;
594 glyphsetCache[best].count = 1;
595 glyphsetCache[best].next = mru;
597 TRACE("new free cache slot at %d\n", mru);
601 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
611 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
612 if(size == GDI_ERROR)
615 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
616 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
618 GetTextMetricsW(physDev->hdc, &tm);
619 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
622 num_recs = get_be_word(*gasp);
626 *flags = get_be_word(*(gasp + 1));
627 if(ppem <= get_be_word(*gasp))
631 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
633 HeapFree(GetProcessHeap(), 0, buffer);
637 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
641 UINT font_smoothing_type, font_smoothing_orientation;
643 if (X11DRV_XRender_Installed && subpixel &&
644 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
645 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
647 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
648 &font_smoothing_orientation, 0) &&
649 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
656 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
657 But, Wine's subpixel rendering can support the portrait mode.
660 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
668 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
673 static int hinter = -1;
674 static int subpixel = -1;
677 if((ret = LookupEntry(plfsz)) != -1) return ret;
680 entry = glyphsetCache + ret;
681 entry->lfsz = *plfsz;
682 for( format = 0; format < AA_MAXVALUE; format++ ) {
683 assert( !entry->format[format] );
686 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
688 if(hinter == -1 || subpixel == -1)
690 RASTERIZER_STATUS status;
691 GetRasterizerCaps(&status, sizeof(status));
692 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
693 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
696 switch (plfsz->lf.lfQuality)
698 case ANTIALIASED_QUALITY:
699 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
701 case CLEARTYPE_QUALITY:
702 case CLEARTYPE_NATURAL_QUALITY:
703 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
705 case DEFAULT_QUALITY:
709 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
712 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
715 entry->aa_default = AA_None;
720 entry->aa_default = AA_None;
725 static void dec_ref_cache(int index)
728 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
729 assert(glyphsetCache[index].count > 0);
730 glyphsetCache[index].count--;
733 static void lfsz_calc_hash(LFANDSIZE *plfsz)
735 DWORD hash = 0, *ptr, two_chars;
739 hash ^= plfsz->devsize.cx;
740 hash ^= plfsz->devsize.cy;
741 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
743 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
745 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
747 pwc = (WCHAR *)&two_chars;
749 *pwc = toupperW(*pwc);
751 *pwc = toupperW(*pwc);
759 /***********************************************************************
760 * X11DRV_XRender_Finalize
762 void X11DRV_XRender_Finalize(void)
766 EnterCriticalSection(&xrender_cs);
767 for(i = mru; i >= 0; i = glyphsetCache[i].next)
769 LeaveCriticalSection(&xrender_cs);
773 /***********************************************************************
774 * X11DRV_XRender_SelectFont
776 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
780 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
781 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
782 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
783 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
784 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
785 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
786 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
787 GetWorldTransform( physDev->hdc, &lfsz.xform );
788 lfsz_calc_hash(&lfsz);
790 EnterCriticalSection(&xrender_cs);
791 if(!physDev->xrender) {
792 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
793 sizeof(*physDev->xrender));
794 physDev->xrender->cache_index = -1;
796 else if(physDev->xrender->cache_index != -1)
797 dec_ref_cache(physDev->xrender->cache_index);
798 physDev->xrender->cache_index = GetCacheEntry(physDev, &lfsz);
799 LeaveCriticalSection(&xrender_cs);
803 /***********************************************************************
804 * X11DRV_XRender_DeleteDC
806 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
808 X11DRV_XRender_UpdateDrawable(physDev);
810 EnterCriticalSection(&xrender_cs);
811 if(physDev->xrender->cache_index != -1)
812 dec_ref_cache(physDev->xrender->cache_index);
813 LeaveCriticalSection(&xrender_cs);
815 HeapFree(GetProcessHeap(), 0, physDev->xrender);
816 physDev->xrender = NULL;
820 /***********************************************************************
821 * X11DRV_XRender_UpdateDrawable
823 * Deletes the pict and tile when the drawable changes.
825 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
829 if(physDev->xrender->pict)
831 TRACE("freeing pict = %lx dc = %p\n", physDev->xrender->pict, physDev->hdc);
833 pXRenderFreePicture(gdi_display, physDev->xrender->pict);
834 physDev->xrender->pict = 0;
841 /************************************************************************
844 * Helper to ExtTextOut. Must be called inside xrender_cs
846 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
853 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
854 gsCacheEntryFormat *formatEntry;
855 UINT ggo_format = GGO_GLYPH_INDEX;
856 WXRFormat wxr_format;
857 static const char zero[4];
858 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
862 ggo_format |= WINE_GGO_GRAY16_BITMAP;
865 ggo_format |= WINE_GGO_HRGB_BITMAP;
868 ggo_format |= WINE_GGO_HBGR_BITMAP;
871 ggo_format |= WINE_GGO_VRGB_BITMAP;
874 ggo_format |= WINE_GGO_VBGR_BITMAP;
878 ERR("aa = %d - not implemented\n", format);
880 ggo_format |= GGO_BITMAP;
884 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
885 if(buflen == GDI_ERROR) {
886 if(format != AA_None) {
888 entry->aa_default = AA_None;
889 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
890 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
892 if(buflen == GDI_ERROR) {
893 WARN("GetGlyphOutlineW failed\n");
896 TRACE("Turning off antialiasing for this monochrome font\n");
899 /* If there is nothing for the current type, we create the entry. */
900 if( !entry->format[format] ) {
901 entry->format[format] = HeapAlloc(GetProcessHeap(),
903 sizeof(gsCacheEntryFormat));
905 formatEntry = entry->format[format];
907 if(formatEntry->nrealized <= glyph) {
908 formatEntry->nrealized = (glyph / 128 + 1) * 128;
910 if (formatEntry->realized)
911 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
913 formatEntry->realized,
914 formatEntry->nrealized * sizeof(BOOL));
916 formatEntry->realized = HeapAlloc(GetProcessHeap(),
918 formatEntry->nrealized * sizeof(BOOL));
920 if(!X11DRV_XRender_Installed) {
921 if (formatEntry->bitmaps)
922 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
924 formatEntry->bitmaps,
925 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
927 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
929 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
931 if (formatEntry->gis)
932 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
935 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
937 formatEntry->gis = HeapAlloc(GetProcessHeap(),
939 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
943 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
946 wxr_format = WXR_FORMAT_GRAY;
953 wxr_format = WXR_FORMAT_A8R8G8B8;
957 ERR("aa = %d - not implemented\n", format);
959 wxr_format = WXR_FORMAT_MONO;
964 formatEntry->font_format = get_xrender_format(wxr_format);
965 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
970 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
971 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
972 formatEntry->realized[glyph] = TRUE;
974 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
976 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
977 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
979 gi.width = gm.gmBlackBoxX;
980 gi.height = gm.gmBlackBoxY;
981 gi.x = -gm.gmptGlyphOrigin.x;
982 gi.y = gm.gmptGlyphOrigin.y;
983 gi.xOff = gm.gmCellIncX;
984 gi.yOff = gm.gmCellIncY;
986 if(TRACE_ON(xrender)) {
991 if(format == AA_None) {
992 pitch = ((gi.width + 31) / 32) * 4;
993 for(i = 0; i < gi.height; i++) {
994 line = (unsigned char*) buf + i * pitch;
996 for(j = 0; j < pitch * 8; j++) {
997 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
999 TRACE("%s\n", output);
1002 static const char blks[] = " .:;!o*#";
1006 pitch = ((gi.width + 3) / 4) * 4;
1007 for(i = 0; i < gi.height; i++) {
1008 line = (unsigned char*) buf + i * pitch;
1010 for(j = 0; j < pitch; j++) {
1011 str[0] = blks[line[j] >> 5];
1012 strcat(output, str);
1014 TRACE("%s\n", output);
1020 if(formatEntry->glyphset) {
1021 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1022 unsigned char *byte = (unsigned char*) buf, c;
1028 /* magic to flip bit order */
1029 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1030 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1031 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1036 else if ( format != AA_Grey &&
1037 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1039 unsigned int i, *data = (unsigned int *)buf;
1040 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1045 XRenderCompositeText seems to ignore 0x0 glyphs when
1046 AA_None, which means we lose the advance width of glyphs
1047 like the space. We'll pretend that such glyphs are 1x1
1052 gi.width = gi.height = 1;
1055 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1056 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1057 wine_tsx11_unlock();
1058 HeapFree(GetProcessHeap(), 0, buf);
1060 formatEntry->bitmaps[glyph] = buf;
1063 formatEntry->gis[glyph] = gi;
1068 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1069 void *bitmap, XGlyphInfo *gi)
1071 unsigned char *srcLine = bitmap, *src;
1072 unsigned char bits, bitsMask;
1073 int width = gi->width;
1074 int stride = ((width + 31) & ~31) >> 3;
1075 int height = gi->height;
1079 TRACE("%d, %d\n", x, y);
1088 bitsMask = 0x80; /* FreeType is always MSB first */
1094 if (bits & bitsMask)
1102 bitsMask = bitsMask >> 1;
1108 } while (bits & bitsMask);
1109 XFillRectangle (gdi_display, physDev->drawable,
1110 physDev->gc, xspan, y, lenspan, 1);
1122 bitsMask = bitsMask >> 1;
1128 } while (!(bits & bitsMask));
1135 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1136 void *bitmap, XGlyphInfo *gi)
1138 unsigned char *srcLine = bitmap, *src, bits;
1139 int width = gi->width;
1140 int stride = ((width + 3) & ~3);
1141 int height = gi->height;
1166 } while (bits >= 0x80);
1167 XFillRectangle (gdi_display, physDev->drawable,
1168 physDev->gc, xspan, y, lenspan, 1);
1181 } while (bits < 0x80);
1189 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1194 while ((mask & 1) == 0)
1200 while ((mask & 1) == 1)
1209 static DWORD GetField (DWORD pixel, int shift, int len)
1211 pixel = pixel & (((1 << (len)) - 1) << shift);
1212 pixel = pixel << (32 - (shift + len)) >> 24;
1215 pixel |= (pixel >> len);
1222 static DWORD PutField (DWORD pixel, int shift, int len)
1224 shift = shift - (8 - len);
1226 pixel &= (((1 << len) - 1) << (8 - len));
1234 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1240 BYTE *maskLine, *mask, m;
1245 BYTE src_r, src_g, src_b;
1250 height = gi->height;
1253 maskStride = (width + 3) & ~3;
1255 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1256 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1257 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1259 src_r = GetField(color, r_shift, r_len);
1260 src_g = GetField(color, g_shift, g_len);
1261 src_b = GetField(color, b_shift, b_len);
1263 for(; height--; y++)
1266 maskLine += maskStride;
1271 if(y >= image->height) break;
1275 if(tx >= image->width) break;
1278 if(tx < 0) continue;
1281 XPutPixel (image, tx, y, color);
1286 pixel = XGetPixel (image, tx, y);
1288 r = GetField(pixel, r_shift, r_len);
1289 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1290 g = GetField(pixel, g_shift, g_len);
1291 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1292 b = GetField(pixel, b_shift, b_len);
1293 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1295 pixel = (PutField (r, r_shift, r_len) |
1296 PutField (g, g_shift, g_len) |
1297 PutField (b, b_shift, b_len));
1298 XPutPixel (image, tx, y, pixel);
1304 /*************************************************************
1307 * Returns an appropriate Picture for tiling the text colour.
1308 * Call and use result within the xrender_cs
1310 static Picture get_tile_pict(WineXRenderFormat *wxr_format, int text_pixel)
1317 } tiles[MAX_FORMATS], *tile;
1320 tile = &tiles[wxr_format->format];
1324 XRenderPictureAttributes pa;
1327 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1329 pa.repeat = RepeatNormal;
1330 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1331 wine_tsx11_unlock();
1333 /* init current_color to something different from text_pixel */
1334 tile->current_color = ~text_pixel;
1336 if(wxr_format->format == WXR_FORMAT_MONO)
1338 /* for a 1bpp bitmap we always need a 1 in the tile */
1339 col.red = col.green = col.blue = 0;
1342 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1343 wine_tsx11_unlock();
1347 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1349 get_xrender_color(wxr_format, text_pixel, &col);
1351 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1352 wine_tsx11_unlock();
1353 tile->current_color = text_pixel;
1358 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1363 /***********************************************************************
1364 * X11DRV_XRender_ExtTextOut
1366 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1367 const RECT *lprect, LPCWSTR wstr, UINT count,
1372 gsCacheEntry *entry;
1373 gsCacheEntryFormat *formatEntry;
1375 HDC hdc = physDev->hdc;
1376 int textPixel, backgroundPixel;
1377 HRGN saved_region = 0;
1378 BOOL disable_antialias = FALSE;
1379 AA_Type aa_type = AA_None;
1382 double cosEsc, sinEsc;
1384 WineXRenderFormat *dst_format = get_xrender_format_from_pdevice(physDev);
1385 Picture tile_pict = 0;
1387 /* Do we need to disable antialiasing because of palette mode? */
1388 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1389 TRACE("bitmap is not a DIB\n");
1391 else if (bmp.dsBmih.biBitCount <= 8) {
1392 TRACE("Disabling antialiasing\n");
1393 disable_antialias = TRUE;
1396 xgcval.function = GXcopy;
1397 xgcval.background = physDev->backgroundPixel;
1398 xgcval.fill_style = FillSolid;
1400 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1401 wine_tsx11_unlock();
1403 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1405 if(physDev->depth == 1) {
1406 if((physDev->textPixel & 0xffffff) == 0) {
1408 backgroundPixel = 1;
1411 backgroundPixel = 0;
1414 textPixel = physDev->textPixel;
1415 backgroundPixel = physDev->backgroundPixel;
1418 if(flags & ETO_OPAQUE)
1421 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1422 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1423 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1424 lprect->right - lprect->left, lprect->bottom - lprect->top );
1425 wine_tsx11_unlock();
1435 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1436 if(lf.lfEscapement != 0) {
1437 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1438 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1444 if (flags & ETO_CLIPPED)
1448 clip_region = CreateRectRgnIndirect( lprect );
1449 /* make a copy of the current device region */
1450 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1451 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1452 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1453 DeleteObject( clip_region );
1456 if(X11DRV_XRender_Installed) {
1457 if(!physDev->xrender->pict) {
1458 XRenderPictureAttributes pa;
1459 pa.subwindow_mode = IncludeInferiors;
1462 physDev->xrender->pict = pXRenderCreatePicture(gdi_display,
1463 physDev->drawable, dst_format->pict_format,
1464 CPSubwindowMode, &pa);
1465 wine_tsx11_unlock();
1467 TRACE("allocing pict = %lx dc = %p drawable = %08lx\n",
1468 physDev->xrender->pict, hdc, physDev->drawable);
1470 TRACE("using existing pict = %lx dc = %p drawable = %08lx\n",
1471 physDev->xrender->pict, hdc, physDev->drawable);
1474 if ((data = X11DRV_GetRegionData( physDev->region, 0 )))
1477 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
1478 physDev->dc_rect.left, physDev->dc_rect.top,
1479 (XRectangle *)data->Buffer, data->rdh.nCount );
1480 wine_tsx11_unlock();
1481 HeapFree( GetProcessHeap(), 0, data );
1485 EnterCriticalSection(&xrender_cs);
1487 entry = glyphsetCache + physDev->xrender->cache_index;
1488 if( disable_antialias == FALSE )
1489 aa_type = entry->aa_default;
1490 formatEntry = entry->format[aa_type];
1492 for(idx = 0; idx < count; idx++) {
1493 if( !formatEntry ) {
1494 UploadGlyph(physDev, wstr[idx], aa_type);
1495 /* re-evaluate antialias since aa_default may have changed */
1496 if( disable_antialias == FALSE )
1497 aa_type = entry->aa_default;
1498 formatEntry = entry->format[aa_type];
1499 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1500 UploadGlyph(physDev, wstr[idx], aa_type);
1505 WARN("could not upload requested glyphs\n");
1506 LeaveCriticalSection(&xrender_cs);
1510 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1511 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1513 if(X11DRV_XRender_Installed)
1515 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1517 POINT desired, current;
1518 int render_op = PictOpOver;
1520 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1521 So we pass zeros to the function and move to our starting position using the first
1522 element of the elts array. */
1524 desired.x = physDev->dc_rect.left + x;
1525 desired.y = physDev->dc_rect.top + y;
1526 current.x = current.y = 0;
1528 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1530 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1532 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1533 render_op = PictOpOutReverse; /* This gives us 'black' text */
1535 for(idx = 0; idx < count; idx++)
1537 elts[idx].glyphset = formatEntry->glyphset;
1538 elts[idx].chars = wstr + idx;
1539 elts[idx].nchars = 1;
1540 elts[idx].xOff = desired.x - current.x;
1541 elts[idx].yOff = desired.y - current.y;
1543 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1544 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1548 desired.x += formatEntry->gis[wstr[idx]].xOff;
1549 desired.y += formatEntry->gis[wstr[idx]].yOff;
1553 offset += lpDx[idx];
1554 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1555 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1559 pXRenderCompositeText16(gdi_display, render_op,
1561 physDev->xrender->pict,
1562 formatEntry->font_format->pict_format,
1563 0, 0, 0, 0, elts, count);
1564 wine_tsx11_unlock();
1565 HeapFree(GetProcessHeap(), 0, elts);
1567 INT offset = 0, xoff = 0, yoff = 0;
1569 XSetForeground( gdi_display, physDev->gc, textPixel );
1571 if(aa_type == AA_None || physDev->depth == 1)
1573 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1575 if(aa_type == AA_None)
1576 sharp_glyph_fn = SharpGlyphMono;
1578 sharp_glyph_fn = SharpGlyphGray;
1580 for(idx = 0; idx < count; idx++) {
1581 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1582 physDev->dc_rect.top + y + yoff,
1583 formatEntry->bitmaps[wstr[idx]],
1584 &formatEntry->gis[wstr[idx]]);
1586 offset += lpDx[idx];
1587 xoff = offset * cosEsc;
1588 yoff = offset * -sinEsc;
1590 xoff += formatEntry->gis[wstr[idx]].xOff;
1591 yoff += formatEntry->gis[wstr[idx]].yOff;
1596 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1597 RECT extents = {0, 0, 0, 0};
1599 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1600 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1602 TRACE("drawable %dx%d\n", w, h);
1604 for(idx = 0; idx < count; idx++) {
1605 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1606 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1607 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1608 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1609 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1610 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1611 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1612 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1614 offset += lpDx[idx];
1615 cur.x = offset * cosEsc;
1616 cur.y = offset * -sinEsc;
1618 cur.x += formatEntry->gis[wstr[idx]].xOff;
1619 cur.y += formatEntry->gis[wstr[idx]].yOff;
1622 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1623 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1625 if(physDev->dc_rect.left + x + extents.left >= 0) {
1626 image_x = physDev->dc_rect.left + x + extents.left;
1630 image_off_x = physDev->dc_rect.left + x + extents.left;
1632 if(physDev->dc_rect.top + y + extents.top >= 0) {
1633 image_y = physDev->dc_rect.top + y + extents.top;
1637 image_off_y = physDev->dc_rect.top + y + extents.top;
1639 if(physDev->dc_rect.left + x + extents.right < w)
1640 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1642 image_w = w - image_x;
1643 if(physDev->dc_rect.top + y + extents.bottom < h)
1644 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1646 image_h = h - image_y;
1648 if(image_w <= 0 || image_h <= 0) goto no_image;
1650 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1651 image = XGetImage(gdi_display, physDev->drawable,
1652 image_x, image_y, image_w, image_h,
1653 AllPlanes, ZPixmap);
1654 X11DRV_check_error();
1656 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1657 gdi_display, (int)physDev->drawable, image_x, image_y,
1658 image_w, image_h, AllPlanes, ZPixmap,
1659 physDev->depth, image);
1661 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1666 gcv.graphics_exposures = False;
1667 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1668 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1669 image_w, image_h, 0, 0);
1670 XFreeGC(gdi_display, gc);
1671 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1672 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1674 X11DRV_check_error();
1675 XFreePixmap(gdi_display, xpm);
1677 if(!image) goto no_image;
1679 image->red_mask = visual->red_mask;
1680 image->green_mask = visual->green_mask;
1681 image->blue_mask = visual->blue_mask;
1683 offset = xoff = yoff = 0;
1684 for(idx = 0; idx < count; idx++) {
1685 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1686 yoff + image_off_y - extents.top,
1687 formatEntry->bitmaps[wstr[idx]],
1688 &formatEntry->gis[wstr[idx]],
1689 physDev->textPixel);
1691 offset += lpDx[idx];
1692 xoff = offset * cosEsc;
1693 yoff = offset * -sinEsc;
1695 xoff += formatEntry->gis[wstr[idx]].xOff;
1696 yoff += formatEntry->gis[wstr[idx]].yOff;
1699 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1700 image_x, image_y, image_w, image_h);
1701 XDestroyImage(image);
1704 wine_tsx11_unlock();
1706 LeaveCriticalSection(&xrender_cs);
1708 if (flags & ETO_CLIPPED)
1710 /* restore the device region */
1711 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1712 DeleteObject( saved_region );
1718 X11DRV_UnlockDIBSection( physDev, TRUE );
1722 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
1723 static void set_xrender_transformation(Picture src_pict, float xscale, float yscale, int xoffset, int yoffset)
1725 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
1726 XTransform xform = {{
1727 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
1728 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
1729 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
1732 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
1736 /* Helper function for (stretched) blitting using xrender */
1737 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)
1739 /* Further down a transformation matrix is used for stretching and mirroring the source data.
1740 * xscale/yscale contain the scaling factors for the width and height. In case of mirroring
1741 * we also need a x- and y-offset because without the pixels will be in the wrong quadrant of the x-y plane.
1743 int x_offset = (xscale<0) ? width : 0;
1744 int y_offset = (yscale<0) ? height : 0;
1746 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1747 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1748 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1749 if(xscale != 1.0 || yscale != 1.0)
1751 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1753 set_xrender_transformation(mask_pict, xscale, yscale, x_offset, y_offset);
1755 set_xrender_transformation(src_pict, xscale, yscale, x_src + x_offset, y_src + y_offset);
1757 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, 0, 0, 0, 0, 0, 0, width, height);
1761 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1763 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
1765 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1767 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, x_src, y_src, 0, 0, 0, 0, width, height);
1771 /******************************************************************************
1772 * AlphaBlend (x11drv.@)
1774 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1775 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1776 BLENDFUNCTION blendfn)
1778 XRenderPictureAttributes pa;
1779 Picture dst_pict, src_pict;
1785 DWORD *dstbits, *data;
1788 BOOL top_down = FALSE;
1790 WineXRenderFormat *dst_format = get_xrender_format_from_pdevice(devDst);
1791 WineXRenderFormat *src_format;
1794 if(!X11DRV_XRender_Installed) {
1795 FIXME("Unable to AlphaBlend without Xrender\n");
1800 pts[1].x = xDst + widthDst;
1801 pts[1].y = yDst + heightDst;
1802 LPtoDP(devDst->hdc, pts, 2);
1805 widthDst = pts[1].x - pts[0].x;
1806 heightDst = pts[1].y - pts[0].y;
1810 pts[1].x = xSrc + widthSrc;
1811 pts[1].y = ySrc + heightSrc;
1812 LPtoDP(devSrc->hdc, pts, 2);
1815 widthSrc = pts[1].x - pts[0].x;
1816 heightSrc = pts[1].y - pts[0].y;
1817 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1819 /* If the source is a 1x1 bitmap, tiling is equivalent to stretching, but
1820 tiling is much faster. Therefore, we do no stretching in this case. */
1821 repeat_src = widthSrc == 1 && heightSrc == 1;
1823 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1824 if((widthDst != widthSrc || heightDst != heightSrc) && !repeat_src)
1826 if(!pXRenderSetPictureTransform && !repeat_src)
1829 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1833 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1835 static BOOL out = FALSE;
1838 FIXME("not a dibsection\n");
1844 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1845 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1847 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1848 SetLastError(ERROR_INVALID_PARAMETER);
1852 if ((blendfn.AlphaFormat & AC_SRC_ALPHA) && blendfn.SourceConstantAlpha != 0xff)
1853 FIXME("Ignoring SourceConstantAlpha %d for AC_SRC_ALPHA\n", blendfn.SourceConstantAlpha);
1855 if(dib.dsBm.bmBitsPixel != 32) {
1856 FIXME("not a 32 bpp dibsection\n");
1859 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1861 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1863 dstbits += widthSrc * (heightSrc - 1);
1865 y = y2 + heightSrc - 1;
1869 y = dib.dsBmih.biHeight - ySrc - 1;
1870 y2 = y - heightSrc + 1;
1873 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1877 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1879 dstbits += (top_down ? -1 : 1) * widthSrc;
1884 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
1889 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1890 for (x = 0; x < widthSrc; x++)
1892 DWORD argb = *srcbits++;
1893 argb = (argb & 0xffffff) | source_alpha;
1896 if (top_down) /* we traversed the row forward so we should go back by two rows */
1897 dstbits -= 2 * widthSrc;
1902 rgndata = X11DRV_GetRegionData( devDst->region, 0 );
1905 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
1906 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
1908 src_format = get_xrender_format(WXR_FORMAT_A8R8G8B8);
1909 TRACE("src_format %p\n", src_format);
1912 WARN("Unable to find a picture format supporting alpha, make sure X is running at 24-bit\n");
1916 pa.subwindow_mode = IncludeInferiors;
1917 pa.repeat = repeat_src ? RepeatNormal : RepeatNone;
1919 /* FIXME use devDst->xrender->pict ? */
1920 dst_pict = pXRenderCreatePicture(gdi_display,
1922 dst_format->pict_format,
1923 CPSubwindowMode, &pa);
1924 TRACE("dst_pict %08lx\n", dst_pict);
1925 TRACE("src_drawable = %08lx\n", devSrc->drawable);
1926 xpm = XCreatePixmap(gdi_display,
1928 widthSrc, heightSrc, 32);
1929 gcv.graphics_exposures = False;
1930 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1931 TRACE("xpm = %08lx\n", xpm);
1932 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
1934 src_pict = pXRenderCreatePicture(gdi_display,
1935 xpm, src_format->pict_format,
1936 CPSubwindowMode|CPRepeat, &pa);
1937 TRACE("src_pict %08lx\n", src_pict);
1941 pXRenderSetPictureClipRectangles( gdi_display, dst_pict,
1942 devDst->dc_rect.left, devDst->dc_rect.top,
1943 (XRectangle *)rgndata->Buffer,
1944 rgndata->rdh.nCount );
1945 HeapFree( GetProcessHeap(), 0, rgndata );
1948 /* Make sure we ALWAYS set the transformation matrix even if we don't need to scale. The reason is
1949 * that later on we want to reuse pictures (it can bring a lot of extra performance) and each time
1950 * a different transformation matrix might have been used. */
1951 set_xrender_transformation(src_pict, widthSrc/(double)widthDst, heightSrc/(double)heightDst, 0, 0);
1952 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
1954 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
1957 pXRenderFreePicture(gdi_display, src_pict);
1958 XFreePixmap(gdi_display, xpm);
1959 XFreeGC(gdi_display, gc);
1960 pXRenderFreePicture(gdi_display, dst_pict);
1962 XDestroyImage(image);
1964 wine_tsx11_unlock();
1965 HeapFree(GetProcessHeap(), 0, data);
1969 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
1970 Pixmap pixmap, GC gc,
1971 INT widthSrc, INT heightSrc,
1972 INT widthDst, INT heightDst,
1973 RECT *visRectSrc, RECT *visRectDst )
1975 BOOL stretch = (widthSrc != widthDst) || (heightSrc != heightDst);
1976 int width = visRectDst->right - visRectDst->left;
1977 int height = visRectDst->bottom - visRectDst->top;
1978 int x_src = physDevSrc->dc_rect.left + visRectSrc->left;
1979 int y_src = physDevSrc->dc_rect.top + visRectSrc->top;
1980 WineXRenderFormat *src_format = get_xrender_format_from_pdevice(physDevSrc);
1981 WineXRenderFormat *dst_format = get_xrender_format_from_pdevice(physDevDst);
1982 Picture src_pict=0, dst_pict=0, mask_pict=0;
1984 double xscale = widthSrc/(double)widthDst;
1985 double yscale = heightSrc/(double)heightDst;
1987 XRenderPictureAttributes pa;
1988 pa.subwindow_mode = IncludeInferiors;
1989 pa.repeat = RepeatNone;
1991 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n", physDevSrc->depth, widthSrc, heightSrc, x_src, y_src);
1992 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, widthDst, heightDst);
1994 if(!X11DRV_XRender_Installed)
1996 TRACE("Not using XRender since it is not available or disabled\n");
2000 /* XRender can't handle palettes, so abort */
2001 if(X11DRV_PALETTE_XPixelToPalette)
2004 /* XRender is of no use in this case */
2005 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2008 /* Just use traditional X copy when the depths match and we don't need stretching */
2009 if((physDevSrc->depth == physDevDst->depth) && !stretch)
2011 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2013 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2014 wine_tsx11_unlock();
2019 if(physDevSrc->depth == 1)
2022 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2024 /* We use the source drawable as a mask */
2026 mask_pict = pXRenderCreatePicture(gdi_display, physDevSrc->drawable, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2028 /* Use backgroundPixel as the foreground color */
2029 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2031 /* Create a destination picture and fill it with textPixel color as the background color */
2032 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2033 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2035 xrender_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2037 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2038 if(mask_pict) pXRenderFreePicture(gdi_display, mask_pict);
2039 wine_tsx11_unlock();
2041 else /* color -> color but with different depths */
2044 src_pict = pXRenderCreatePicture(gdi_display,
2045 physDevSrc->drawable, src_format->pict_format,
2046 CPSubwindowMode|CPRepeat, &pa);
2048 dst_pict = pXRenderCreatePicture(gdi_display,
2049 pixmap, dst_format->pict_format,
2050 CPSubwindowMode|CPRepeat, &pa);
2052 xrender_blit(src_pict, 0, dst_pict, x_src, y_src, xscale, yscale, width, height);
2054 if(src_pict) pXRenderFreePicture(gdi_display, src_pict);
2055 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2056 wine_tsx11_unlock();
2061 #else /* SONAME_LIBXRENDER */
2063 void X11DRV_XRender_Init(void)
2065 TRACE("XRender support not compiled in.\n");
2069 void X11DRV_XRender_Finalize(void)
2073 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2079 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2085 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2086 const RECT *lprect, LPCWSTR wstr, UINT count,
2093 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2099 /******************************************************************************
2100 * AlphaBlend (x11drv.@)
2102 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
2103 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
2104 BLENDFUNCTION blendfn)
2106 FIXME("not supported - XRENDER headers were missing at compile time\n");
2110 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2111 Pixmap pixmap, GC gc,
2113 INT widthSrc, INT heightSrc,
2115 INT widthDst, INT heightDst,
2116 RECT *visRectSrc, RECT *visRectDst)
2120 #endif /* SONAME_LIBXRENDER */