winemenubuilder: Escape freedesktop exec keys properly.
[wine/multimedia.git] / dlls / winex11.drv / xrender.c
blobd9735b0a87cda117dddaa644c1930b33363ecf10
1 /*
2 * Functions to use the XRender extension
4 * Copyright 2001, 2002 Huw D M Davies for CodeWeavers
5 * Copyright 2009 Roderick Colenbrander
7 * Some parts also:
8 * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <stdlib.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "x11drv.h"
35 #include "winternl.h"
36 #include "wine/library.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 int using_client_side_fonts = FALSE;
42 WINE_DEFAULT_DEBUG_CHANNEL(xrender);
44 #ifdef SONAME_LIBXRENDER
46 static BOOL X11DRV_XRender_Installed = FALSE;
48 #include <X11/Xlib.h>
49 #include <X11/extensions/Xrender.h>
51 #ifndef RepeatNone /* added in 0.10 */
52 #define RepeatNone 0
53 #define RepeatNormal 1
54 #define RepeatPad 2
55 #define RepeatReflect 3
56 #endif
58 typedef enum wine_xrformat
60 WXR_FORMAT_MONO,
61 WXR_FORMAT_GRAY,
62 WXR_FORMAT_X1R5G5B5,
63 WXR_FORMAT_X1B5G5R5,
64 WXR_FORMAT_R5G6B5,
65 WXR_FORMAT_B5G6R5,
66 WXR_FORMAT_R8G8B8,
67 WXR_FORMAT_B8G8R8,
68 WXR_FORMAT_A8R8G8B8,
69 WXR_FORMAT_B8G8R8A8,
70 WXR_FORMAT_X8R8G8B8,
71 WXR_FORMAT_B8G8R8X8,
72 WXR_NB_FORMATS
73 } WXRFormat;
75 typedef struct wine_xrender_format_template
77 WXRFormat wxr_format;
78 unsigned int depth;
79 unsigned int alpha;
80 unsigned int alphaMask;
81 unsigned int red;
82 unsigned int redMask;
83 unsigned int green;
84 unsigned int greenMask;
85 unsigned int blue;
86 unsigned int blueMask;
87 } WineXRenderFormatTemplate;
89 static const WineXRenderFormatTemplate wxr_formats_template[WXR_NB_FORMATS] =
91 /* Format depth alpha mask red mask green mask blue mask*/
92 {WXR_FORMAT_MONO, 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
93 {WXR_FORMAT_GRAY, 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
94 {WXR_FORMAT_X1R5G5B5, 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
95 {WXR_FORMAT_X1B5G5R5, 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
96 {WXR_FORMAT_R5G6B5, 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
97 {WXR_FORMAT_B5G6R5, 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
98 {WXR_FORMAT_R8G8B8, 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
99 {WXR_FORMAT_B8G8R8, 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
100 {WXR_FORMAT_A8R8G8B8, 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
101 {WXR_FORMAT_B8G8R8A8, 32, 0, 0xff, 8, 0xff, 16, 0xff, 24, 0xff },
102 {WXR_FORMAT_X8R8G8B8, 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
103 {WXR_FORMAT_B8G8R8X8, 32, 0, 0, 8, 0xff, 16, 0xff, 24, 0xff },
106 typedef struct wine_xrender_format
108 WXRFormat format;
109 XRenderPictFormat *pict_format;
110 } WineXRenderFormat;
112 static WineXRenderFormat wxr_formats[WXR_NB_FORMATS];
113 static int WineXRenderFormatsListSize = 0;
114 static WineXRenderFormat *default_format = NULL;
116 typedef struct
118 LOGFONTW lf;
119 XFORM xform;
120 SIZE devsize; /* size in device coords */
121 DWORD hash;
122 } LFANDSIZE;
124 #define INITIAL_REALIZED_BUF_SIZE 128
126 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
128 typedef struct
130 GlyphSet glyphset;
131 const WineXRenderFormat *font_format;
132 int nrealized;
133 BOOL *realized;
134 void **bitmaps;
135 XGlyphInfo *gis;
136 } gsCacheEntryFormat;
138 typedef struct
140 LFANDSIZE lfsz;
141 AA_Type aa_default;
142 gsCacheEntryFormat * format[AA_MAXVALUE];
143 INT count;
144 INT next;
145 } gsCacheEntry;
147 struct xrender_info
149 int cache_index;
150 Picture pict;
151 Picture pict_src;
152 const WineXRenderFormat *format;
155 static gsCacheEntry *glyphsetCache = NULL;
156 static DWORD glyphsetCacheSize = 0;
157 static INT lastfree = -1;
158 static INT mru = -1;
160 #define INIT_CACHE_SIZE 10
162 static int antialias = 1;
164 static void *xrender_handle;
166 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
167 MAKE_FUNCPTR(XRenderAddGlyphs)
168 MAKE_FUNCPTR(XRenderComposite)
169 MAKE_FUNCPTR(XRenderCompositeString8)
170 MAKE_FUNCPTR(XRenderCompositeString16)
171 MAKE_FUNCPTR(XRenderCompositeString32)
172 MAKE_FUNCPTR(XRenderCompositeText16)
173 MAKE_FUNCPTR(XRenderCreateGlyphSet)
174 MAKE_FUNCPTR(XRenderCreatePicture)
175 MAKE_FUNCPTR(XRenderFillRectangle)
176 MAKE_FUNCPTR(XRenderFindFormat)
177 MAKE_FUNCPTR(XRenderFindVisualFormat)
178 MAKE_FUNCPTR(XRenderFreeGlyphSet)
179 MAKE_FUNCPTR(XRenderFreePicture)
180 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
181 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
182 MAKE_FUNCPTR(XRenderSetPictureTransform)
183 #endif
184 MAKE_FUNCPTR(XRenderQueryExtension)
185 #undef MAKE_FUNCPTR
187 static CRITICAL_SECTION xrender_cs;
188 static CRITICAL_SECTION_DEBUG critsect_debug =
190 0, 0, &xrender_cs,
191 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
192 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
194 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
196 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
197 ( ( (ULONG)_x4 << 24 ) | \
198 ( (ULONG)_x3 << 16 ) | \
199 ( (ULONG)_x2 << 8 ) | \
200 (ULONG)_x1 )
202 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
204 #define GASP_GRIDFIT 0x01
205 #define GASP_DOGRAY 0x02
207 #ifdef WORDS_BIGENDIAN
208 #define get_be_word(x) (x)
209 #define NATIVE_BYTE_ORDER MSBFirst
210 #else
211 #define get_be_word(x) RtlUshortByteSwap(x)
212 #define NATIVE_BYTE_ORDER LSBFirst
213 #endif
215 static WXRFormat get_format_without_alpha( WXRFormat format )
217 switch (format)
219 case WXR_FORMAT_A8R8G8B8: return WXR_FORMAT_X8R8G8B8;
220 case WXR_FORMAT_B8G8R8A8: return WXR_FORMAT_B8G8R8X8;
221 default: return format;
225 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
227 templ->id = 0;
228 templ->type = PictTypeDirect;
229 templ->depth = fmt->depth;
230 templ->direct.alpha = fmt->alpha;
231 templ->direct.alphaMask = fmt->alphaMask;
232 templ->direct.red = fmt->red;
233 templ->direct.redMask = fmt->redMask;
234 templ->direct.green = fmt->green;
235 templ->direct.greenMask = fmt->greenMask;
236 templ->direct.blue = fmt->blue;
237 templ->direct.blueMask = fmt->blueMask;
238 templ->colormap = 0;
240 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
242 return TRUE;
245 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
247 if(fmt->depth != screen_depth)
248 return FALSE;
249 if( (fmt->redMask << fmt->red) != visual->red_mask)
250 return FALSE;
251 if( (fmt->greenMask << fmt->green) != visual->green_mask)
252 return FALSE;
253 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
254 return FALSE;
256 /* We never select a default ARGB visual */
257 if(fmt->alphaMask)
258 return FALSE;
260 return TRUE;
263 static int load_xrender_formats(void)
265 unsigned int i;
266 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
268 XRenderPictFormat templ, *pict_format;
270 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
272 wine_tsx11_lock();
273 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
274 if(!pict_format)
276 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
277 if (visual->class == DirectColor)
279 XVisualInfo info;
280 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
281 screen_depth, TrueColor, &info ))
283 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
284 if (pict_format) visual = info.visual;
288 wine_tsx11_unlock();
290 if(pict_format)
292 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
293 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
294 default_format = &wxr_formats[WineXRenderFormatsListSize];
295 WineXRenderFormatsListSize++;
296 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
299 else
301 unsigned long mask = 0;
302 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
304 wine_tsx11_lock();
305 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
306 wine_tsx11_unlock();
308 if(pict_format)
310 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
311 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
312 WineXRenderFormatsListSize++;
313 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
317 return WineXRenderFormatsListSize;
320 /***********************************************************************
321 * X11DRV_XRender_Init
323 * Let's see if our XServer has the extension available
326 void X11DRV_XRender_Init(void)
328 int event_base, i;
330 if (client_side_with_render &&
331 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
332 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
333 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
336 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
337 LOAD_FUNCPTR(XRenderAddGlyphs)
338 LOAD_FUNCPTR(XRenderComposite)
339 LOAD_FUNCPTR(XRenderCompositeString8)
340 LOAD_FUNCPTR(XRenderCompositeString16)
341 LOAD_FUNCPTR(XRenderCompositeString32)
342 LOAD_FUNCPTR(XRenderCompositeText16)
343 LOAD_FUNCPTR(XRenderCreateGlyphSet)
344 LOAD_FUNCPTR(XRenderCreatePicture)
345 LOAD_FUNCPTR(XRenderFillRectangle)
346 LOAD_FUNCPTR(XRenderFindFormat)
347 LOAD_FUNCPTR(XRenderFindVisualFormat)
348 LOAD_FUNCPTR(XRenderFreeGlyphSet)
349 LOAD_FUNCPTR(XRenderFreePicture)
350 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
351 LOAD_FUNCPTR(XRenderQueryExtension)
352 #undef LOAD_FUNCPTR
353 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
354 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
355 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
356 #undef LOAD_OPTIONAL_FUNCPTR
357 #endif
359 wine_tsx11_lock();
360 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
361 wine_tsx11_unlock();
362 if(X11DRV_XRender_Installed) {
363 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
364 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
366 wine_tsx11_unlock();
367 WINE_MESSAGE(
368 "Wine has detected that you probably have a buggy version\n"
369 "of libXrender.so . Because of this client side font rendering\n"
370 "will be disabled. Please upgrade this library.\n");
371 X11DRV_XRender_Installed = FALSE;
372 return;
375 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
376 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
377 X11DRV_XRender_Installed = FALSE;
382 sym_not_found:
383 if(X11DRV_XRender_Installed || client_side_with_core)
385 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
386 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
388 glyphsetCacheSize = INIT_CACHE_SIZE;
389 lastfree = 0;
390 for(i = 0; i < INIT_CACHE_SIZE; i++) {
391 glyphsetCache[i].next = i + 1;
392 glyphsetCache[i].count = -1;
394 glyphsetCache[i-1].next = -1;
395 using_client_side_fonts = 1;
397 if(!X11DRV_XRender_Installed) {
398 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
399 if(screen_depth <= 8 || !client_side_antialias_with_core)
400 antialias = 0;
401 } else {
402 if(screen_depth <= 8 || !client_side_antialias_with_render)
403 antialias = 0;
406 else TRACE("Using X11 core fonts\n");
409 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
410 static void get_xrender_color(const WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
412 XRenderPictFormat *pf = wxr_format->pict_format;
414 if(pf->direct.redMask)
415 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
416 else
417 dst_color->red = 0;
419 if(pf->direct.greenMask)
420 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
421 else
422 dst_color->green = 0;
424 if(pf->direct.blueMask)
425 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
426 else
427 dst_color->blue = 0;
429 dst_color->alpha = 0xffff;
432 static const WineXRenderFormat *get_xrender_format(WXRFormat format)
434 int i;
435 for(i=0; i<WineXRenderFormatsListSize; i++)
437 if(wxr_formats[i].format == format)
439 TRACE("Returning wxr_format=%#x\n", format);
440 return &wxr_formats[i];
443 return NULL;
446 static const WineXRenderFormat *get_xrender_format_from_color_shifts(int depth, ColorShifts *shifts)
448 int redMask, greenMask, blueMask;
449 unsigned int i;
451 if(depth == 1)
452 return get_xrender_format(WXR_FORMAT_MONO);
454 /* physDevs of a depth <=8, don't have color_shifts set and XRender can't handle those except for 1-bit */
455 if(!shifts)
456 return default_format;
458 redMask = shifts->physicalRed.max << shifts->physicalRed.shift;
459 greenMask = shifts->physicalGreen.max << shifts->physicalGreen.shift;
460 blueMask = shifts->physicalBlue.max << shifts->physicalBlue.shift;
462 /* Try to locate a format which matches the specification of the dibsection. */
463 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
465 if( depth == wxr_formats_template[i].depth &&
466 redMask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
467 greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
468 blueMask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
471 /* When we reach this stage the format was found in our template table but this doesn't mean that
472 * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
474 return get_xrender_format(wxr_formats_template[i].wxr_format);
478 /* This should not happen because when we reach 'shifts' must have been set and we only allows shifts which are backed by X */
479 ERR("No XRender format found!\n");
480 return NULL;
483 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
484 static void set_xrender_transformation(Picture src_pict, double xscale, double yscale, int xoffset, int yoffset)
486 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
487 XTransform xform = {{
488 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
489 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
490 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
493 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
494 #endif
497 /* check if we can use repeating instead of scaling for the specified source DC */
498 static BOOL use_source_repeat( X11DRV_PDEVICE *physDev )
500 return (physDev->bitmap &&
501 physDev->drawable_rect.right - physDev->drawable_rect.left == 1 &&
502 physDev->drawable_rect.bottom - physDev->drawable_rect.top == 1);
505 static struct xrender_info *get_xrender_info(X11DRV_PDEVICE *physDev)
507 if(!physDev->xrender)
509 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physDev->xrender));
511 if(!physDev->xrender)
513 ERR("Unable to allocate XRENDERINFO!\n");
514 return NULL;
516 physDev->xrender->cache_index = -1;
518 if (!physDev->xrender->format)
519 physDev->xrender->format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
521 return physDev->xrender;
524 static Picture get_xrender_picture(X11DRV_PDEVICE *physDev)
526 struct xrender_info *info = get_xrender_info(physDev);
527 if (!info) return 0;
529 if (!info->pict && info->format)
531 XRenderPictureAttributes pa;
532 RGNDATA *clip = X11DRV_GetRegionData( physDev->region, 0 );
534 wine_tsx11_lock();
535 pa.subwindow_mode = IncludeInferiors;
536 info->pict = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
537 CPSubwindowMode, &pa);
538 if (info->pict && clip)
539 pXRenderSetPictureClipRectangles( gdi_display, info->pict,
540 physDev->dc_rect.left, physDev->dc_rect.top,
541 (XRectangle *)clip->Buffer, clip->rdh.nCount );
542 wine_tsx11_unlock();
543 TRACE("Allocing pict=%lx dc=%p drawable=%08lx\n", info->pict, physDev->hdc, physDev->drawable);
544 HeapFree( GetProcessHeap(), 0, clip );
547 return info->pict;
550 static Picture get_xrender_picture_source(X11DRV_PDEVICE *physDev, BOOL repeat)
552 struct xrender_info *info = get_xrender_info(physDev);
553 if (!info) return 0;
555 if (!info->pict_src && info->format)
557 XRenderPictureAttributes pa;
559 wine_tsx11_lock();
560 pa.subwindow_mode = IncludeInferiors;
561 pa.repeat = repeat ? RepeatNormal : RepeatNone;
562 info->pict_src = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
563 CPSubwindowMode|CPRepeat, &pa);
564 wine_tsx11_unlock();
566 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx repeat=%u\n",
567 info->pict_src, physDev->hdc, physDev->drawable, pa.repeat);
570 return info->pict_src;
573 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
575 if(p1->hash != p2->hash) return TRUE;
576 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
577 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
578 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
579 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
582 #if 0
583 static void walk_cache(void)
585 int i;
587 EnterCriticalSection(&xrender_cs);
588 for(i=mru; i >= 0; i = glyphsetCache[i].next)
589 TRACE("item %d\n", i);
590 LeaveCriticalSection(&xrender_cs);
592 #endif
594 static int LookupEntry(LFANDSIZE *plfsz)
596 int i, prev_i = -1;
598 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
599 TRACE("%d\n", i);
600 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
601 i = -1;
602 break;
605 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
606 glyphsetCache[i].count++;
607 if(prev_i >= 0) {
608 glyphsetCache[prev_i].next = glyphsetCache[i].next;
609 glyphsetCache[i].next = mru;
610 mru = i;
612 TRACE("found font in cache %d\n", i);
613 return i;
615 prev_i = i;
617 TRACE("font not in cache\n");
618 return -1;
621 static void FreeEntry(int entry)
623 int i, format;
625 for(format = 0; format < AA_MAXVALUE; format++) {
626 gsCacheEntryFormat * formatEntry;
628 if( !glyphsetCache[entry].format[format] )
629 continue;
631 formatEntry = glyphsetCache[entry].format[format];
633 if(formatEntry->glyphset) {
634 wine_tsx11_lock();
635 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
636 wine_tsx11_unlock();
637 formatEntry->glyphset = 0;
639 if(formatEntry->nrealized) {
640 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
641 formatEntry->realized = NULL;
642 if(formatEntry->bitmaps) {
643 for(i = 0; i < formatEntry->nrealized; i++)
644 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
645 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
646 formatEntry->bitmaps = NULL;
648 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
649 formatEntry->gis = NULL;
650 formatEntry->nrealized = 0;
653 HeapFree(GetProcessHeap(), 0, formatEntry);
654 glyphsetCache[entry].format[format] = NULL;
658 static int AllocEntry(void)
660 int best = -1, prev_best = -1, i, prev_i = -1;
662 if(lastfree >= 0) {
663 assert(glyphsetCache[lastfree].count == -1);
664 glyphsetCache[lastfree].count = 1;
665 best = lastfree;
666 lastfree = glyphsetCache[lastfree].next;
667 assert(best != mru);
668 glyphsetCache[best].next = mru;
669 mru = best;
671 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
672 return mru;
675 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
676 if(glyphsetCache[i].count == 0) {
677 best = i;
678 prev_best = prev_i;
680 prev_i = i;
683 if(best >= 0) {
684 TRACE("freeing unused glyphset at cache %d\n", best);
685 FreeEntry(best);
686 glyphsetCache[best].count = 1;
687 if(prev_best >= 0) {
688 glyphsetCache[prev_best].next = glyphsetCache[best].next;
689 glyphsetCache[best].next = mru;
690 mru = best;
691 } else {
692 assert(mru == best);
694 return mru;
697 TRACE("Growing cache\n");
699 if (glyphsetCache)
700 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
701 glyphsetCache,
702 (glyphsetCacheSize + INIT_CACHE_SIZE)
703 * sizeof(*glyphsetCache));
704 else
705 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
706 (glyphsetCacheSize + INIT_CACHE_SIZE)
707 * sizeof(*glyphsetCache));
709 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
710 i++) {
711 glyphsetCache[i].next = i + 1;
712 glyphsetCache[i].count = -1;
714 glyphsetCache[i-1].next = -1;
715 glyphsetCacheSize += INIT_CACHE_SIZE;
717 lastfree = glyphsetCache[best].next;
718 glyphsetCache[best].count = 1;
719 glyphsetCache[best].next = mru;
720 mru = best;
721 TRACE("new free cache slot at %d\n", mru);
722 return mru;
725 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
727 DWORD size;
728 WORD *gasp, *buffer;
729 WORD num_recs;
730 DWORD ppem;
731 TEXTMETRICW tm;
733 *flags = 0;
735 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
736 if(size == GDI_ERROR)
737 return FALSE;
739 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
740 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
742 GetTextMetricsW(physDev->hdc, &tm);
743 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
745 gasp++;
746 num_recs = get_be_word(*gasp);
747 gasp++;
748 while(num_recs--)
750 *flags = get_be_word(*(gasp + 1));
751 if(ppem <= get_be_word(*gasp))
752 break;
753 gasp += 2;
755 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
757 HeapFree(GetProcessHeap(), 0, buffer);
758 return TRUE;
761 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
763 AA_Type ret;
764 WORD flags;
765 UINT font_smoothing_type, font_smoothing_orientation;
767 if (X11DRV_XRender_Installed && subpixel &&
768 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
769 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
771 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
772 &font_smoothing_orientation, 0) &&
773 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
775 ret = AA_BGR;
777 else
778 ret = AA_RGB;
779 /*FIXME
780 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
781 But, Wine's subpixel rendering can support the portrait mode.
784 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
785 ret = AA_Grey;
786 else
787 ret = AA_None;
789 return ret;
792 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
794 int ret;
795 int format;
796 gsCacheEntry *entry;
797 static int hinter = -1;
798 static int subpixel = -1;
799 BOOL font_smoothing;
801 if((ret = LookupEntry(plfsz)) != -1) return ret;
803 ret = AllocEntry();
804 entry = glyphsetCache + ret;
805 entry->lfsz = *plfsz;
806 for( format = 0; format < AA_MAXVALUE; format++ ) {
807 assert( !entry->format[format] );
810 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
812 if(hinter == -1 || subpixel == -1)
814 RASTERIZER_STATUS status;
815 GetRasterizerCaps(&status, sizeof(status));
816 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
817 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
820 switch (plfsz->lf.lfQuality)
822 case ANTIALIASED_QUALITY:
823 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
824 break;
825 case CLEARTYPE_QUALITY:
826 case CLEARTYPE_NATURAL_QUALITY:
827 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
828 break;
829 case DEFAULT_QUALITY:
830 case DRAFT_QUALITY:
831 case PROOF_QUALITY:
832 default:
833 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
834 font_smoothing)
836 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
838 else
839 entry->aa_default = AA_None;
840 break;
843 else
844 entry->aa_default = AA_None;
846 return ret;
849 static void dec_ref_cache(int index)
851 assert(index >= 0);
852 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
853 assert(glyphsetCache[index].count > 0);
854 glyphsetCache[index].count--;
857 static void lfsz_calc_hash(LFANDSIZE *plfsz)
859 DWORD hash = 0, *ptr, two_chars;
860 WORD *pwc;
861 int i;
863 hash ^= plfsz->devsize.cx;
864 hash ^= plfsz->devsize.cy;
865 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
866 hash ^= *ptr;
867 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
868 hash ^= *ptr;
869 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
870 two_chars = *ptr;
871 pwc = (WCHAR *)&two_chars;
872 if(!*pwc) break;
873 *pwc = toupperW(*pwc);
874 pwc++;
875 *pwc = toupperW(*pwc);
876 hash ^= two_chars;
877 if(!*pwc) break;
879 plfsz->hash = hash;
880 return;
883 /***********************************************************************
884 * X11DRV_XRender_Finalize
886 void X11DRV_XRender_Finalize(void)
888 int i;
890 EnterCriticalSection(&xrender_cs);
891 for(i = mru; i >= 0; i = glyphsetCache[i].next)
892 FreeEntry(i);
893 LeaveCriticalSection(&xrender_cs);
897 /***********************************************************************
898 * X11DRV_XRender_SelectFont
900 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
902 LFANDSIZE lfsz;
903 struct xrender_info *info;
905 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
906 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
907 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
908 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
909 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
910 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
911 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
912 GetWorldTransform( physDev->hdc, &lfsz.xform );
913 lfsz_calc_hash(&lfsz);
915 info = get_xrender_info(physDev);
916 if (!info) return 0;
918 EnterCriticalSection(&xrender_cs);
919 if(info->cache_index != -1)
920 dec_ref_cache(info->cache_index);
921 info->cache_index = GetCacheEntry(physDev, &lfsz);
922 LeaveCriticalSection(&xrender_cs);
923 return 0;
926 /***********************************************************************
927 * X11DRV_XRender_SetDeviceClipping
929 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
931 if (physDev->xrender->pict)
933 wine_tsx11_lock();
934 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
935 physDev->dc_rect.left, physDev->dc_rect.top,
936 (XRectangle *)data->Buffer, data->rdh.nCount );
937 wine_tsx11_unlock();
941 /***********************************************************************
942 * X11DRV_XRender_DeleteDC
944 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
946 X11DRV_XRender_UpdateDrawable(physDev);
948 EnterCriticalSection(&xrender_cs);
949 if(physDev->xrender->cache_index != -1)
950 dec_ref_cache(physDev->xrender->cache_index);
951 LeaveCriticalSection(&xrender_cs);
953 HeapFree(GetProcessHeap(), 0, physDev->xrender);
954 physDev->xrender = NULL;
955 return;
958 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
960 const WineXRenderFormat *fmt;
961 ColorShifts shifts;
963 /* When XRender is not around we can only use the screen_depth and when needed we perform depth conversion
964 * in software. Further we also return the screen depth for paletted formats or TrueColor formats with a low
965 * number of bits because XRender can't handle paletted formats and 8-bit TrueColor does not exist for XRender. */
966 if (!X11DRV_XRender_Installed || bits_pixel <= 8)
967 return FALSE;
969 if (dib)
971 X11DRV_PALETTE_ComputeColorShifts(&shifts, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
972 fmt = get_xrender_format_from_color_shifts(dib->dsBm.bmBitsPixel, &shifts);
974 /* Common formats should be in our picture format table. */
975 if (!fmt)
977 TRACE("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n",
978 dib->dsBm.bmBitsPixel, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
979 return FALSE;
982 else
984 int red_mask, green_mask, blue_mask;
986 /* We are dealing with a DDB */
987 switch (bits_pixel)
989 case 16:
990 fmt = get_xrender_format(WXR_FORMAT_R5G6B5);
991 break;
992 case 24:
993 fmt = get_xrender_format(WXR_FORMAT_R8G8B8);
994 break;
995 case 32:
996 fmt = get_xrender_format(WXR_FORMAT_A8R8G8B8);
997 break;
998 default:
999 fmt = NULL;
1002 if (!fmt)
1004 TRACE("Unhandled DDB bits_pixel=%d\n", bits_pixel);
1005 return FALSE;
1008 red_mask = fmt->pict_format->direct.redMask << fmt->pict_format->direct.red;
1009 green_mask = fmt->pict_format->direct.greenMask << fmt->pict_format->direct.green;
1010 blue_mask = fmt->pict_format->direct.blueMask << fmt->pict_format->direct.blue;
1011 X11DRV_PALETTE_ComputeColorShifts(&shifts, red_mask, green_mask, blue_mask);
1014 physBitmap->pixmap_depth = fmt->pict_format->depth;
1015 physBitmap->trueColor = TRUE;
1016 physBitmap->pixmap_color_shifts = shifts;
1017 return TRUE;
1020 /***********************************************************************
1021 * X11DRV_XRender_UpdateDrawable
1023 * Deletes the pict and tile when the drawable changes.
1025 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
1027 struct xrender_info *info = physDev->xrender;
1029 if (info->pict || info->pict_src)
1031 wine_tsx11_lock();
1032 XFlush( gdi_display );
1033 if (info->pict)
1035 TRACE("freeing pict = %lx dc = %p\n", info->pict, physDev->hdc);
1036 pXRenderFreePicture(gdi_display, info->pict);
1037 info->pict = 0;
1039 if(info->pict_src)
1041 TRACE("freeing pict = %lx dc = %p\n", info->pict_src, physDev->hdc);
1042 pXRenderFreePicture(gdi_display, info->pict_src);
1043 info->pict_src = 0;
1045 wine_tsx11_unlock();
1048 info->format = NULL;
1051 /************************************************************************
1052 * UploadGlyph
1054 * Helper to ExtTextOut. Must be called inside xrender_cs
1056 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
1058 unsigned int buflen;
1059 char *buf;
1060 Glyph gid;
1061 GLYPHMETRICS gm;
1062 XGlyphInfo gi;
1063 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
1064 gsCacheEntryFormat *formatEntry;
1065 UINT ggo_format = GGO_GLYPH_INDEX;
1066 WXRFormat wxr_format;
1067 static const char zero[4];
1068 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1070 switch(format) {
1071 case AA_Grey:
1072 ggo_format |= WINE_GGO_GRAY16_BITMAP;
1073 break;
1074 case AA_RGB:
1075 ggo_format |= WINE_GGO_HRGB_BITMAP;
1076 break;
1077 case AA_BGR:
1078 ggo_format |= WINE_GGO_HBGR_BITMAP;
1079 break;
1080 case AA_VRGB:
1081 ggo_format |= WINE_GGO_VRGB_BITMAP;
1082 break;
1083 case AA_VBGR:
1084 ggo_format |= WINE_GGO_VBGR_BITMAP;
1085 break;
1087 default:
1088 ERR("aa = %d - not implemented\n", format);
1089 case AA_None:
1090 ggo_format |= GGO_BITMAP;
1091 break;
1094 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1095 if(buflen == GDI_ERROR) {
1096 if(format != AA_None) {
1097 format = AA_None;
1098 entry->aa_default = AA_None;
1099 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
1100 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1102 if(buflen == GDI_ERROR) {
1103 WARN("GetGlyphOutlineW failed\n");
1104 return FALSE;
1106 TRACE("Turning off antialiasing for this monochrome font\n");
1109 /* If there is nothing for the current type, we create the entry. */
1110 if( !entry->format[format] ) {
1111 entry->format[format] = HeapAlloc(GetProcessHeap(),
1112 HEAP_ZERO_MEMORY,
1113 sizeof(gsCacheEntryFormat));
1115 formatEntry = entry->format[format];
1117 if(formatEntry->nrealized <= glyph) {
1118 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1120 if (formatEntry->realized)
1121 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1122 HEAP_ZERO_MEMORY,
1123 formatEntry->realized,
1124 formatEntry->nrealized * sizeof(BOOL));
1125 else
1126 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1127 HEAP_ZERO_MEMORY,
1128 formatEntry->nrealized * sizeof(BOOL));
1130 if(!X11DRV_XRender_Installed) {
1131 if (formatEntry->bitmaps)
1132 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
1133 HEAP_ZERO_MEMORY,
1134 formatEntry->bitmaps,
1135 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1136 else
1137 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
1138 HEAP_ZERO_MEMORY,
1139 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1141 if (formatEntry->gis)
1142 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1143 HEAP_ZERO_MEMORY,
1144 formatEntry->gis,
1145 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1146 else
1147 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1148 HEAP_ZERO_MEMORY,
1149 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1153 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
1154 switch(format) {
1155 case AA_Grey:
1156 wxr_format = WXR_FORMAT_GRAY;
1157 break;
1159 case AA_RGB:
1160 case AA_BGR:
1161 case AA_VRGB:
1162 case AA_VBGR:
1163 wxr_format = WXR_FORMAT_A8R8G8B8;
1164 break;
1166 default:
1167 ERR("aa = %d - not implemented\n", format);
1168 case AA_None:
1169 wxr_format = WXR_FORMAT_MONO;
1170 break;
1173 wine_tsx11_lock();
1174 formatEntry->font_format = get_xrender_format(wxr_format);
1175 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
1176 wine_tsx11_unlock();
1180 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1181 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1182 formatEntry->realized[glyph] = TRUE;
1184 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1185 buflen,
1186 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1187 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1189 gi.width = gm.gmBlackBoxX;
1190 gi.height = gm.gmBlackBoxY;
1191 gi.x = -gm.gmptGlyphOrigin.x;
1192 gi.y = gm.gmptGlyphOrigin.y;
1193 gi.xOff = gm.gmCellIncX;
1194 gi.yOff = gm.gmCellIncY;
1196 if(TRACE_ON(xrender)) {
1197 int pitch, i, j;
1198 char output[300];
1199 unsigned char *line;
1201 if(format == AA_None) {
1202 pitch = ((gi.width + 31) / 32) * 4;
1203 for(i = 0; i < gi.height; i++) {
1204 line = (unsigned char*) buf + i * pitch;
1205 output[0] = '\0';
1206 for(j = 0; j < pitch * 8; j++) {
1207 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1209 TRACE("%s\n", output);
1211 } else {
1212 static const char blks[] = " .:;!o*#";
1213 char str[2];
1215 str[1] = '\0';
1216 pitch = ((gi.width + 3) / 4) * 4;
1217 for(i = 0; i < gi.height; i++) {
1218 line = (unsigned char*) buf + i * pitch;
1219 output[0] = '\0';
1220 for(j = 0; j < pitch; j++) {
1221 str[0] = blks[line[j] >> 5];
1222 strcat(output, str);
1224 TRACE("%s\n", output);
1230 if(formatEntry->glyphset) {
1231 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1232 unsigned char *byte = (unsigned char*) buf, c;
1233 int i = buflen;
1235 while(i--) {
1236 c = *byte;
1238 /* magic to flip bit order */
1239 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1240 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1241 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1243 *byte++ = c;
1246 else if ( format != AA_Grey &&
1247 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1249 unsigned int i, *data = (unsigned int *)buf;
1250 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1252 gid = glyph;
1255 XRenderCompositeText seems to ignore 0x0 glyphs when
1256 AA_None, which means we lose the advance width of glyphs
1257 like the space. We'll pretend that such glyphs are 1x1
1258 bitmaps.
1261 if(buflen == 0)
1262 gi.width = gi.height = 1;
1264 wine_tsx11_lock();
1265 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1266 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1267 wine_tsx11_unlock();
1268 HeapFree(GetProcessHeap(), 0, buf);
1269 } else {
1270 formatEntry->bitmaps[glyph] = buf;
1273 formatEntry->gis[glyph] = gi;
1275 return TRUE;
1278 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1279 void *bitmap, XGlyphInfo *gi)
1281 unsigned char *srcLine = bitmap, *src;
1282 unsigned char bits, bitsMask;
1283 int width = gi->width;
1284 int stride = ((width + 31) & ~31) >> 3;
1285 int height = gi->height;
1286 int w;
1287 int xspan, lenspan;
1289 TRACE("%d, %d\n", x, y);
1290 x -= gi->x;
1291 y -= gi->y;
1292 while (height--)
1294 src = srcLine;
1295 srcLine += stride;
1296 w = width;
1298 bitsMask = 0x80; /* FreeType is always MSB first */
1299 bits = *src++;
1301 xspan = x;
1302 while (w)
1304 if (bits & bitsMask)
1306 lenspan = 0;
1309 lenspan++;
1310 if (lenspan == w)
1311 break;
1312 bitsMask = bitsMask >> 1;
1313 if (!bitsMask)
1315 bits = *src++;
1316 bitsMask = 0x80;
1318 } while (bits & bitsMask);
1319 XFillRectangle (gdi_display, physDev->drawable,
1320 physDev->gc, xspan, y, lenspan, 1);
1321 xspan += lenspan;
1322 w -= lenspan;
1324 else
1328 w--;
1329 xspan++;
1330 if (!w)
1331 break;
1332 bitsMask = bitsMask >> 1;
1333 if (!bitsMask)
1335 bits = *src++;
1336 bitsMask = 0x80;
1338 } while (!(bits & bitsMask));
1341 y++;
1345 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1346 void *bitmap, XGlyphInfo *gi)
1348 unsigned char *srcLine = bitmap, *src, bits;
1349 int width = gi->width;
1350 int stride = ((width + 3) & ~3);
1351 int height = gi->height;
1352 int w;
1353 int xspan, lenspan;
1355 x -= gi->x;
1356 y -= gi->y;
1357 while (height--)
1359 src = srcLine;
1360 srcLine += stride;
1361 w = width;
1363 bits = *src++;
1364 xspan = x;
1365 while (w)
1367 if (bits >= 0x80)
1369 lenspan = 0;
1372 lenspan++;
1373 if (lenspan == w)
1374 break;
1375 bits = *src++;
1376 } while (bits >= 0x80);
1377 XFillRectangle (gdi_display, physDev->drawable,
1378 physDev->gc, xspan, y, lenspan, 1);
1379 xspan += lenspan;
1380 w -= lenspan;
1382 else
1386 w--;
1387 xspan++;
1388 if (!w)
1389 break;
1390 bits = *src++;
1391 } while (bits < 0x80);
1394 y++;
1399 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1401 int s, l;
1403 s = 0;
1404 while ((mask & 1) == 0)
1406 mask >>= 1;
1407 s++;
1409 l = 0;
1410 while ((mask & 1) == 1)
1412 mask >>= 1;
1413 l++;
1415 *shift = s;
1416 *len = l;
1419 static DWORD GetField (DWORD pixel, int shift, int len)
1421 pixel = pixel & (((1 << (len)) - 1) << shift);
1422 pixel = pixel << (32 - (shift + len)) >> 24;
1423 while (len < 8)
1425 pixel |= (pixel >> len);
1426 len <<= 1;
1428 return pixel;
1432 static DWORD PutField (DWORD pixel, int shift, int len)
1434 shift = shift - (8 - len);
1435 if (len <= 8)
1436 pixel &= (((1 << len) - 1) << (8 - len));
1437 if (shift < 0)
1438 pixel >>= -shift;
1439 else
1440 pixel <<= shift;
1441 return pixel;
1444 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1445 int color)
1447 int r_shift, r_len;
1448 int g_shift, g_len;
1449 int b_shift, b_len;
1450 BYTE *maskLine, *mask, m;
1451 int maskStride;
1452 DWORD pixel;
1453 int width, height;
1454 int w, tx;
1455 BYTE src_r, src_g, src_b;
1457 x -= gi->x;
1458 y -= gi->y;
1459 width = gi->width;
1460 height = gi->height;
1462 maskLine = bitmap;
1463 maskStride = (width + 3) & ~3;
1465 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1466 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1467 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1469 src_r = GetField(color, r_shift, r_len);
1470 src_g = GetField(color, g_shift, g_len);
1471 src_b = GetField(color, b_shift, b_len);
1473 for(; height--; y++)
1475 mask = maskLine;
1476 maskLine += maskStride;
1477 w = width;
1478 tx = x;
1480 if(y < 0) continue;
1481 if(y >= image->height) break;
1483 for(; w--; tx++)
1485 if(tx >= image->width) break;
1487 m = *mask++;
1488 if(tx < 0) continue;
1490 if (m == 0xff)
1491 XPutPixel (image, tx, y, color);
1492 else if (m)
1494 BYTE r, g, b;
1496 pixel = XGetPixel (image, tx, y);
1498 r = GetField(pixel, r_shift, r_len);
1499 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1500 g = GetField(pixel, g_shift, g_len);
1501 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1502 b = GetField(pixel, b_shift, b_len);
1503 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1505 pixel = (PutField (r, r_shift, r_len) |
1506 PutField (g, g_shift, g_len) |
1507 PutField (b, b_shift, b_len));
1508 XPutPixel (image, tx, y, pixel);
1514 /*************************************************************
1515 * get_tile_pict
1517 * Returns an appropriate Picture for tiling the text colour.
1518 * Call and use result within the xrender_cs
1520 static Picture get_tile_pict(const WineXRenderFormat *wxr_format, int text_pixel)
1522 static struct
1524 Pixmap xpm;
1525 Picture pict;
1526 int current_color;
1527 } tiles[WXR_NB_FORMATS], *tile;
1528 XRenderColor col;
1530 tile = &tiles[wxr_format->format];
1532 if(!tile->xpm)
1534 XRenderPictureAttributes pa;
1536 wine_tsx11_lock();
1537 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1539 pa.repeat = RepeatNormal;
1540 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1541 wine_tsx11_unlock();
1543 /* init current_color to something different from text_pixel */
1544 tile->current_color = ~text_pixel;
1546 if(wxr_format->format == WXR_FORMAT_MONO)
1548 /* for a 1bpp bitmap we always need a 1 in the tile */
1549 col.red = col.green = col.blue = 0;
1550 col.alpha = 0xffff;
1551 wine_tsx11_lock();
1552 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1553 wine_tsx11_unlock();
1557 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1559 get_xrender_color(wxr_format, text_pixel, &col);
1560 wine_tsx11_lock();
1561 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1562 wine_tsx11_unlock();
1563 tile->current_color = text_pixel;
1565 return tile->pict;
1568 /*************************************************************
1569 * get_mask_pict
1571 * Returns an appropriate Picture for masking with the specified alpha.
1572 * Call and use result within the xrender_cs
1574 static Picture get_mask_pict( int alpha )
1576 static Pixmap pixmap;
1577 static Picture pict;
1578 static int current_alpha;
1580 if (alpha == 0xffff) return 0; /* don't need a mask for alpha==1.0 */
1582 if (!pixmap)
1584 const WineXRenderFormat *fmt = get_xrender_format( WXR_FORMAT_A8R8G8B8 );
1585 XRenderPictureAttributes pa;
1587 wine_tsx11_lock();
1588 pixmap = XCreatePixmap( gdi_display, root_window, 1, 1, 32 );
1589 pa.repeat = RepeatNormal;
1590 pict = pXRenderCreatePicture( gdi_display, pixmap, fmt->pict_format, CPRepeat, &pa );
1591 wine_tsx11_unlock();
1592 current_alpha = -1;
1595 if (alpha != current_alpha)
1597 XRenderColor col;
1598 col.red = col.green = col.blue = 0;
1599 col.alpha = current_alpha = alpha;
1600 wine_tsx11_lock();
1601 pXRenderFillRectangle( gdi_display, PictOpSrc, pict, &col, 0, 0, 1, 1 );
1602 wine_tsx11_unlock();
1604 return pict;
1607 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1609 return 1;
1612 /***********************************************************************
1613 * X11DRV_XRender_ExtTextOut
1615 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1616 const RECT *lprect, LPCWSTR wstr, UINT count,
1617 const INT *lpDx )
1619 XGCValues xgcval;
1620 gsCacheEntry *entry;
1621 gsCacheEntryFormat *formatEntry;
1622 BOOL retv = FALSE;
1623 int textPixel, backgroundPixel;
1624 HRGN saved_region = 0;
1625 BOOL disable_antialias = FALSE;
1626 AA_Type aa_type = AA_None;
1627 DIBSECTION bmp;
1628 unsigned int idx;
1629 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1630 Picture tile_pict = 0;
1632 /* Do we need to disable antialiasing because of palette mode? */
1633 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1634 TRACE("bitmap is not a DIB\n");
1636 else if (bmp.dsBmih.biBitCount <= 8) {
1637 TRACE("Disabling antialiasing\n");
1638 disable_antialias = TRUE;
1641 xgcval.function = GXcopy;
1642 xgcval.background = physDev->backgroundPixel;
1643 xgcval.fill_style = FillSolid;
1644 wine_tsx11_lock();
1645 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1646 wine_tsx11_unlock();
1648 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1650 if(physDev->depth == 1) {
1651 if((physDev->textPixel & 0xffffff) == 0) {
1652 textPixel = 0;
1653 backgroundPixel = 1;
1654 } else {
1655 textPixel = 1;
1656 backgroundPixel = 0;
1658 } else {
1659 textPixel = physDev->textPixel;
1660 backgroundPixel = physDev->backgroundPixel;
1663 if(flags & ETO_OPAQUE)
1665 wine_tsx11_lock();
1666 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1667 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1668 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1669 lprect->right - lprect->left, lprect->bottom - lprect->top );
1670 wine_tsx11_unlock();
1673 if(count == 0)
1675 retv = TRUE;
1676 goto done_unlock;
1679 if (flags & ETO_CLIPPED)
1681 HRGN clip_region;
1683 clip_region = CreateRectRgnIndirect( lprect );
1684 /* make a copy of the current device region */
1685 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1686 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1687 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1688 DeleteObject( clip_region );
1691 EnterCriticalSection(&xrender_cs);
1693 entry = glyphsetCache + physDev->xrender->cache_index;
1694 if( disable_antialias == FALSE )
1695 aa_type = entry->aa_default;
1696 formatEntry = entry->format[aa_type];
1698 for(idx = 0; idx < count; idx++) {
1699 if( !formatEntry ) {
1700 UploadGlyph(physDev, wstr[idx], aa_type);
1701 /* re-evaluate antialias since aa_default may have changed */
1702 if( disable_antialias == FALSE )
1703 aa_type = entry->aa_default;
1704 formatEntry = entry->format[aa_type];
1705 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1706 UploadGlyph(physDev, wstr[idx], aa_type);
1709 if (!formatEntry)
1711 WARN("could not upload requested glyphs\n");
1712 LeaveCriticalSection(&xrender_cs);
1713 goto done_unlock;
1716 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1717 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1719 if(X11DRV_XRender_Installed)
1721 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1722 POINT offset = {0, 0};
1723 POINT desired, current;
1724 int render_op = PictOpOver;
1725 Picture pict = get_xrender_picture(physDev);
1727 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1728 So we pass zeros to the function and move to our starting position using the first
1729 element of the elts array. */
1731 desired.x = physDev->dc_rect.left + x;
1732 desired.y = physDev->dc_rect.top + y;
1733 current.x = current.y = 0;
1735 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1737 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1739 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1740 render_op = PictOpOutReverse; /* This gives us 'black' text */
1742 for(idx = 0; idx < count; idx++)
1744 elts[idx].glyphset = formatEntry->glyphset;
1745 elts[idx].chars = wstr + idx;
1746 elts[idx].nchars = 1;
1747 elts[idx].xOff = desired.x - current.x;
1748 elts[idx].yOff = desired.y - current.y;
1750 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1751 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1753 if(!lpDx)
1755 desired.x += formatEntry->gis[wstr[idx]].xOff;
1756 desired.y += formatEntry->gis[wstr[idx]].yOff;
1758 else
1760 if(flags & ETO_PDY)
1762 offset.x += lpDx[idx * 2];
1763 offset.y += lpDx[idx * 2 + 1];
1765 else
1766 offset.x += lpDx[idx];
1767 desired.x = physDev->dc_rect.left + x + offset.x;
1768 desired.y = physDev->dc_rect.top + y + offset.y;
1771 wine_tsx11_lock();
1772 /* Make sure we don't have any transforms set from a previous call */
1773 set_xrender_transformation(pict, 1, 1, 0, 0);
1774 pXRenderCompositeText16(gdi_display, render_op,
1775 tile_pict,
1776 pict,
1777 formatEntry->font_format->pict_format,
1778 0, 0, 0, 0, elts, count);
1779 wine_tsx11_unlock();
1780 HeapFree(GetProcessHeap(), 0, elts);
1781 } else {
1782 POINT offset = {0, 0};
1783 wine_tsx11_lock();
1784 XSetForeground( gdi_display, physDev->gc, textPixel );
1786 if(aa_type == AA_None || physDev->depth == 1)
1788 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1790 if(aa_type == AA_None)
1791 sharp_glyph_fn = SharpGlyphMono;
1792 else
1793 sharp_glyph_fn = SharpGlyphGray;
1795 for(idx = 0; idx < count; idx++) {
1796 sharp_glyph_fn(physDev,
1797 physDev->dc_rect.left + x + offset.x,
1798 physDev->dc_rect.top + y + offset.y,
1799 formatEntry->bitmaps[wstr[idx]],
1800 &formatEntry->gis[wstr[idx]]);
1801 if(lpDx)
1803 if(flags & ETO_PDY)
1805 offset.x += lpDx[idx * 2];
1806 offset.y += lpDx[idx * 2 + 1];
1808 else
1809 offset.x += lpDx[idx];
1811 else
1813 offset.x += formatEntry->gis[wstr[idx]].xOff;
1814 offset.y += formatEntry->gis[wstr[idx]].yOff;
1817 } else {
1818 XImage *image;
1819 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1820 RECT extents = {0, 0, 0, 0};
1821 POINT cur = {0, 0};
1822 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1823 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1825 TRACE("drawable %dx%d\n", w, h);
1827 for(idx = 0; idx < count; idx++) {
1828 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1829 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1830 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1831 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1832 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1833 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1834 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1835 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1837 if(lpDx)
1839 if(flags & ETO_PDY)
1841 cur.x += lpDx[idx * 2];
1842 cur.y += lpDx[idx * 2 + 1];
1844 else
1845 cur.x += lpDx[idx];
1847 else
1849 cur.x += formatEntry->gis[wstr[idx]].xOff;
1850 cur.y += formatEntry->gis[wstr[idx]].yOff;
1853 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1854 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1856 if(physDev->dc_rect.left + x + extents.left >= 0) {
1857 image_x = physDev->dc_rect.left + x + extents.left;
1858 image_off_x = 0;
1859 } else {
1860 image_x = 0;
1861 image_off_x = physDev->dc_rect.left + x + extents.left;
1863 if(physDev->dc_rect.top + y + extents.top >= 0) {
1864 image_y = physDev->dc_rect.top + y + extents.top;
1865 image_off_y = 0;
1866 } else {
1867 image_y = 0;
1868 image_off_y = physDev->dc_rect.top + y + extents.top;
1870 if(physDev->dc_rect.left + x + extents.right < w)
1871 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1872 else
1873 image_w = w - image_x;
1874 if(physDev->dc_rect.top + y + extents.bottom < h)
1875 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1876 else
1877 image_h = h - image_y;
1879 if(image_w <= 0 || image_h <= 0) goto no_image;
1881 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1882 image = XGetImage(gdi_display, physDev->drawable,
1883 image_x, image_y, image_w, image_h,
1884 AllPlanes, ZPixmap);
1885 X11DRV_check_error();
1887 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1888 gdi_display, (int)physDev->drawable, image_x, image_y,
1889 image_w, image_h, AllPlanes, ZPixmap,
1890 physDev->depth, image);
1891 if(!image) {
1892 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1893 physDev->depth);
1894 GC gc;
1895 XGCValues gcv;
1897 gcv.graphics_exposures = False;
1898 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1899 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1900 image_w, image_h, 0, 0);
1901 XFreeGC(gdi_display, gc);
1902 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1903 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1904 ZPixmap);
1905 X11DRV_check_error();
1906 XFreePixmap(gdi_display, xpm);
1908 if(!image) goto no_image;
1910 image->red_mask = visual->red_mask;
1911 image->green_mask = visual->green_mask;
1912 image->blue_mask = visual->blue_mask;
1914 for(idx = 0; idx < count; idx++) {
1915 SmoothGlyphGray(image,
1916 offset.x + image_off_x - extents.left,
1917 offset.y + image_off_y - extents.top,
1918 formatEntry->bitmaps[wstr[idx]],
1919 &formatEntry->gis[wstr[idx]],
1920 physDev->textPixel);
1921 if(lpDx)
1923 if(flags & ETO_PDY)
1925 offset.x += lpDx[idx * 2];
1926 offset.y += lpDx[idx * 2 + 1];
1928 else
1929 offset.x += lpDx[idx];
1931 else
1933 offset.x += formatEntry->gis[wstr[idx]].xOff;
1934 offset.y += formatEntry->gis[wstr[idx]].yOff;
1937 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1938 image_x, image_y, image_w, image_h);
1939 XDestroyImage(image);
1941 no_image:
1942 wine_tsx11_unlock();
1944 LeaveCriticalSection(&xrender_cs);
1946 if (flags & ETO_CLIPPED)
1948 /* restore the device region */
1949 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1950 DeleteObject( saved_region );
1953 retv = TRUE;
1955 done_unlock:
1956 X11DRV_UnlockDIBSection( physDev, TRUE );
1957 return retv;
1960 /* Helper function for (stretched) blitting using xrender */
1961 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1962 int x_src, int y_src, int x_dst, int y_dst,
1963 double xscale, double yscale, int width, int height )
1965 int x_offset, y_offset;
1967 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1968 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1969 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1970 if(xscale != 1.0 || yscale != 1.0)
1972 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1973 * in the wrong quadrant of the x-y plane.
1975 x_offset = (xscale < 0) ? -width : 0;
1976 y_offset = (yscale < 0) ? -height : 0;
1977 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1979 else
1981 x_offset = x_src;
1982 y_offset = y_src;
1983 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1985 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1986 x_offset, y_offset, 0, 0, x_dst, y_dst, width, height );
1989 /* Helper function for (stretched) mono->color blitting using xrender */
1990 static void xrender_mono_blit( Picture src_pict, Picture mask_pict, Picture dst_pict,
1991 int x_src, int y_src, double xscale, double yscale, int width, int height )
1993 int x_offset, y_offset;
1995 /* When doing a mono->color blit, 'src_pict' contains a 1x1 picture for tiling, the actual
1996 * source data is in mask_pict. The 'src_pict' data effectively acts as an alpha channel to the
1997 * tile data. We need PictOpOver for correct rendering.
1998 * Note since the 'source data' is in the mask picture, we have to pass x_src / y_src using
1999 * mask_x / mask_y
2001 if (xscale != 1.0 || yscale != 1.0)
2003 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
2004 * in the wrong quadrant of the x-y plane.
2006 x_offset = (xscale < 0) ? -width : 0;
2007 y_offset = (yscale < 0) ? -height : 0;
2008 set_xrender_transformation(mask_pict, xscale, yscale, x_src, y_src);
2010 else
2012 x_offset = x_src;
2013 y_offset = y_src;
2014 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
2016 pXRenderComposite(gdi_display, PictOpOver, src_pict, mask_pict, dst_pict,
2017 0, 0, x_offset, y_offset, 0, 0, width, height);
2020 /******************************************************************************
2021 * AlphaBlend
2023 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2024 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2026 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
2027 struct xrender_info *src_info = get_xrender_info( devSrc );
2028 double xscale, yscale;
2029 BOOL use_repeat;
2031 if(!X11DRV_XRender_Installed) {
2032 FIXME("Unable to AlphaBlend without Xrender\n");
2033 return FALSE;
2036 if (devSrc != devDst) X11DRV_LockDIBSection( devSrc, DIB_Status_GdiMod );
2037 X11DRV_LockDIBSection( devDst, DIB_Status_GdiMod );
2039 dst_pict = get_xrender_picture( devDst );
2041 use_repeat = use_source_repeat( devSrc );
2042 if (!use_repeat)
2044 xscale = src->width / (double)dst->width;
2045 yscale = src->height / (double)dst->height;
2047 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2049 if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && src_info->format)
2051 /* we need a source picture with no alpha */
2052 WXRFormat format = get_format_without_alpha( src_info->format->format );
2053 if (format != src_info->format->format)
2055 XRenderPictureAttributes pa;
2056 const WineXRenderFormat *fmt = get_xrender_format( format );
2058 wine_tsx11_lock();
2059 pa.subwindow_mode = IncludeInferiors;
2060 pa.repeat = use_repeat ? RepeatNormal : RepeatNone;
2061 tmp_pict = pXRenderCreatePicture( gdi_display, devSrc->drawable, fmt->pict_format,
2062 CPSubwindowMode|CPRepeat, &pa );
2063 wine_tsx11_unlock();
2064 src_pict = tmp_pict;
2068 if (!src_pict) src_pict = get_xrender_picture_source( devSrc, use_repeat );
2070 EnterCriticalSection( &xrender_cs );
2071 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2073 wine_tsx11_lock();
2074 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2075 devSrc->dc_rect.left + src->visrect.left, devSrc->dc_rect.top + src->visrect.top,
2076 devDst->dc_rect.left + dst->visrect.left, devDst->dc_rect.top + dst->visrect.top,
2077 xscale, yscale,
2078 dst->visrect.right - dst->visrect.left, dst->visrect.bottom - dst->visrect.top );
2079 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2080 wine_tsx11_unlock();
2082 LeaveCriticalSection( &xrender_cs );
2083 if (devSrc != devDst) X11DRV_UnlockDIBSection( devSrc, FALSE );
2084 X11DRV_UnlockDIBSection( devDst, TRUE );
2085 return TRUE;
2089 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2091 /* 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 */
2092 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2093 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2094 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2096 wine_tsx11_lock();
2097 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2099 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2100 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2101 (src_format->format == dst_format->format) )
2103 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2104 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2106 else /* We need depth conversion */
2108 Picture src_pict, dst_pict;
2109 XRenderPictureAttributes pa;
2110 pa.subwindow_mode = IncludeInferiors;
2111 pa.repeat = RepeatNone;
2113 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2114 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2116 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, 0, 0, 0, 0, 1.0, 1.0, width, height);
2117 pXRenderFreePicture(gdi_display, src_pict);
2118 pXRenderFreePicture(gdi_display, dst_pict);
2120 wine_tsx11_unlock();
2123 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2124 Pixmap pixmap, GC gc,
2125 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2127 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
2128 int width = dst->visrect.right - dst->visrect.left;
2129 int height = dst->visrect.bottom - dst->visrect.top;
2130 int x_src = physDevSrc->dc_rect.left + src->visrect.left;
2131 int y_src = physDevSrc->dc_rect.top + src->visrect.top;
2132 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2133 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2134 Picture src_pict=0, dst_pict=0, mask_pict=0;
2135 BOOL use_repeat;
2136 double xscale, yscale;
2138 XRenderPictureAttributes pa;
2139 pa.subwindow_mode = IncludeInferiors;
2140 pa.repeat = RepeatNone;
2142 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n",
2143 physDevSrc->depth, src->width, src->height, x_src, y_src);
2144 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, dst->width, dst->height);
2146 if(!X11DRV_XRender_Installed)
2148 TRACE("Not using XRender since it is not available or disabled\n");
2149 return FALSE;
2152 /* XRender can't handle palettes, so abort */
2153 if(X11DRV_PALETTE_XPixelToPalette)
2154 return FALSE;
2156 /* XRender is of no use in this case */
2157 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2158 return FALSE;
2160 /* Just use traditional X copy when the formats match and we don't need stretching */
2161 if((src_info->format->format == dst_format->format) && !stretch)
2163 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2164 wine_tsx11_lock();
2165 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2166 wine_tsx11_unlock();
2167 return TRUE;
2170 use_repeat = use_source_repeat( physDevSrc );
2171 if (!use_repeat)
2173 xscale = src->width / (double)dst->width;
2174 yscale = src->height / (double)dst->height;
2176 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2178 /* mono -> color */
2179 if(physDevSrc->depth == 1 && physDevDst->depth > 1)
2181 XRenderColor col;
2182 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2184 /* We use the source drawable as a mask */
2185 mask_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2187 /* Use backgroundPixel as the foreground color */
2188 EnterCriticalSection( &xrender_cs );
2189 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2191 /* Create a destination picture and fill it with textPixel color as the background color */
2192 wine_tsx11_lock();
2193 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2194 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2196 xrender_mono_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2198 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2199 wine_tsx11_unlock();
2200 LeaveCriticalSection( &xrender_cs );
2202 else /* color -> color (can be at different depths) or mono -> mono */
2204 src_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2206 wine_tsx11_lock();
2207 dst_pict = pXRenderCreatePicture(gdi_display,
2208 pixmap, dst_format->pict_format,
2209 CPSubwindowMode|CPRepeat, &pa);
2211 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, x_src, y_src, 0, 0, xscale, yscale, width, height);
2213 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2214 wine_tsx11_unlock();
2216 return TRUE;
2219 #else /* SONAME_LIBXRENDER */
2221 void X11DRV_XRender_Init(void)
2223 TRACE("XRender support not compiled in.\n");
2224 return;
2227 void X11DRV_XRender_Finalize(void)
2231 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2233 assert(0);
2234 return FALSE;
2237 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2239 assert(0);
2240 return;
2243 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2245 assert(0);
2246 return;
2249 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2250 const RECT *lprect, LPCWSTR wstr, UINT count,
2251 const INT *lpDx )
2253 assert(0);
2254 return FALSE;
2257 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2259 assert(0);
2260 return;
2263 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2264 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2266 FIXME("not supported - XRENDER headers were missing at compile time\n");
2267 return FALSE;
2270 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2272 wine_tsx11_lock();
2273 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2275 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2276 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2277 wine_tsx11_unlock();
2280 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
2282 return FALSE;
2285 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2286 Pixmap pixmap, GC gc,
2287 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2289 return FALSE;
2291 #endif /* SONAME_LIBXRENDER */