comdlg32: Fix some alignment issues in the Dutch translation.
[wine/hramrach.git] / dlls / winex11.drv / xrender.c
blob0d4ef60de926f275548da450a3f5276b4304ec2f
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 double cosEsc, sinEsc;
1630 LOGFONTW lf;
1631 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1632 Picture tile_pict = 0;
1634 /* Do we need to disable antialiasing because of palette mode? */
1635 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1636 TRACE("bitmap is not a DIB\n");
1638 else if (bmp.dsBmih.biBitCount <= 8) {
1639 TRACE("Disabling antialiasing\n");
1640 disable_antialias = TRUE;
1643 xgcval.function = GXcopy;
1644 xgcval.background = physDev->backgroundPixel;
1645 xgcval.fill_style = FillSolid;
1646 wine_tsx11_lock();
1647 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1648 wine_tsx11_unlock();
1650 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1652 if(physDev->depth == 1) {
1653 if((physDev->textPixel & 0xffffff) == 0) {
1654 textPixel = 0;
1655 backgroundPixel = 1;
1656 } else {
1657 textPixel = 1;
1658 backgroundPixel = 0;
1660 } else {
1661 textPixel = physDev->textPixel;
1662 backgroundPixel = physDev->backgroundPixel;
1665 if(flags & ETO_OPAQUE)
1667 wine_tsx11_lock();
1668 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1669 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1670 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1671 lprect->right - lprect->left, lprect->bottom - lprect->top );
1672 wine_tsx11_unlock();
1675 if(count == 0)
1677 retv = TRUE;
1678 goto done_unlock;
1682 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1683 if(lf.lfEscapement != 0) {
1684 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1685 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1686 } else {
1687 cosEsc = 1;
1688 sinEsc = 0;
1691 if (flags & ETO_CLIPPED)
1693 HRGN clip_region;
1695 clip_region = CreateRectRgnIndirect( lprect );
1696 /* make a copy of the current device region */
1697 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1698 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1699 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1700 DeleteObject( clip_region );
1703 EnterCriticalSection(&xrender_cs);
1705 entry = glyphsetCache + physDev->xrender->cache_index;
1706 if( disable_antialias == FALSE )
1707 aa_type = entry->aa_default;
1708 formatEntry = entry->format[aa_type];
1710 for(idx = 0; idx < count; idx++) {
1711 if( !formatEntry ) {
1712 UploadGlyph(physDev, wstr[idx], aa_type);
1713 /* re-evaluate antialias since aa_default may have changed */
1714 if( disable_antialias == FALSE )
1715 aa_type = entry->aa_default;
1716 formatEntry = entry->format[aa_type];
1717 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1718 UploadGlyph(physDev, wstr[idx], aa_type);
1721 if (!formatEntry)
1723 WARN("could not upload requested glyphs\n");
1724 LeaveCriticalSection(&xrender_cs);
1725 goto done_unlock;
1728 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1729 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1731 if(X11DRV_XRender_Installed)
1733 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1734 INT offset = 0;
1735 POINT desired, current;
1736 int render_op = PictOpOver;
1737 Picture pict = get_xrender_picture(physDev);
1739 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1740 So we pass zeros to the function and move to our starting position using the first
1741 element of the elts array. */
1743 desired.x = physDev->dc_rect.left + x;
1744 desired.y = physDev->dc_rect.top + y;
1745 current.x = current.y = 0;
1747 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1749 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1751 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1752 render_op = PictOpOutReverse; /* This gives us 'black' text */
1754 for(idx = 0; idx < count; idx++)
1756 elts[idx].glyphset = formatEntry->glyphset;
1757 elts[idx].chars = wstr + idx;
1758 elts[idx].nchars = 1;
1759 elts[idx].xOff = desired.x - current.x;
1760 elts[idx].yOff = desired.y - current.y;
1762 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1763 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1765 if(!lpDx)
1767 desired.x += formatEntry->gis[wstr[idx]].xOff;
1768 desired.y += formatEntry->gis[wstr[idx]].yOff;
1770 else
1772 offset += lpDx[idx];
1773 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1774 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1777 wine_tsx11_lock();
1778 /* Make sure we don't have any transforms set from a previous call */
1779 set_xrender_transformation(pict, 1, 1, 0, 0);
1780 pXRenderCompositeText16(gdi_display, render_op,
1781 tile_pict,
1782 pict,
1783 formatEntry->font_format->pict_format,
1784 0, 0, 0, 0, elts, count);
1785 wine_tsx11_unlock();
1786 HeapFree(GetProcessHeap(), 0, elts);
1787 } else {
1788 INT offset = 0, xoff = 0, yoff = 0;
1789 wine_tsx11_lock();
1790 XSetForeground( gdi_display, physDev->gc, textPixel );
1792 if(aa_type == AA_None || physDev->depth == 1)
1794 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1796 if(aa_type == AA_None)
1797 sharp_glyph_fn = SharpGlyphMono;
1798 else
1799 sharp_glyph_fn = SharpGlyphGray;
1801 for(idx = 0; idx < count; idx++) {
1802 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1803 physDev->dc_rect.top + y + yoff,
1804 formatEntry->bitmaps[wstr[idx]],
1805 &formatEntry->gis[wstr[idx]]);
1806 if(lpDx) {
1807 offset += lpDx[idx];
1808 xoff = offset * cosEsc;
1809 yoff = offset * -sinEsc;
1810 } else {
1811 xoff += formatEntry->gis[wstr[idx]].xOff;
1812 yoff += formatEntry->gis[wstr[idx]].yOff;
1815 } else {
1816 XImage *image;
1817 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1818 RECT extents = {0, 0, 0, 0};
1819 POINT cur = {0, 0};
1820 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1821 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1823 TRACE("drawable %dx%d\n", w, h);
1825 for(idx = 0; idx < count; idx++) {
1826 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1827 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1828 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1829 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1830 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1831 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1832 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1833 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1834 if(lpDx) {
1835 offset += lpDx[idx];
1836 cur.x = offset * cosEsc;
1837 cur.y = offset * -sinEsc;
1838 } else {
1839 cur.x += formatEntry->gis[wstr[idx]].xOff;
1840 cur.y += formatEntry->gis[wstr[idx]].yOff;
1843 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1844 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1846 if(physDev->dc_rect.left + x + extents.left >= 0) {
1847 image_x = physDev->dc_rect.left + x + extents.left;
1848 image_off_x = 0;
1849 } else {
1850 image_x = 0;
1851 image_off_x = physDev->dc_rect.left + x + extents.left;
1853 if(physDev->dc_rect.top + y + extents.top >= 0) {
1854 image_y = physDev->dc_rect.top + y + extents.top;
1855 image_off_y = 0;
1856 } else {
1857 image_y = 0;
1858 image_off_y = physDev->dc_rect.top + y + extents.top;
1860 if(physDev->dc_rect.left + x + extents.right < w)
1861 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1862 else
1863 image_w = w - image_x;
1864 if(physDev->dc_rect.top + y + extents.bottom < h)
1865 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1866 else
1867 image_h = h - image_y;
1869 if(image_w <= 0 || image_h <= 0) goto no_image;
1871 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1872 image = XGetImage(gdi_display, physDev->drawable,
1873 image_x, image_y, image_w, image_h,
1874 AllPlanes, ZPixmap);
1875 X11DRV_check_error();
1877 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1878 gdi_display, (int)physDev->drawable, image_x, image_y,
1879 image_w, image_h, AllPlanes, ZPixmap,
1880 physDev->depth, image);
1881 if(!image) {
1882 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1883 physDev->depth);
1884 GC gc;
1885 XGCValues gcv;
1887 gcv.graphics_exposures = False;
1888 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1889 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1890 image_w, image_h, 0, 0);
1891 XFreeGC(gdi_display, gc);
1892 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1893 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1894 ZPixmap);
1895 X11DRV_check_error();
1896 XFreePixmap(gdi_display, xpm);
1898 if(!image) goto no_image;
1900 image->red_mask = visual->red_mask;
1901 image->green_mask = visual->green_mask;
1902 image->blue_mask = visual->blue_mask;
1904 offset = xoff = yoff = 0;
1905 for(idx = 0; idx < count; idx++) {
1906 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1907 yoff + image_off_y - extents.top,
1908 formatEntry->bitmaps[wstr[idx]],
1909 &formatEntry->gis[wstr[idx]],
1910 physDev->textPixel);
1911 if(lpDx) {
1912 offset += lpDx[idx];
1913 xoff = offset * cosEsc;
1914 yoff = offset * -sinEsc;
1915 } else {
1916 xoff += formatEntry->gis[wstr[idx]].xOff;
1917 yoff += formatEntry->gis[wstr[idx]].yOff;
1920 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1921 image_x, image_y, image_w, image_h);
1922 XDestroyImage(image);
1924 no_image:
1925 wine_tsx11_unlock();
1927 LeaveCriticalSection(&xrender_cs);
1929 if (flags & ETO_CLIPPED)
1931 /* restore the device region */
1932 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1933 DeleteObject( saved_region );
1936 retv = TRUE;
1938 done_unlock:
1939 X11DRV_UnlockDIBSection( physDev, TRUE );
1940 return retv;
1943 /* Helper function for (stretched) blitting using xrender */
1944 static void xrender_blit( int op, Picture src_pict, Picture mask_pict, Picture dst_pict,
1945 int x_src, int y_src, int x_dst, int y_dst,
1946 double xscale, double yscale, int width, int height )
1948 int x_offset, y_offset;
1950 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1951 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1952 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1953 if(xscale != 1.0 || yscale != 1.0)
1955 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1956 * in the wrong quadrant of the x-y plane.
1958 x_offset = (xscale < 0) ? -width : 0;
1959 y_offset = (yscale < 0) ? -height : 0;
1960 set_xrender_transformation(src_pict, xscale, yscale, x_src, y_src);
1962 else
1964 x_offset = x_src;
1965 y_offset = y_src;
1966 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1968 pXRenderComposite( gdi_display, op, src_pict, mask_pict, dst_pict,
1969 x_offset, y_offset, 0, 0, x_dst, y_dst, width, height );
1972 /* Helper function for (stretched) mono->color blitting using xrender */
1973 static void xrender_mono_blit( Picture src_pict, Picture mask_pict, Picture dst_pict,
1974 int x_src, int y_src, double xscale, double yscale, int width, int height )
1976 int x_offset, y_offset;
1978 /* When doing a mono->color blit, 'src_pict' contains a 1x1 picture for tiling, the actual
1979 * source data is in mask_pict. The 'src_pict' data effectively acts as an alpha channel to the
1980 * tile data. We need PictOpOver for correct rendering.
1981 * Note since the 'source data' is in the mask picture, we have to pass x_src / y_src using
1982 * mask_x / mask_y
1984 if (xscale != 1.0 || yscale != 1.0)
1986 /* In case of mirroring we need a source x- and y-offset because without the pixels will be
1987 * in the wrong quadrant of the x-y plane.
1989 x_offset = (xscale < 0) ? -width : 0;
1990 y_offset = (yscale < 0) ? -height : 0;
1991 set_xrender_transformation(mask_pict, xscale, yscale, x_src, y_src);
1993 else
1995 x_offset = x_src;
1996 y_offset = y_src;
1997 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
1999 pXRenderComposite(gdi_display, PictOpOver, src_pict, mask_pict, dst_pict,
2000 0, 0, x_offset, y_offset, 0, 0, width, height);
2003 /******************************************************************************
2004 * AlphaBlend
2006 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2007 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2009 Picture dst_pict, src_pict = 0, mask_pict = 0, tmp_pict = 0;
2010 struct xrender_info *src_info = get_xrender_info( devSrc );
2011 double xscale, yscale;
2012 BOOL use_repeat;
2014 if(!X11DRV_XRender_Installed) {
2015 FIXME("Unable to AlphaBlend without Xrender\n");
2016 return FALSE;
2019 if (devSrc != devDst) X11DRV_LockDIBSection( devSrc, DIB_Status_GdiMod );
2020 X11DRV_LockDIBSection( devDst, DIB_Status_GdiMod );
2022 dst_pict = get_xrender_picture( devDst );
2024 use_repeat = use_source_repeat( devSrc );
2025 if (!use_repeat)
2027 xscale = src->width / (double)dst->width;
2028 yscale = src->height / (double)dst->height;
2030 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2032 if (!(blendfn.AlphaFormat & AC_SRC_ALPHA) && src_info->format)
2034 /* we need a source picture with no alpha */
2035 WXRFormat format = get_format_without_alpha( src_info->format->format );
2036 if (format != src_info->format->format)
2038 XRenderPictureAttributes pa;
2039 const WineXRenderFormat *fmt = get_xrender_format( format );
2041 wine_tsx11_lock();
2042 pa.subwindow_mode = IncludeInferiors;
2043 pa.repeat = use_repeat ? RepeatNormal : RepeatNone;
2044 tmp_pict = pXRenderCreatePicture( gdi_display, devSrc->drawable, fmt->pict_format,
2045 CPSubwindowMode|CPRepeat, &pa );
2046 wine_tsx11_unlock();
2047 src_pict = tmp_pict;
2051 if (!src_pict) src_pict = get_xrender_picture_source( devSrc, use_repeat );
2053 EnterCriticalSection( &xrender_cs );
2054 mask_pict = get_mask_pict( blendfn.SourceConstantAlpha * 257 );
2056 wine_tsx11_lock();
2057 xrender_blit( PictOpOver, src_pict, mask_pict, dst_pict,
2058 devSrc->dc_rect.left + src->visrect.left, devSrc->dc_rect.top + src->visrect.top,
2059 devDst->dc_rect.left + dst->visrect.left, devDst->dc_rect.top + dst->visrect.top,
2060 xscale, yscale,
2061 dst->visrect.right - dst->visrect.left, dst->visrect.bottom - dst->visrect.top );
2062 if (tmp_pict) pXRenderFreePicture( gdi_display, tmp_pict );
2063 wine_tsx11_unlock();
2065 LeaveCriticalSection( &xrender_cs );
2066 if (devSrc != devDst) X11DRV_UnlockDIBSection( devSrc, FALSE );
2067 X11DRV_UnlockDIBSection( devDst, TRUE );
2068 return TRUE;
2072 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2074 /* 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 */
2075 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2076 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2077 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2079 wine_tsx11_lock();
2080 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2082 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2083 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2084 (src_format->format == dst_format->format) )
2086 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2087 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2089 else /* We need depth conversion */
2091 Picture src_pict, dst_pict;
2092 XRenderPictureAttributes pa;
2093 pa.subwindow_mode = IncludeInferiors;
2094 pa.repeat = RepeatNone;
2096 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2097 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2099 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, 0, 0, 0, 0, 1.0, 1.0, width, height);
2100 pXRenderFreePicture(gdi_display, src_pict);
2101 pXRenderFreePicture(gdi_display, dst_pict);
2103 wine_tsx11_unlock();
2106 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2107 Pixmap pixmap, GC gc,
2108 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2110 BOOL stretch = (src->width != dst->width) || (src->height != dst->height);
2111 int width = dst->visrect.right - dst->visrect.left;
2112 int height = dst->visrect.bottom - dst->visrect.top;
2113 int x_src = physDevSrc->dc_rect.left + src->visrect.left;
2114 int y_src = physDevSrc->dc_rect.top + src->visrect.top;
2115 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2116 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2117 Picture src_pict=0, dst_pict=0, mask_pict=0;
2118 BOOL use_repeat;
2119 double xscale, yscale;
2121 XRenderPictureAttributes pa;
2122 pa.subwindow_mode = IncludeInferiors;
2123 pa.repeat = RepeatNone;
2125 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n",
2126 physDevSrc->depth, src->width, src->height, x_src, y_src);
2127 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, dst->width, dst->height);
2129 if(!X11DRV_XRender_Installed)
2131 TRACE("Not using XRender since it is not available or disabled\n");
2132 return FALSE;
2135 /* XRender can't handle palettes, so abort */
2136 if(X11DRV_PALETTE_XPixelToPalette)
2137 return FALSE;
2139 /* XRender is of no use in this case */
2140 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2141 return FALSE;
2143 /* Just use traditional X copy when the formats match and we don't need stretching */
2144 if((src_info->format->format == dst_format->format) && !stretch)
2146 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2147 wine_tsx11_lock();
2148 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2149 wine_tsx11_unlock();
2150 return TRUE;
2153 use_repeat = use_source_repeat( physDevSrc );
2154 if (!use_repeat)
2156 xscale = src->width / (double)dst->width;
2157 yscale = src->height / (double)dst->height;
2159 else xscale = yscale = 1; /* no scaling needed with a repeating source */
2161 /* mono -> color */
2162 if(physDevSrc->depth == 1 && physDevDst->depth > 1)
2164 XRenderColor col;
2165 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2167 /* We use the source drawable as a mask */
2168 mask_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2170 /* Use backgroundPixel as the foreground color */
2171 EnterCriticalSection( &xrender_cs );
2172 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2174 /* Create a destination picture and fill it with textPixel color as the background color */
2175 wine_tsx11_lock();
2176 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2177 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2179 xrender_mono_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2181 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2182 wine_tsx11_unlock();
2183 LeaveCriticalSection( &xrender_cs );
2185 else /* color -> color (can be at different depths) or mono -> mono */
2187 src_pict = get_xrender_picture_source( physDevSrc, use_repeat );
2189 wine_tsx11_lock();
2190 dst_pict = pXRenderCreatePicture(gdi_display,
2191 pixmap, dst_format->pict_format,
2192 CPSubwindowMode|CPRepeat, &pa);
2194 xrender_blit(PictOpSrc, src_pict, 0, dst_pict, x_src, y_src, 0, 0, xscale, yscale, width, height);
2196 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2197 wine_tsx11_unlock();
2199 return TRUE;
2202 #else /* SONAME_LIBXRENDER */
2204 void X11DRV_XRender_Init(void)
2206 TRACE("XRender support not compiled in.\n");
2207 return;
2210 void X11DRV_XRender_Finalize(void)
2214 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2216 assert(0);
2217 return FALSE;
2220 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2222 assert(0);
2223 return;
2226 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2228 assert(0);
2229 return;
2232 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2233 const RECT *lprect, LPCWSTR wstr, UINT count,
2234 const INT *lpDx )
2236 assert(0);
2237 return FALSE;
2240 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2242 assert(0);
2243 return;
2246 BOOL XRender_AlphaBlend( X11DRV_PDEVICE *devDst, X11DRV_PDEVICE *devSrc,
2247 struct bitblt_coords *dst, struct bitblt_coords *src, BLENDFUNCTION blendfn )
2249 FIXME("not supported - XRENDER headers were missing at compile time\n");
2250 return FALSE;
2253 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2255 wine_tsx11_lock();
2256 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2258 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2259 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2260 wine_tsx11_unlock();
2263 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, int bits_pixel, const DIBSECTION *dib)
2265 return FALSE;
2268 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2269 Pixmap pixmap, GC gc,
2270 const struct bitblt_coords *src, const struct bitblt_coords *dst )
2272 return FALSE;
2274 #endif /* SONAME_LIBXRENDER */