Release 1.1.37.
[wine/gsoc-2012-control.git] / dlls / winex11.drv / xrender.c
blob214be7b682aa70c52d0dffd5094d5bef971bfde8
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 #define MAX_FORMATS 10
59 typedef enum wine_xrformat
61 WXR_FORMAT_MONO,
62 WXR_FORMAT_GRAY,
63 WXR_FORMAT_X1R5G5B5,
64 WXR_FORMAT_X1B5G5R5,
65 WXR_FORMAT_R5G6B5,
66 WXR_FORMAT_B5G6R5,
67 WXR_FORMAT_R8G8B8,
68 WXR_FORMAT_B8G8R8,
69 WXR_FORMAT_A8R8G8B8,
70 WXR_FORMAT_X8R8G8B8,
71 } WXRFormat;
73 typedef struct wine_xrender_format_template
75 WXRFormat wxr_format;
76 unsigned int depth;
77 unsigned int alpha;
78 unsigned int alphaMask;
79 unsigned int red;
80 unsigned int redMask;
81 unsigned int green;
82 unsigned int greenMask;
83 unsigned int blue;
84 unsigned int blueMask;
85 } WineXRenderFormatTemplate;
87 static const WineXRenderFormatTemplate wxr_formats_template[] =
89 /* Format depth alpha mask red mask green mask blue mask*/
90 {WXR_FORMAT_MONO, 1, 0, 0x01, 0, 0, 0, 0, 0, 0 },
91 {WXR_FORMAT_GRAY, 8, 0, 0xff, 0, 0, 0, 0, 0, 0 },
92 {WXR_FORMAT_X1R5G5B5, 16, 0, 0, 10, 0x1f, 5, 0x1f, 0, 0x1f },
93 {WXR_FORMAT_X1B5G5R5, 16, 0, 0, 0, 0x1f, 5, 0x1f, 10, 0x1f },
94 {WXR_FORMAT_R5G6B5, 16, 0, 0, 11, 0x1f, 5, 0x3f, 0, 0x1f },
95 {WXR_FORMAT_B5G6R5, 16, 0, 0, 0, 0x1f, 5, 0x3f, 11, 0x1f },
96 {WXR_FORMAT_R8G8B8, 24, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff },
97 {WXR_FORMAT_B8G8R8, 24, 0, 0, 0, 0xff, 8, 0xff, 16, 0xff },
98 {WXR_FORMAT_A8R8G8B8, 32, 24, 0xff, 16, 0xff, 8, 0xff, 0, 0xff },
99 {WXR_FORMAT_X8R8G8B8, 32, 0, 0, 16, 0xff, 8, 0xff, 0, 0xff }
102 typedef struct wine_xrender_format
104 WXRFormat format;
105 XRenderPictFormat *pict_format;
106 } WineXRenderFormat;
108 static WineXRenderFormat wxr_formats[MAX_FORMATS];
109 static int WineXRenderFormatsListSize = 0;
110 static WineXRenderFormat *default_format = NULL;
112 typedef struct
114 LOGFONTW lf;
115 XFORM xform;
116 SIZE devsize; /* size in device coords */
117 DWORD hash;
118 } LFANDSIZE;
120 #define INITIAL_REALIZED_BUF_SIZE 128
122 typedef enum { AA_None = 0, AA_Grey, AA_RGB, AA_BGR, AA_VRGB, AA_VBGR, AA_MAXVALUE } AA_Type;
124 typedef struct
126 GlyphSet glyphset;
127 const WineXRenderFormat *font_format;
128 int nrealized;
129 BOOL *realized;
130 void **bitmaps;
131 XGlyphInfo *gis;
132 } gsCacheEntryFormat;
134 typedef struct
136 LFANDSIZE lfsz;
137 AA_Type aa_default;
138 gsCacheEntryFormat * format[AA_MAXVALUE];
139 INT count;
140 INT next;
141 } gsCacheEntry;
143 struct xrender_info
145 int cache_index;
146 Picture pict;
147 Picture pict_src;
148 const WineXRenderFormat *format;
151 static gsCacheEntry *glyphsetCache = NULL;
152 static DWORD glyphsetCacheSize = 0;
153 static INT lastfree = -1;
154 static INT mru = -1;
156 #define INIT_CACHE_SIZE 10
158 static int antialias = 1;
160 static void *xrender_handle;
162 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
163 MAKE_FUNCPTR(XRenderAddGlyphs)
164 MAKE_FUNCPTR(XRenderComposite)
165 MAKE_FUNCPTR(XRenderCompositeString8)
166 MAKE_FUNCPTR(XRenderCompositeString16)
167 MAKE_FUNCPTR(XRenderCompositeString32)
168 MAKE_FUNCPTR(XRenderCompositeText16)
169 MAKE_FUNCPTR(XRenderCreateGlyphSet)
170 MAKE_FUNCPTR(XRenderCreatePicture)
171 MAKE_FUNCPTR(XRenderFillRectangle)
172 MAKE_FUNCPTR(XRenderFindFormat)
173 MAKE_FUNCPTR(XRenderFindVisualFormat)
174 MAKE_FUNCPTR(XRenderFreeGlyphSet)
175 MAKE_FUNCPTR(XRenderFreePicture)
176 MAKE_FUNCPTR(XRenderSetPictureClipRectangles)
177 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
178 MAKE_FUNCPTR(XRenderSetPictureTransform)
179 #endif
180 MAKE_FUNCPTR(XRenderQueryExtension)
181 #undef MAKE_FUNCPTR
183 static CRITICAL_SECTION xrender_cs;
184 static CRITICAL_SECTION_DEBUG critsect_debug =
186 0, 0, &xrender_cs,
187 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
188 0, 0, { (DWORD_PTR)(__FILE__ ": xrender_cs") }
190 static CRITICAL_SECTION xrender_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
192 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
193 ( ( (ULONG)_x4 << 24 ) | \
194 ( (ULONG)_x3 << 16 ) | \
195 ( (ULONG)_x2 << 8 ) | \
196 (ULONG)_x1 )
198 #define MS_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p')
200 #define GASP_GRIDFIT 0x01
201 #define GASP_DOGRAY 0x02
203 #ifdef WORDS_BIGENDIAN
204 #define get_be_word(x) (x)
205 #define NATIVE_BYTE_ORDER MSBFirst
206 #else
207 #define get_be_word(x) RtlUshortByteSwap(x)
208 #define NATIVE_BYTE_ORDER LSBFirst
209 #endif
211 static BOOL get_xrender_template(const WineXRenderFormatTemplate *fmt, XRenderPictFormat *templ, unsigned long *mask)
213 templ->id = 0;
214 templ->type = PictTypeDirect;
215 templ->depth = fmt->depth;
216 templ->direct.alpha = fmt->alpha;
217 templ->direct.alphaMask = fmt->alphaMask;
218 templ->direct.red = fmt->red;
219 templ->direct.redMask = fmt->redMask;
220 templ->direct.green = fmt->green;
221 templ->direct.greenMask = fmt->greenMask;
222 templ->direct.blue = fmt->blue;
223 templ->direct.blueMask = fmt->blueMask;
224 templ->colormap = 0;
226 *mask = PictFormatType | PictFormatDepth | PictFormatAlpha | PictFormatAlphaMask | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask;
228 return TRUE;
231 static BOOL is_wxrformat_compatible_with_default_visual(const WineXRenderFormatTemplate *fmt)
233 if(fmt->depth != screen_depth)
234 return FALSE;
235 if( (fmt->redMask << fmt->red) != visual->red_mask)
236 return FALSE;
237 if( (fmt->greenMask << fmt->green) != visual->green_mask)
238 return FALSE;
239 if( (fmt->blueMask << fmt->blue) != visual->blue_mask)
240 return FALSE;
242 /* We never select a default ARGB visual */
243 if(fmt->alphaMask)
244 return FALSE;
246 return TRUE;
249 static int load_xrender_formats(void)
251 unsigned int i;
252 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
254 XRenderPictFormat templ, *pict_format;
256 if(is_wxrformat_compatible_with_default_visual(&wxr_formats_template[i]))
258 wine_tsx11_lock();
259 pict_format = pXRenderFindVisualFormat(gdi_display, visual);
260 if(!pict_format)
262 /* Xrender doesn't like DirectColor visuals, try to find a TrueColor one instead */
263 if (visual->class == DirectColor)
265 XVisualInfo info;
266 if (XMatchVisualInfo( gdi_display, DefaultScreen(gdi_display),
267 screen_depth, TrueColor, &info ))
269 pict_format = pXRenderFindVisualFormat(gdi_display, info.visual);
270 if (pict_format) visual = info.visual;
274 wine_tsx11_unlock();
276 if(pict_format)
278 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
279 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
280 default_format = &wxr_formats[WineXRenderFormatsListSize];
281 WineXRenderFormatsListSize++;
282 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
285 else
287 unsigned long mask = 0;
288 get_xrender_template(&wxr_formats_template[i], &templ, &mask);
290 wine_tsx11_lock();
291 pict_format = pXRenderFindFormat(gdi_display, mask, &templ, 0);
292 wine_tsx11_unlock();
294 if(pict_format)
296 wxr_formats[WineXRenderFormatsListSize].format = wxr_formats_template[i].wxr_format;
297 wxr_formats[WineXRenderFormatsListSize].pict_format = pict_format;
298 WineXRenderFormatsListSize++;
299 TRACE("Loaded pict_format with id=%#lx for wxr_format=%#x\n", pict_format->id, wxr_formats_template[i].wxr_format);
303 return WineXRenderFormatsListSize;
306 /***********************************************************************
307 * X11DRV_XRender_Init
309 * Let's see if our XServer has the extension available
312 void X11DRV_XRender_Init(void)
314 int event_base, i;
316 if (client_side_with_render &&
317 wine_dlopen(SONAME_LIBX11, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
318 wine_dlopen(SONAME_LIBXEXT, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
319 (xrender_handle = wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW, NULL, 0)))
322 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xrender_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
323 LOAD_FUNCPTR(XRenderAddGlyphs)
324 LOAD_FUNCPTR(XRenderComposite)
325 LOAD_FUNCPTR(XRenderCompositeString8)
326 LOAD_FUNCPTR(XRenderCompositeString16)
327 LOAD_FUNCPTR(XRenderCompositeString32)
328 LOAD_FUNCPTR(XRenderCompositeText16)
329 LOAD_FUNCPTR(XRenderCreateGlyphSet)
330 LOAD_FUNCPTR(XRenderCreatePicture)
331 LOAD_FUNCPTR(XRenderFillRectangle)
332 LOAD_FUNCPTR(XRenderFindFormat)
333 LOAD_FUNCPTR(XRenderFindVisualFormat)
334 LOAD_FUNCPTR(XRenderFreeGlyphSet)
335 LOAD_FUNCPTR(XRenderFreePicture)
336 LOAD_FUNCPTR(XRenderSetPictureClipRectangles)
337 LOAD_FUNCPTR(XRenderQueryExtension)
338 #undef LOAD_FUNCPTR
339 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
340 #define LOAD_OPTIONAL_FUNCPTR(f) p##f = wine_dlsym(xrender_handle, #f, NULL, 0);
341 LOAD_OPTIONAL_FUNCPTR(XRenderSetPictureTransform)
342 #undef LOAD_OPTIONAL_FUNCPTR
343 #endif
345 wine_tsx11_lock();
346 X11DRV_XRender_Installed = pXRenderQueryExtension(gdi_display, &event_base, &xrender_error_base);
347 wine_tsx11_unlock();
348 if(X11DRV_XRender_Installed) {
349 TRACE("Xrender is up and running error_base = %d\n", xrender_error_base);
350 if(!load_xrender_formats()) /* This fails in buggy versions of libXrender.so */
352 wine_tsx11_unlock();
353 WINE_MESSAGE(
354 "Wine has detected that you probably have a buggy version\n"
355 "of libXrender.so . Because of this client side font rendering\n"
356 "will be disabled. Please upgrade this library.\n");
357 X11DRV_XRender_Installed = FALSE;
358 return;
361 if (!visual->red_mask || !visual->green_mask || !visual->blue_mask) {
362 WARN("one or more of the colour masks are 0, disabling XRENDER. Try running in 16-bit mode or higher.\n");
363 X11DRV_XRender_Installed = FALSE;
368 sym_not_found:
369 if(X11DRV_XRender_Installed || client_side_with_core)
371 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
372 sizeof(*glyphsetCache) * INIT_CACHE_SIZE);
374 glyphsetCacheSize = INIT_CACHE_SIZE;
375 lastfree = 0;
376 for(i = 0; i < INIT_CACHE_SIZE; i++) {
377 glyphsetCache[i].next = i + 1;
378 glyphsetCache[i].count = -1;
380 glyphsetCache[i-1].next = -1;
381 using_client_side_fonts = 1;
383 if(!X11DRV_XRender_Installed) {
384 TRACE("Xrender is not available on your XServer, client side rendering with the core protocol instead.\n");
385 if(screen_depth <= 8 || !client_side_antialias_with_core)
386 antialias = 0;
387 } else {
388 if(screen_depth <= 8 || !client_side_antialias_with_render)
389 antialias = 0;
392 else TRACE("Using X11 core fonts\n");
395 /* Helper function to convert from a color packed in a 32-bit integer to a XRenderColor */
396 static void get_xrender_color(const WineXRenderFormat *wxr_format, int src_color, XRenderColor *dst_color)
398 XRenderPictFormat *pf = wxr_format->pict_format;
400 if(pf->direct.redMask)
401 dst_color->red = ((src_color >> pf->direct.red) & pf->direct.redMask) * 65535/pf->direct.redMask;
402 else
403 dst_color->red = 0;
405 if(pf->direct.greenMask)
406 dst_color->green = ((src_color >> pf->direct.green) & pf->direct.greenMask) * 65535/pf->direct.greenMask;
407 else
408 dst_color->green = 0;
410 if(pf->direct.blueMask)
411 dst_color->blue = ((src_color >> pf->direct.blue) & pf->direct.blueMask) * 65535/pf->direct.blueMask;
412 else
413 dst_color->blue = 0;
415 dst_color->alpha = 0xffff;
418 static const WineXRenderFormat *get_xrender_format(WXRFormat format)
420 int i;
421 for(i=0; i<WineXRenderFormatsListSize; i++)
423 if(wxr_formats[i].format == format)
425 TRACE("Returning wxr_format=%#x\n", format);
426 return &wxr_formats[i];
429 return NULL;
432 static const WineXRenderFormat *get_xrender_format_from_color_shifts(int depth, ColorShifts *shifts)
434 int redMask, greenMask, blueMask;
435 unsigned int i;
437 if(depth == 1)
438 return get_xrender_format(WXR_FORMAT_MONO);
440 /* physDevs of a depth <=8, don't have color_shifts set and XRender can't handle those except for 1-bit */
441 if(!shifts)
442 return default_format;
444 redMask = shifts->physicalRed.max << shifts->physicalRed.shift;
445 greenMask = shifts->physicalGreen.max << shifts->physicalGreen.shift;
446 blueMask = shifts->physicalBlue.max << shifts->physicalBlue.shift;
448 /* Try to locate a format which matches the specification of the dibsection. */
449 for(i = 0; i < (sizeof(wxr_formats_template) / sizeof(wxr_formats_template[0])); i++)
451 if( depth == wxr_formats_template[i].depth &&
452 redMask == (wxr_formats_template[i].redMask << wxr_formats_template[i].red) &&
453 greenMask == (wxr_formats_template[i].greenMask << wxr_formats_template[i].green) &&
454 blueMask == (wxr_formats_template[i].blueMask << wxr_formats_template[i].blue) )
457 /* When we reach this stage the format was found in our template table but this doesn't mean that
458 * the Xserver also supports this format (e.g. its depth might be too low). The call below verifies that.
460 return get_xrender_format(wxr_formats_template[i].wxr_format);
464 /* This should not happen because when we reach 'shifts' must have been set and we only allows shifts which are backed by X */
465 ERR("No XRender format found!\n");
466 return NULL;
469 /* Set the x/y scaling and x/y offsets in the transformation matrix of the source picture */
470 static void set_xrender_transformation(Picture src_pict, float xscale, float yscale, int xoffset, int yoffset)
472 #ifdef HAVE_XRENDERSETPICTURETRANSFORM
473 XTransform xform = {{
474 { XDoubleToFixed(xscale), XDoubleToFixed(0), XDoubleToFixed(xoffset) },
475 { XDoubleToFixed(0), XDoubleToFixed(yscale), XDoubleToFixed(yoffset) },
476 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
479 pXRenderSetPictureTransform(gdi_display, src_pict, &xform);
480 #endif
483 static struct xrender_info *get_xrender_info(X11DRV_PDEVICE *physDev)
485 if(!physDev->xrender)
487 physDev->xrender = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physDev->xrender));
489 if(!physDev->xrender)
491 ERR("Unable to allocate XRENDERINFO!\n");
492 return NULL;
494 physDev->xrender->cache_index = -1;
496 if (!physDev->xrender->format)
497 physDev->xrender->format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
499 return physDev->xrender;
502 static Picture get_xrender_picture(X11DRV_PDEVICE *physDev)
504 struct xrender_info *info = get_xrender_info(physDev);
505 if (!info) return 0;
507 if (!info->pict && info->format)
509 XRenderPictureAttributes pa;
510 RGNDATA *clip = X11DRV_GetRegionData( physDev->region, 0 );
512 wine_tsx11_lock();
513 pa.subwindow_mode = IncludeInferiors;
514 info->pict = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
515 CPSubwindowMode, &pa);
516 if (info->pict && clip)
517 pXRenderSetPictureClipRectangles( gdi_display, info->pict,
518 physDev->dc_rect.left, physDev->dc_rect.top,
519 (XRectangle *)clip->Buffer, clip->rdh.nCount );
520 wine_tsx11_unlock();
521 TRACE("Allocing pict=%lx dc=%p drawable=%08lx\n", info->pict, physDev->hdc, physDev->drawable);
522 HeapFree( GetProcessHeap(), 0, clip );
525 return info->pict;
528 static Picture get_xrender_picture_source(X11DRV_PDEVICE *physDev)
530 struct xrender_info *info = get_xrender_info(physDev);
531 if (!info) return 0;
533 if (!info->pict_src && info->format)
535 XRenderPictureAttributes pa;
537 wine_tsx11_lock();
538 pa.subwindow_mode = IncludeInferiors;
539 info->pict_src = pXRenderCreatePicture(gdi_display, physDev->drawable, info->format->pict_format,
540 CPSubwindowMode, &pa);
541 wine_tsx11_unlock();
543 TRACE("Allocing pict_src=%lx dc=%p drawable=%08lx\n", info->pict_src, physDev->hdc, physDev->drawable);
546 return info->pict_src;
549 static BOOL fontcmp(LFANDSIZE *p1, LFANDSIZE *p2)
551 if(p1->hash != p2->hash) return TRUE;
552 if(memcmp(&p1->devsize, &p2->devsize, sizeof(p1->devsize))) return TRUE;
553 if(memcmp(&p1->xform, &p2->xform, sizeof(p1->xform))) return TRUE;
554 if(memcmp(&p1->lf, &p2->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
555 return strcmpiW(p1->lf.lfFaceName, p2->lf.lfFaceName);
558 #if 0
559 static void walk_cache(void)
561 int i;
563 EnterCriticalSection(&xrender_cs);
564 for(i=mru; i >= 0; i = glyphsetCache[i].next)
565 TRACE("item %d\n", i);
566 LeaveCriticalSection(&xrender_cs);
568 #endif
570 static int LookupEntry(LFANDSIZE *plfsz)
572 int i, prev_i = -1;
574 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
575 TRACE("%d\n", i);
576 if(glyphsetCache[i].count == -1) { /* reached free list so stop */
577 i = -1;
578 break;
581 if(!fontcmp(&glyphsetCache[i].lfsz, plfsz)) {
582 glyphsetCache[i].count++;
583 if(prev_i >= 0) {
584 glyphsetCache[prev_i].next = glyphsetCache[i].next;
585 glyphsetCache[i].next = mru;
586 mru = i;
588 TRACE("found font in cache %d\n", i);
589 return i;
591 prev_i = i;
593 TRACE("font not in cache\n");
594 return -1;
597 static void FreeEntry(int entry)
599 int i, format;
601 for(format = 0; format < AA_MAXVALUE; format++) {
602 gsCacheEntryFormat * formatEntry;
604 if( !glyphsetCache[entry].format[format] )
605 continue;
607 formatEntry = glyphsetCache[entry].format[format];
609 if(formatEntry->glyphset) {
610 wine_tsx11_lock();
611 pXRenderFreeGlyphSet(gdi_display, formatEntry->glyphset);
612 wine_tsx11_unlock();
613 formatEntry->glyphset = 0;
615 if(formatEntry->nrealized) {
616 HeapFree(GetProcessHeap(), 0, formatEntry->realized);
617 formatEntry->realized = NULL;
618 if(formatEntry->bitmaps) {
619 for(i = 0; i < formatEntry->nrealized; i++)
620 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps[i]);
621 HeapFree(GetProcessHeap(), 0, formatEntry->bitmaps);
622 formatEntry->bitmaps = NULL;
624 HeapFree(GetProcessHeap(), 0, formatEntry->gis);
625 formatEntry->gis = NULL;
626 formatEntry->nrealized = 0;
629 HeapFree(GetProcessHeap(), 0, formatEntry);
630 glyphsetCache[entry].format[format] = NULL;
634 static int AllocEntry(void)
636 int best = -1, prev_best = -1, i, prev_i = -1;
638 if(lastfree >= 0) {
639 assert(glyphsetCache[lastfree].count == -1);
640 glyphsetCache[lastfree].count = 1;
641 best = lastfree;
642 lastfree = glyphsetCache[lastfree].next;
643 assert(best != mru);
644 glyphsetCache[best].next = mru;
645 mru = best;
647 TRACE("empty space at %d, next lastfree = %d\n", mru, lastfree);
648 return mru;
651 for(i = mru; i >= 0; i = glyphsetCache[i].next) {
652 if(glyphsetCache[i].count == 0) {
653 best = i;
654 prev_best = prev_i;
656 prev_i = i;
659 if(best >= 0) {
660 TRACE("freeing unused glyphset at cache %d\n", best);
661 FreeEntry(best);
662 glyphsetCache[best].count = 1;
663 if(prev_best >= 0) {
664 glyphsetCache[prev_best].next = glyphsetCache[best].next;
665 glyphsetCache[best].next = mru;
666 mru = best;
667 } else {
668 assert(mru == best);
670 return mru;
673 TRACE("Growing cache\n");
675 if (glyphsetCache)
676 glyphsetCache = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
677 glyphsetCache,
678 (glyphsetCacheSize + INIT_CACHE_SIZE)
679 * sizeof(*glyphsetCache));
680 else
681 glyphsetCache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
682 (glyphsetCacheSize + INIT_CACHE_SIZE)
683 * sizeof(*glyphsetCache));
685 for(best = i = glyphsetCacheSize; i < glyphsetCacheSize + INIT_CACHE_SIZE;
686 i++) {
687 glyphsetCache[i].next = i + 1;
688 glyphsetCache[i].count = -1;
690 glyphsetCache[i-1].next = -1;
691 glyphsetCacheSize += INIT_CACHE_SIZE;
693 lastfree = glyphsetCache[best].next;
694 glyphsetCache[best].count = 1;
695 glyphsetCache[best].next = mru;
696 mru = best;
697 TRACE("new free cache slot at %d\n", mru);
698 return mru;
701 static BOOL get_gasp_flags(X11DRV_PDEVICE *physDev, WORD *flags)
703 DWORD size;
704 WORD *gasp, *buffer;
705 WORD num_recs;
706 DWORD ppem;
707 TEXTMETRICW tm;
709 *flags = 0;
711 size = GetFontData(physDev->hdc, MS_GASP_TAG, 0, NULL, 0);
712 if(size == GDI_ERROR)
713 return FALSE;
715 gasp = buffer = HeapAlloc(GetProcessHeap(), 0, size);
716 GetFontData(physDev->hdc, MS_GASP_TAG, 0, gasp, size);
718 GetTextMetricsW(physDev->hdc, &tm);
719 ppem = abs(X11DRV_YWStoDS(physDev, tm.tmAscent + tm.tmDescent - tm.tmInternalLeading));
721 gasp++;
722 num_recs = get_be_word(*gasp);
723 gasp++;
724 while(num_recs--)
726 *flags = get_be_word(*(gasp + 1));
727 if(ppem <= get_be_word(*gasp))
728 break;
729 gasp += 2;
731 TRACE("got flags %04x for ppem %d\n", *flags, ppem);
733 HeapFree(GetProcessHeap(), 0, buffer);
734 return TRUE;
737 static AA_Type get_antialias_type( X11DRV_PDEVICE *physDev, BOOL subpixel, BOOL hinter)
739 AA_Type ret;
740 WORD flags;
741 UINT font_smoothing_type, font_smoothing_orientation;
743 if (X11DRV_XRender_Installed && subpixel &&
744 SystemParametersInfoW( SPI_GETFONTSMOOTHINGTYPE, 0, &font_smoothing_type, 0) &&
745 font_smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
747 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHINGORIENTATION, 0,
748 &font_smoothing_orientation, 0) &&
749 font_smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
751 ret = AA_BGR;
753 else
754 ret = AA_RGB;
755 /*FIXME
756 If the monitor is in portrait mode, ClearType is disabled in the MS Windows (MSDN).
757 But, Wine's subpixel rendering can support the portrait mode.
760 else if (!hinter || !get_gasp_flags(physDev, &flags) || flags & GASP_DOGRAY)
761 ret = AA_Grey;
762 else
763 ret = AA_None;
765 return ret;
768 static int GetCacheEntry(X11DRV_PDEVICE *physDev, LFANDSIZE *plfsz)
770 int ret;
771 int format;
772 gsCacheEntry *entry;
773 static int hinter = -1;
774 static int subpixel = -1;
775 BOOL font_smoothing;
777 if((ret = LookupEntry(plfsz)) != -1) return ret;
779 ret = AllocEntry();
780 entry = glyphsetCache + ret;
781 entry->lfsz = *plfsz;
782 for( format = 0; format < AA_MAXVALUE; format++ ) {
783 assert( !entry->format[format] );
786 if(antialias && plfsz->lf.lfQuality != NONANTIALIASED_QUALITY)
788 if(hinter == -1 || subpixel == -1)
790 RASTERIZER_STATUS status;
791 GetRasterizerCaps(&status, sizeof(status));
792 hinter = status.wFlags & WINE_TT_HINTER_ENABLED;
793 subpixel = status.wFlags & WINE_TT_SUBPIXEL_RENDERING_ENABLED;
796 switch (plfsz->lf.lfQuality)
798 case ANTIALIASED_QUALITY:
799 entry->aa_default = get_antialias_type( physDev, FALSE, hinter );
800 break;
801 case CLEARTYPE_QUALITY:
802 case CLEARTYPE_NATURAL_QUALITY:
803 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
804 break;
805 case DEFAULT_QUALITY:
806 case DRAFT_QUALITY:
807 case PROOF_QUALITY:
808 default:
809 if ( SystemParametersInfoW( SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0) &&
810 font_smoothing)
812 entry->aa_default = get_antialias_type( physDev, subpixel, hinter );
814 else
815 entry->aa_default = AA_None;
816 break;
819 else
820 entry->aa_default = AA_None;
822 return ret;
825 static void dec_ref_cache(int index)
827 assert(index >= 0);
828 TRACE("dec'ing entry %d to %d\n", index, glyphsetCache[index].count - 1);
829 assert(glyphsetCache[index].count > 0);
830 glyphsetCache[index].count--;
833 static void lfsz_calc_hash(LFANDSIZE *plfsz)
835 DWORD hash = 0, *ptr, two_chars;
836 WORD *pwc;
837 int i;
839 hash ^= plfsz->devsize.cx;
840 hash ^= plfsz->devsize.cy;
841 for(i = 0, ptr = (DWORD*)&plfsz->xform; i < sizeof(XFORM)/sizeof(DWORD); i++, ptr++)
842 hash ^= *ptr;
843 for(i = 0, ptr = (DWORD*)&plfsz->lf; i < 7; i++, ptr++)
844 hash ^= *ptr;
845 for(i = 0, ptr = (DWORD*)plfsz->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
846 two_chars = *ptr;
847 pwc = (WCHAR *)&two_chars;
848 if(!*pwc) break;
849 *pwc = toupperW(*pwc);
850 pwc++;
851 *pwc = toupperW(*pwc);
852 hash ^= two_chars;
853 if(!*pwc) break;
855 plfsz->hash = hash;
856 return;
859 /***********************************************************************
860 * X11DRV_XRender_Finalize
862 void X11DRV_XRender_Finalize(void)
864 int i;
866 EnterCriticalSection(&xrender_cs);
867 for(i = mru; i >= 0; i = glyphsetCache[i].next)
868 FreeEntry(i);
869 LeaveCriticalSection(&xrender_cs);
873 /***********************************************************************
874 * X11DRV_XRender_SelectFont
876 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
878 LFANDSIZE lfsz;
879 struct xrender_info *info;
881 GetObjectW(hfont, sizeof(lfsz.lf), &lfsz.lf);
882 TRACE("h=%d w=%d weight=%d it=%d charset=%d name=%s\n",
883 lfsz.lf.lfHeight, lfsz.lf.lfWidth, lfsz.lf.lfWeight,
884 lfsz.lf.lfItalic, lfsz.lf.lfCharSet, debugstr_w(lfsz.lf.lfFaceName));
885 lfsz.lf.lfWidth = abs( lfsz.lf.lfWidth );
886 lfsz.devsize.cx = X11DRV_XWStoDS( physDev, lfsz.lf.lfWidth );
887 lfsz.devsize.cy = X11DRV_YWStoDS( physDev, lfsz.lf.lfHeight );
888 GetWorldTransform( physDev->hdc, &lfsz.xform );
889 lfsz_calc_hash(&lfsz);
891 info = get_xrender_info(physDev);
892 if (!info) return 0;
894 EnterCriticalSection(&xrender_cs);
895 if(info->cache_index != -1)
896 dec_ref_cache(info->cache_index);
897 info->cache_index = GetCacheEntry(physDev, &lfsz);
898 LeaveCriticalSection(&xrender_cs);
899 return 0;
902 /***********************************************************************
903 * X11DRV_XRender_SetDeviceClipping
905 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
907 if (physDev->xrender->pict)
909 wine_tsx11_lock();
910 pXRenderSetPictureClipRectangles( gdi_display, physDev->xrender->pict,
911 physDev->dc_rect.left, physDev->dc_rect.top,
912 (XRectangle *)data->Buffer, data->rdh.nCount );
913 wine_tsx11_unlock();
917 /***********************************************************************
918 * X11DRV_XRender_DeleteDC
920 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
922 X11DRV_XRender_UpdateDrawable(physDev);
924 EnterCriticalSection(&xrender_cs);
925 if(physDev->xrender->cache_index != -1)
926 dec_ref_cache(physDev->xrender->cache_index);
927 LeaveCriticalSection(&xrender_cs);
929 HeapFree(GetProcessHeap(), 0, physDev->xrender);
930 physDev->xrender = NULL;
931 return;
934 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, const DIBSECTION *dib)
936 const WineXRenderFormat *fmt;
937 ColorShifts shifts;
939 /* When XRender is not around we can only use the screen_depth and when needed we perform depth conversion
940 * in software. Further we also return the screen depth for paletted formats or TrueColor formats with a low
941 * number of bits because XRender can't handle paletted formats and 8-bit TrueColor does not exist for XRender. */
942 if(!X11DRV_XRender_Installed || dib->dsBm.bmBitsPixel <= 8)
943 return FALSE;
945 X11DRV_PALETTE_ComputeColorShifts(&shifts, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
947 /* Common formats should be in our picture format table. */
948 fmt = get_xrender_format_from_color_shifts(dib->dsBm.bmBitsPixel, &shifts);
949 if(fmt)
951 physBitmap->pixmap_depth = fmt->pict_format->depth;
952 physBitmap->trueColor = TRUE;
953 physBitmap->pixmap_color_shifts = shifts;
954 return TRUE;
956 TRACE("Unhandled dibsection format bpp=%d, redMask=%x, greenMask=%x, blueMask=%x\n",
957 dib->dsBm.bmBitsPixel, dib->dsBitfields[0], dib->dsBitfields[1], dib->dsBitfields[2]);
958 return FALSE;
961 /***********************************************************************
962 * X11DRV_XRender_UpdateDrawable
964 * Deletes the pict and tile when the drawable changes.
966 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
968 struct xrender_info *info = physDev->xrender;
970 if (info->pict || info->pict_src)
972 wine_tsx11_lock();
973 XFlush( gdi_display );
974 if (info->pict)
976 TRACE("freeing pict = %lx dc = %p\n", info->pict, physDev->hdc);
977 pXRenderFreePicture(gdi_display, info->pict);
978 info->pict = 0;
980 if(info->pict_src)
982 TRACE("freeing pict = %lx dc = %p\n", info->pict_src, physDev->hdc);
983 pXRenderFreePicture(gdi_display, info->pict_src);
984 info->pict_src = 0;
986 wine_tsx11_unlock();
989 info->format = NULL;
992 /************************************************************************
993 * UploadGlyph
995 * Helper to ExtTextOut. Must be called inside xrender_cs
997 static BOOL UploadGlyph(X11DRV_PDEVICE *physDev, int glyph, AA_Type format)
999 unsigned int buflen;
1000 char *buf;
1001 Glyph gid;
1002 GLYPHMETRICS gm;
1003 XGlyphInfo gi;
1004 gsCacheEntry *entry = glyphsetCache + physDev->xrender->cache_index;
1005 gsCacheEntryFormat *formatEntry;
1006 UINT ggo_format = GGO_GLYPH_INDEX;
1007 WXRFormat wxr_format;
1008 static const char zero[4];
1009 static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
1011 switch(format) {
1012 case AA_Grey:
1013 ggo_format |= WINE_GGO_GRAY16_BITMAP;
1014 break;
1015 case AA_RGB:
1016 ggo_format |= WINE_GGO_HRGB_BITMAP;
1017 break;
1018 case AA_BGR:
1019 ggo_format |= WINE_GGO_HBGR_BITMAP;
1020 break;
1021 case AA_VRGB:
1022 ggo_format |= WINE_GGO_VRGB_BITMAP;
1023 break;
1024 case AA_VBGR:
1025 ggo_format |= WINE_GGO_VBGR_BITMAP;
1026 break;
1028 default:
1029 ERR("aa = %d - not implemented\n", format);
1030 case AA_None:
1031 ggo_format |= GGO_BITMAP;
1032 break;
1035 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1036 if(buflen == GDI_ERROR) {
1037 if(format != AA_None) {
1038 format = AA_None;
1039 entry->aa_default = AA_None;
1040 ggo_format = GGO_GLYPH_INDEX | GGO_BITMAP;
1041 buflen = GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, 0, NULL, &identity);
1043 if(buflen == GDI_ERROR) {
1044 WARN("GetGlyphOutlineW failed\n");
1045 return FALSE;
1047 TRACE("Turning off antialiasing for this monochrome font\n");
1050 /* If there is nothing for the current type, we create the entry. */
1051 if( !entry->format[format] ) {
1052 entry->format[format] = HeapAlloc(GetProcessHeap(),
1053 HEAP_ZERO_MEMORY,
1054 sizeof(gsCacheEntryFormat));
1056 formatEntry = entry->format[format];
1058 if(formatEntry->nrealized <= glyph) {
1059 formatEntry->nrealized = (glyph / 128 + 1) * 128;
1061 if (formatEntry->realized)
1062 formatEntry->realized = HeapReAlloc(GetProcessHeap(),
1063 HEAP_ZERO_MEMORY,
1064 formatEntry->realized,
1065 formatEntry->nrealized * sizeof(BOOL));
1066 else
1067 formatEntry->realized = HeapAlloc(GetProcessHeap(),
1068 HEAP_ZERO_MEMORY,
1069 formatEntry->nrealized * sizeof(BOOL));
1071 if(!X11DRV_XRender_Installed) {
1072 if (formatEntry->bitmaps)
1073 formatEntry->bitmaps = HeapReAlloc(GetProcessHeap(),
1074 HEAP_ZERO_MEMORY,
1075 formatEntry->bitmaps,
1076 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1077 else
1078 formatEntry->bitmaps = HeapAlloc(GetProcessHeap(),
1079 HEAP_ZERO_MEMORY,
1080 formatEntry->nrealized * sizeof(formatEntry->bitmaps[0]));
1082 if (formatEntry->gis)
1083 formatEntry->gis = HeapReAlloc(GetProcessHeap(),
1084 HEAP_ZERO_MEMORY,
1085 formatEntry->gis,
1086 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1087 else
1088 formatEntry->gis = HeapAlloc(GetProcessHeap(),
1089 HEAP_ZERO_MEMORY,
1090 formatEntry->nrealized * sizeof(formatEntry->gis[0]));
1094 if(formatEntry->glyphset == 0 && X11DRV_XRender_Installed) {
1095 switch(format) {
1096 case AA_Grey:
1097 wxr_format = WXR_FORMAT_GRAY;
1098 break;
1100 case AA_RGB:
1101 case AA_BGR:
1102 case AA_VRGB:
1103 case AA_VBGR:
1104 wxr_format = WXR_FORMAT_A8R8G8B8;
1105 break;
1107 default:
1108 ERR("aa = %d - not implemented\n", format);
1109 case AA_None:
1110 wxr_format = WXR_FORMAT_MONO;
1111 break;
1114 wine_tsx11_lock();
1115 formatEntry->font_format = get_xrender_format(wxr_format);
1116 formatEntry->glyphset = pXRenderCreateGlyphSet(gdi_display, formatEntry->font_format->pict_format);
1117 wine_tsx11_unlock();
1121 buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buflen);
1122 GetGlyphOutlineW(physDev->hdc, glyph, ggo_format, &gm, buflen, buf, &identity);
1123 formatEntry->realized[glyph] = TRUE;
1125 TRACE("buflen = %d. Got metrics: %dx%d adv=%d,%d origin=%d,%d\n",
1126 buflen,
1127 gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY,
1128 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1130 gi.width = gm.gmBlackBoxX;
1131 gi.height = gm.gmBlackBoxY;
1132 gi.x = -gm.gmptGlyphOrigin.x;
1133 gi.y = gm.gmptGlyphOrigin.y;
1134 gi.xOff = gm.gmCellIncX;
1135 gi.yOff = gm.gmCellIncY;
1137 if(TRACE_ON(xrender)) {
1138 int pitch, i, j;
1139 char output[300];
1140 unsigned char *line;
1142 if(format == AA_None) {
1143 pitch = ((gi.width + 31) / 32) * 4;
1144 for(i = 0; i < gi.height; i++) {
1145 line = (unsigned char*) buf + i * pitch;
1146 output[0] = '\0';
1147 for(j = 0; j < pitch * 8; j++) {
1148 strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
1150 TRACE("%s\n", output);
1152 } else {
1153 static const char blks[] = " .:;!o*#";
1154 char str[2];
1156 str[1] = '\0';
1157 pitch = ((gi.width + 3) / 4) * 4;
1158 for(i = 0; i < gi.height; i++) {
1159 line = (unsigned char*) buf + i * pitch;
1160 output[0] = '\0';
1161 for(j = 0; j < pitch; j++) {
1162 str[0] = blks[line[j] >> 5];
1163 strcat(output, str);
1165 TRACE("%s\n", output);
1171 if(formatEntry->glyphset) {
1172 if(format == AA_None && BitmapBitOrder(gdi_display) != MSBFirst) {
1173 unsigned char *byte = (unsigned char*) buf, c;
1174 int i = buflen;
1176 while(i--) {
1177 c = *byte;
1179 /* magic to flip bit order */
1180 c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
1181 c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
1182 c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
1184 *byte++ = c;
1187 else if ( format != AA_Grey &&
1188 ImageByteOrder (gdi_display) != NATIVE_BYTE_ORDER)
1190 unsigned int i, *data = (unsigned int *)buf;
1191 for (i = buflen / sizeof(int); i; i--, data++) *data = RtlUlongByteSwap(*data);
1193 gid = glyph;
1196 XRenderCompositeText seems to ignore 0x0 glyphs when
1197 AA_None, which means we lose the advance width of glyphs
1198 like the space. We'll pretend that such glyphs are 1x1
1199 bitmaps.
1202 if(buflen == 0)
1203 gi.width = gi.height = 1;
1205 wine_tsx11_lock();
1206 pXRenderAddGlyphs(gdi_display, formatEntry->glyphset, &gid, &gi, 1,
1207 buflen ? buf : zero, buflen ? buflen : sizeof(zero));
1208 wine_tsx11_unlock();
1209 HeapFree(GetProcessHeap(), 0, buf);
1210 } else {
1211 formatEntry->bitmaps[glyph] = buf;
1214 formatEntry->gis[glyph] = gi;
1216 return TRUE;
1219 static void SharpGlyphMono(X11DRV_PDEVICE *physDev, INT x, INT y,
1220 void *bitmap, XGlyphInfo *gi)
1222 unsigned char *srcLine = bitmap, *src;
1223 unsigned char bits, bitsMask;
1224 int width = gi->width;
1225 int stride = ((width + 31) & ~31) >> 3;
1226 int height = gi->height;
1227 int w;
1228 int xspan, lenspan;
1230 TRACE("%d, %d\n", x, y);
1231 x -= gi->x;
1232 y -= gi->y;
1233 while (height--)
1235 src = srcLine;
1236 srcLine += stride;
1237 w = width;
1239 bitsMask = 0x80; /* FreeType is always MSB first */
1240 bits = *src++;
1242 xspan = x;
1243 while (w)
1245 if (bits & bitsMask)
1247 lenspan = 0;
1250 lenspan++;
1251 if (lenspan == w)
1252 break;
1253 bitsMask = bitsMask >> 1;
1254 if (!bitsMask)
1256 bits = *src++;
1257 bitsMask = 0x80;
1259 } while (bits & bitsMask);
1260 XFillRectangle (gdi_display, physDev->drawable,
1261 physDev->gc, xspan, y, lenspan, 1);
1262 xspan += lenspan;
1263 w -= lenspan;
1265 else
1269 w--;
1270 xspan++;
1271 if (!w)
1272 break;
1273 bitsMask = bitsMask >> 1;
1274 if (!bitsMask)
1276 bits = *src++;
1277 bitsMask = 0x80;
1279 } while (!(bits & bitsMask));
1282 y++;
1286 static void SharpGlyphGray(X11DRV_PDEVICE *physDev, INT x, INT y,
1287 void *bitmap, XGlyphInfo *gi)
1289 unsigned char *srcLine = bitmap, *src, bits;
1290 int width = gi->width;
1291 int stride = ((width + 3) & ~3);
1292 int height = gi->height;
1293 int w;
1294 int xspan, lenspan;
1296 x -= gi->x;
1297 y -= gi->y;
1298 while (height--)
1300 src = srcLine;
1301 srcLine += stride;
1302 w = width;
1304 bits = *src++;
1305 xspan = x;
1306 while (w)
1308 if (bits >= 0x80)
1310 lenspan = 0;
1313 lenspan++;
1314 if (lenspan == w)
1315 break;
1316 bits = *src++;
1317 } while (bits >= 0x80);
1318 XFillRectangle (gdi_display, physDev->drawable,
1319 physDev->gc, xspan, y, lenspan, 1);
1320 xspan += lenspan;
1321 w -= lenspan;
1323 else
1327 w--;
1328 xspan++;
1329 if (!w)
1330 break;
1331 bits = *src++;
1332 } while (bits < 0x80);
1335 y++;
1340 static void ExamineBitfield (DWORD mask, int *shift, int *len)
1342 int s, l;
1344 s = 0;
1345 while ((mask & 1) == 0)
1347 mask >>= 1;
1348 s++;
1350 l = 0;
1351 while ((mask & 1) == 1)
1353 mask >>= 1;
1354 l++;
1356 *shift = s;
1357 *len = l;
1360 static DWORD GetField (DWORD pixel, int shift, int len)
1362 pixel = pixel & (((1 << (len)) - 1) << shift);
1363 pixel = pixel << (32 - (shift + len)) >> 24;
1364 while (len < 8)
1366 pixel |= (pixel >> len);
1367 len <<= 1;
1369 return pixel;
1373 static DWORD PutField (DWORD pixel, int shift, int len)
1375 shift = shift - (8 - len);
1376 if (len <= 8)
1377 pixel &= (((1 << len) - 1) << (8 - len));
1378 if (shift < 0)
1379 pixel >>= -shift;
1380 else
1381 pixel <<= shift;
1382 return pixel;
1385 static void SmoothGlyphGray(XImage *image, int x, int y, void *bitmap, XGlyphInfo *gi,
1386 int color)
1388 int r_shift, r_len;
1389 int g_shift, g_len;
1390 int b_shift, b_len;
1391 BYTE *maskLine, *mask, m;
1392 int maskStride;
1393 DWORD pixel;
1394 int width, height;
1395 int w, tx;
1396 BYTE src_r, src_g, src_b;
1398 x -= gi->x;
1399 y -= gi->y;
1400 width = gi->width;
1401 height = gi->height;
1403 maskLine = bitmap;
1404 maskStride = (width + 3) & ~3;
1406 ExamineBitfield (image->red_mask, &r_shift, &r_len);
1407 ExamineBitfield (image->green_mask, &g_shift, &g_len);
1408 ExamineBitfield (image->blue_mask, &b_shift, &b_len);
1410 src_r = GetField(color, r_shift, r_len);
1411 src_g = GetField(color, g_shift, g_len);
1412 src_b = GetField(color, b_shift, b_len);
1414 for(; height--; y++)
1416 mask = maskLine;
1417 maskLine += maskStride;
1418 w = width;
1419 tx = x;
1421 if(y < 0) continue;
1422 if(y >= image->height) break;
1424 for(; w--; tx++)
1426 if(tx >= image->width) break;
1428 m = *mask++;
1429 if(tx < 0) continue;
1431 if (m == 0xff)
1432 XPutPixel (image, tx, y, color);
1433 else if (m)
1435 BYTE r, g, b;
1437 pixel = XGetPixel (image, tx, y);
1439 r = GetField(pixel, r_shift, r_len);
1440 r = ((BYTE)~m * (WORD)r + (BYTE)m * (WORD)src_r) >> 8;
1441 g = GetField(pixel, g_shift, g_len);
1442 g = ((BYTE)~m * (WORD)g + (BYTE)m * (WORD)src_g) >> 8;
1443 b = GetField(pixel, b_shift, b_len);
1444 b = ((BYTE)~m * (WORD)b + (BYTE)m * (WORD)src_b) >> 8;
1446 pixel = (PutField (r, r_shift, r_len) |
1447 PutField (g, g_shift, g_len) |
1448 PutField (b, b_shift, b_len));
1449 XPutPixel (image, tx, y, pixel);
1455 /*************************************************************
1456 * get_tile_pict
1458 * Returns an appropriate Picture for tiling the text colour.
1459 * Call and use result within the xrender_cs
1461 static Picture get_tile_pict(const WineXRenderFormat *wxr_format, int text_pixel)
1463 static struct
1465 Pixmap xpm;
1466 Picture pict;
1467 int current_color;
1468 } tiles[MAX_FORMATS], *tile;
1469 XRenderColor col;
1471 tile = &tiles[wxr_format->format];
1473 if(!tile->xpm)
1475 XRenderPictureAttributes pa;
1477 wine_tsx11_lock();
1478 tile->xpm = XCreatePixmap(gdi_display, root_window, 1, 1, wxr_format->pict_format->depth);
1480 pa.repeat = RepeatNormal;
1481 tile->pict = pXRenderCreatePicture(gdi_display, tile->xpm, wxr_format->pict_format, CPRepeat, &pa);
1482 wine_tsx11_unlock();
1484 /* init current_color to something different from text_pixel */
1485 tile->current_color = ~text_pixel;
1487 if(wxr_format->format == WXR_FORMAT_MONO)
1489 /* for a 1bpp bitmap we always need a 1 in the tile */
1490 col.red = col.green = col.blue = 0;
1491 col.alpha = 0xffff;
1492 wine_tsx11_lock();
1493 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1494 wine_tsx11_unlock();
1498 if(text_pixel != tile->current_color && wxr_format->format != WXR_FORMAT_MONO)
1500 get_xrender_color(wxr_format, text_pixel, &col);
1501 wine_tsx11_lock();
1502 pXRenderFillRectangle(gdi_display, PictOpSrc, tile->pict, &col, 0, 0, 1, 1);
1503 wine_tsx11_unlock();
1504 tile->current_color = text_pixel;
1506 return tile->pict;
1509 static int XRenderErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
1511 return 1;
1514 /***********************************************************************
1515 * X11DRV_XRender_ExtTextOut
1517 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
1518 const RECT *lprect, LPCWSTR wstr, UINT count,
1519 const INT *lpDx )
1521 XGCValues xgcval;
1522 gsCacheEntry *entry;
1523 gsCacheEntryFormat *formatEntry;
1524 BOOL retv = FALSE;
1525 int textPixel, backgroundPixel;
1526 HRGN saved_region = 0;
1527 BOOL disable_antialias = FALSE;
1528 AA_Type aa_type = AA_None;
1529 DIBSECTION bmp;
1530 unsigned int idx;
1531 double cosEsc, sinEsc;
1532 LOGFONTW lf;
1533 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
1534 Picture tile_pict = 0;
1536 /* Do we need to disable antialiasing because of palette mode? */
1537 if( !physDev->bitmap || GetObjectW( physDev->bitmap->hbitmap, sizeof(bmp), &bmp ) != sizeof(bmp) ) {
1538 TRACE("bitmap is not a DIB\n");
1540 else if (bmp.dsBmih.biBitCount <= 8) {
1541 TRACE("Disabling antialiasing\n");
1542 disable_antialias = TRUE;
1545 xgcval.function = GXcopy;
1546 xgcval.background = physDev->backgroundPixel;
1547 xgcval.fill_style = FillSolid;
1548 wine_tsx11_lock();
1549 XChangeGC( gdi_display, physDev->gc, GCFunction | GCBackground | GCFillStyle, &xgcval );
1550 wine_tsx11_unlock();
1552 X11DRV_LockDIBSection( physDev, DIB_Status_GdiMod );
1554 if(physDev->depth == 1) {
1555 if((physDev->textPixel & 0xffffff) == 0) {
1556 textPixel = 0;
1557 backgroundPixel = 1;
1558 } else {
1559 textPixel = 1;
1560 backgroundPixel = 0;
1562 } else {
1563 textPixel = physDev->textPixel;
1564 backgroundPixel = physDev->backgroundPixel;
1567 if(flags & ETO_OPAQUE)
1569 wine_tsx11_lock();
1570 XSetForeground( gdi_display, physDev->gc, backgroundPixel );
1571 XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1572 physDev->dc_rect.left + lprect->left, physDev->dc_rect.top + lprect->top,
1573 lprect->right - lprect->left, lprect->bottom - lprect->top );
1574 wine_tsx11_unlock();
1577 if(count == 0)
1579 retv = TRUE;
1580 goto done_unlock;
1584 GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
1585 if(lf.lfEscapement != 0) {
1586 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1587 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1588 } else {
1589 cosEsc = 1;
1590 sinEsc = 0;
1593 if (flags & ETO_CLIPPED)
1595 HRGN clip_region;
1597 clip_region = CreateRectRgnIndirect( lprect );
1598 /* make a copy of the current device region */
1599 saved_region = CreateRectRgn( 0, 0, 0, 0 );
1600 CombineRgn( saved_region, physDev->region, 0, RGN_COPY );
1601 X11DRV_SetDeviceClipping( physDev, saved_region, clip_region );
1602 DeleteObject( clip_region );
1605 EnterCriticalSection(&xrender_cs);
1607 entry = glyphsetCache + physDev->xrender->cache_index;
1608 if( disable_antialias == FALSE )
1609 aa_type = entry->aa_default;
1610 formatEntry = entry->format[aa_type];
1612 for(idx = 0; idx < count; idx++) {
1613 if( !formatEntry ) {
1614 UploadGlyph(physDev, wstr[idx], aa_type);
1615 /* re-evaluate antialias since aa_default may have changed */
1616 if( disable_antialias == FALSE )
1617 aa_type = entry->aa_default;
1618 formatEntry = entry->format[aa_type];
1619 } else if( wstr[idx] >= formatEntry->nrealized || formatEntry->realized[wstr[idx]] == FALSE) {
1620 UploadGlyph(physDev, wstr[idx], aa_type);
1623 if (!formatEntry)
1625 WARN("could not upload requested glyphs\n");
1626 LeaveCriticalSection(&xrender_cs);
1627 goto done_unlock;
1630 TRACE("Writing %s at %d,%d\n", debugstr_wn(wstr,count),
1631 physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1633 if(X11DRV_XRender_Installed)
1635 XGlyphElt16 *elts = HeapAlloc(GetProcessHeap(), 0, sizeof(XGlyphElt16) * count);
1636 INT offset = 0;
1637 POINT desired, current;
1638 int render_op = PictOpOver;
1639 Picture pict = get_xrender_picture(physDev);
1641 /* There's a bug in XRenderCompositeText that ignores the xDst and yDst parameters.
1642 So we pass zeros to the function and move to our starting position using the first
1643 element of the elts array. */
1645 desired.x = physDev->dc_rect.left + x;
1646 desired.y = physDev->dc_rect.top + y;
1647 current.x = current.y = 0;
1649 tile_pict = get_tile_pict(dst_format, physDev->textPixel);
1651 /* FIXME the mapping of Text/BkColor onto 1 or 0 needs investigation.
1653 if((dst_format->format == WXR_FORMAT_MONO) && (textPixel == 0))
1654 render_op = PictOpOutReverse; /* This gives us 'black' text */
1656 for(idx = 0; idx < count; idx++)
1658 elts[idx].glyphset = formatEntry->glyphset;
1659 elts[idx].chars = wstr + idx;
1660 elts[idx].nchars = 1;
1661 elts[idx].xOff = desired.x - current.x;
1662 elts[idx].yOff = desired.y - current.y;
1664 current.x += (elts[idx].xOff + formatEntry->gis[wstr[idx]].xOff);
1665 current.y += (elts[idx].yOff + formatEntry->gis[wstr[idx]].yOff);
1667 if(!lpDx)
1669 desired.x += formatEntry->gis[wstr[idx]].xOff;
1670 desired.y += formatEntry->gis[wstr[idx]].yOff;
1672 else
1674 offset += lpDx[idx];
1675 desired.x = physDev->dc_rect.left + x + offset * cosEsc;
1676 desired.y = physDev->dc_rect.top + y - offset * sinEsc;
1679 wine_tsx11_lock();
1680 /* Make sure we don't have any transforms set from a previous call */
1681 set_xrender_transformation(pict, 1, 1, 0, 0);
1682 pXRenderCompositeText16(gdi_display, render_op,
1683 tile_pict,
1684 pict,
1685 formatEntry->font_format->pict_format,
1686 0, 0, 0, 0, elts, count);
1687 wine_tsx11_unlock();
1688 HeapFree(GetProcessHeap(), 0, elts);
1689 } else {
1690 INT offset = 0, xoff = 0, yoff = 0;
1691 wine_tsx11_lock();
1692 XSetForeground( gdi_display, physDev->gc, textPixel );
1694 if(aa_type == AA_None || physDev->depth == 1)
1696 void (* sharp_glyph_fn)(X11DRV_PDEVICE *, INT, INT, void *, XGlyphInfo *);
1698 if(aa_type == AA_None)
1699 sharp_glyph_fn = SharpGlyphMono;
1700 else
1701 sharp_glyph_fn = SharpGlyphGray;
1703 for(idx = 0; idx < count; idx++) {
1704 sharp_glyph_fn(physDev, physDev->dc_rect.left + x + xoff,
1705 physDev->dc_rect.top + y + yoff,
1706 formatEntry->bitmaps[wstr[idx]],
1707 &formatEntry->gis[wstr[idx]]);
1708 if(lpDx) {
1709 offset += lpDx[idx];
1710 xoff = offset * cosEsc;
1711 yoff = offset * -sinEsc;
1712 } else {
1713 xoff += formatEntry->gis[wstr[idx]].xOff;
1714 yoff += formatEntry->gis[wstr[idx]].yOff;
1717 } else {
1718 XImage *image;
1719 int image_x, image_y, image_off_x, image_off_y, image_w, image_h;
1720 RECT extents = {0, 0, 0, 0};
1721 POINT cur = {0, 0};
1722 int w = physDev->drawable_rect.right - physDev->drawable_rect.left;
1723 int h = physDev->drawable_rect.bottom - physDev->drawable_rect.top;
1725 TRACE("drawable %dx%d\n", w, h);
1727 for(idx = 0; idx < count; idx++) {
1728 if(extents.left > cur.x - formatEntry->gis[wstr[idx]].x)
1729 extents.left = cur.x - formatEntry->gis[wstr[idx]].x;
1730 if(extents.top > cur.y - formatEntry->gis[wstr[idx]].y)
1731 extents.top = cur.y - formatEntry->gis[wstr[idx]].y;
1732 if(extents.right < cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width)
1733 extents.right = cur.x - formatEntry->gis[wstr[idx]].x + formatEntry->gis[wstr[idx]].width;
1734 if(extents.bottom < cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height)
1735 extents.bottom = cur.y - formatEntry->gis[wstr[idx]].y + formatEntry->gis[wstr[idx]].height;
1736 if(lpDx) {
1737 offset += lpDx[idx];
1738 cur.x = offset * cosEsc;
1739 cur.y = offset * -sinEsc;
1740 } else {
1741 cur.x += formatEntry->gis[wstr[idx]].xOff;
1742 cur.y += formatEntry->gis[wstr[idx]].yOff;
1745 TRACE("glyph extents %d,%d - %d,%d drawable x,y %d,%d\n", extents.left, extents.top,
1746 extents.right, extents.bottom, physDev->dc_rect.left + x, physDev->dc_rect.top + y);
1748 if(physDev->dc_rect.left + x + extents.left >= 0) {
1749 image_x = physDev->dc_rect.left + x + extents.left;
1750 image_off_x = 0;
1751 } else {
1752 image_x = 0;
1753 image_off_x = physDev->dc_rect.left + x + extents.left;
1755 if(physDev->dc_rect.top + y + extents.top >= 0) {
1756 image_y = physDev->dc_rect.top + y + extents.top;
1757 image_off_y = 0;
1758 } else {
1759 image_y = 0;
1760 image_off_y = physDev->dc_rect.top + y + extents.top;
1762 if(physDev->dc_rect.left + x + extents.right < w)
1763 image_w = physDev->dc_rect.left + x + extents.right - image_x;
1764 else
1765 image_w = w - image_x;
1766 if(physDev->dc_rect.top + y + extents.bottom < h)
1767 image_h = physDev->dc_rect.top + y + extents.bottom - image_y;
1768 else
1769 image_h = h - image_y;
1771 if(image_w <= 0 || image_h <= 0) goto no_image;
1773 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1774 image = XGetImage(gdi_display, physDev->drawable,
1775 image_x, image_y, image_w, image_h,
1776 AllPlanes, ZPixmap);
1777 X11DRV_check_error();
1779 TRACE("XGetImage(%p, %x, %d, %d, %d, %d, %lx, %x) depth = %d rets %p\n",
1780 gdi_display, (int)physDev->drawable, image_x, image_y,
1781 image_w, image_h, AllPlanes, ZPixmap,
1782 physDev->depth, image);
1783 if(!image) {
1784 Pixmap xpm = XCreatePixmap(gdi_display, root_window, image_w, image_h,
1785 physDev->depth);
1786 GC gc;
1787 XGCValues gcv;
1789 gcv.graphics_exposures = False;
1790 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
1791 XCopyArea(gdi_display, physDev->drawable, xpm, gc, image_x, image_y,
1792 image_w, image_h, 0, 0);
1793 XFreeGC(gdi_display, gc);
1794 X11DRV_expect_error(gdi_display, XRenderErrorHandler, NULL);
1795 image = XGetImage(gdi_display, xpm, 0, 0, image_w, image_h, AllPlanes,
1796 ZPixmap);
1797 X11DRV_check_error();
1798 XFreePixmap(gdi_display, xpm);
1800 if(!image) goto no_image;
1802 image->red_mask = visual->red_mask;
1803 image->green_mask = visual->green_mask;
1804 image->blue_mask = visual->blue_mask;
1806 offset = xoff = yoff = 0;
1807 for(idx = 0; idx < count; idx++) {
1808 SmoothGlyphGray(image, xoff + image_off_x - extents.left,
1809 yoff + image_off_y - extents.top,
1810 formatEntry->bitmaps[wstr[idx]],
1811 &formatEntry->gis[wstr[idx]],
1812 physDev->textPixel);
1813 if(lpDx) {
1814 offset += lpDx[idx];
1815 xoff = offset * cosEsc;
1816 yoff = offset * -sinEsc;
1817 } else {
1818 xoff += formatEntry->gis[wstr[idx]].xOff;
1819 yoff += formatEntry->gis[wstr[idx]].yOff;
1822 XPutImage(gdi_display, physDev->drawable, physDev->gc, image, 0, 0,
1823 image_x, image_y, image_w, image_h);
1824 XDestroyImage(image);
1826 no_image:
1827 wine_tsx11_unlock();
1829 LeaveCriticalSection(&xrender_cs);
1831 if (flags & ETO_CLIPPED)
1833 /* restore the device region */
1834 X11DRV_SetDeviceClipping( physDev, saved_region, 0 );
1835 DeleteObject( saved_region );
1838 retv = TRUE;
1840 done_unlock:
1841 X11DRV_UnlockDIBSection( physDev, TRUE );
1842 return retv;
1845 /* Helper function for (stretched) blitting using xrender */
1846 static void xrender_blit(Picture src_pict, Picture mask_pict, Picture dst_pict, int x_src, int y_src, float xscale, float yscale, int width, int height)
1848 /* Further down a transformation matrix is used for stretching and mirroring the source data.
1849 * xscale/yscale contain the scaling factors for the width and height. In case of mirroring
1850 * we also need a x- and y-offset because without the pixels will be in the wrong quadrant of the x-y plane.
1852 int x_offset = (xscale<0) ? width : 0;
1853 int y_offset = (yscale<0) ? height : 0;
1855 /* When we need to scale we perform scaling and source_x / source_y translation using a transformation matrix.
1856 * This is needed because XRender is inaccurate in combination with scaled source coordinates passed to XRenderComposite.
1857 * In all other cases we do use XRenderComposite for translation as it is faster than using a transformation matrix. */
1858 if(xscale != 1.0 || yscale != 1.0)
1860 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1861 if(mask_pict)
1862 set_xrender_transformation(mask_pict, xscale, yscale, x_offset, y_offset);
1863 else
1864 set_xrender_transformation(src_pict, xscale, yscale, x_src + x_offset, y_src + y_offset);
1866 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, 0, 0, 0, 0, 0, 0, width, height);
1868 else
1870 /* When we are using a mask, 'src_pict' contains a 1x1 picture for tiling, the actual source data is in mask_pict */
1871 if(mask_pict)
1872 set_xrender_transformation(mask_pict, 1, 1, 0, 0);
1873 else
1874 set_xrender_transformation(src_pict, 1, 1, 0, 0);
1876 pXRenderComposite(gdi_display, PictOpSrc, src_pict, mask_pict, dst_pict, x_src, y_src, 0, 0, 0, 0, width, height);
1880 /******************************************************************************
1881 * AlphaBlend (x11drv.@)
1883 BOOL CDECL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
1884 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
1885 BLENDFUNCTION blendfn)
1887 XRenderPictureAttributes pa;
1888 Picture dst_pict, src_pict;
1889 Pixmap xpm;
1890 DIBSECTION dib;
1891 XImage *image;
1892 GC gc;
1893 XGCValues gcv;
1894 DWORD *dstbits, *data;
1895 int y, y2;
1896 POINT pts[2];
1897 BOOL top_down = FALSE;
1898 const WineXRenderFormat *src_format;
1899 int repeat_src;
1901 if(!X11DRV_XRender_Installed) {
1902 FIXME("Unable to AlphaBlend without Xrender\n");
1903 return FALSE;
1905 pts[0].x = xDst;
1906 pts[0].y = yDst;
1907 pts[1].x = xDst + widthDst;
1908 pts[1].y = yDst + heightDst;
1909 LPtoDP(devDst->hdc, pts, 2);
1910 xDst = pts[0].x;
1911 yDst = pts[0].y;
1912 widthDst = pts[1].x - pts[0].x;
1913 heightDst = pts[1].y - pts[0].y;
1915 pts[0].x = xSrc;
1916 pts[0].y = ySrc;
1917 pts[1].x = xSrc + widthSrc;
1918 pts[1].y = ySrc + heightSrc;
1919 LPtoDP(devSrc->hdc, pts, 2);
1920 xSrc = pts[0].x;
1921 ySrc = pts[0].y;
1922 widthSrc = pts[1].x - pts[0].x;
1923 heightSrc = pts[1].y - pts[0].y;
1924 if (!widthDst || !heightDst || !widthSrc || !heightSrc) return TRUE;
1926 #ifndef HAVE_XRENDERSETPICTURETRANSFORM
1927 if(widthDst != widthSrc || heightDst != heightSrc)
1928 #else
1929 if(!pXRenderSetPictureTransform)
1930 #endif
1932 FIXME("Unable to Stretch, XRenderSetPictureTransform is currently required\n");
1933 return FALSE;
1936 if (!devSrc->bitmap || GetObjectW( devSrc->bitmap->hbitmap, sizeof(dib), &dib ) != sizeof(dib))
1938 static BOOL out = FALSE;
1939 if (!out)
1941 FIXME("not a dibsection\n");
1942 out = TRUE;
1944 return FALSE;
1947 /* If the source is a 1x1 bitmap, tiling is equivalent to stretching, but
1948 tiling is much faster. Therefore, we do no stretching in this case. */
1949 repeat_src = dib.dsBmih.biWidth == 1 && abs(dib.dsBmih.biHeight) == 1;
1951 if (xSrc < 0 || ySrc < 0 || widthSrc < 0 || heightSrc < 0 || xSrc + widthSrc > dib.dsBmih.biWidth
1952 || ySrc + heightSrc > abs(dib.dsBmih.biHeight))
1954 WARN("Invalid src coords: (%d,%d), size %dx%d\n", xSrc, ySrc, widthSrc, heightSrc);
1955 SetLastError(ERROR_INVALID_PARAMETER);
1956 return FALSE;
1959 if(dib.dsBm.bmBitsPixel != 32) {
1960 FIXME("not a 32 bpp dibsection\n");
1961 return FALSE;
1963 dstbits = data = HeapAlloc(GetProcessHeap(), 0, heightSrc * widthSrc * 4);
1965 if(dib.dsBmih.biHeight < 0) { /* top-down dib */
1966 top_down = TRUE;
1967 dstbits += widthSrc * (heightSrc - 1);
1968 y2 = ySrc;
1969 y = y2 + heightSrc - 1;
1971 else
1973 y = dib.dsBmih.biHeight - ySrc - 1;
1974 y2 = y - heightSrc + 1;
1977 if (blendfn.AlphaFormat & AC_SRC_ALPHA)
1979 if (blendfn.SourceConstantAlpha == 0xff)
1981 for (; y >= y2; y--)
1983 memcpy(dstbits, (char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes + xSrc * 4,
1984 widthSrc * 4);
1985 dstbits += (top_down ? -1 : 1) * widthSrc;
1988 else
1990 /* SourceConstantAlpha combined with source alpha */
1991 for (; y >= y2; y--)
1993 int x;
1994 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
1995 for (x = 0; x < widthSrc; x++)
1997 DWORD argb = *srcbits++;
1998 BYTE *s = (BYTE *) &argb;
1999 s[0] = (s[0] * blendfn.SourceConstantAlpha) / 255;
2000 s[1] = (s[1] * blendfn.SourceConstantAlpha) / 255;
2001 s[2] = (s[2] * blendfn.SourceConstantAlpha) / 255;
2002 s[3] = (s[3] * blendfn.SourceConstantAlpha) / 255;
2003 *dstbits++ = argb;
2005 if (top_down) /* we traversed the row forward so we should go back by two rows */
2006 dstbits -= 2 * widthSrc;
2010 else
2012 DWORD source_alpha = (DWORD)blendfn.SourceConstantAlpha << 24;
2013 int x;
2015 for(; y >= y2; y--)
2017 DWORD *srcbits = (DWORD *)((char *)dib.dsBm.bmBits + y * dib.dsBm.bmWidthBytes) + xSrc;
2018 for (x = 0; x < widthSrc; x++)
2020 DWORD argb = *srcbits++;
2021 argb = (argb & 0xffffff) | source_alpha;
2022 *dstbits++ = argb;
2024 if (top_down) /* we traversed the row forward so we should go back by two rows */
2025 dstbits -= 2 * widthSrc;
2030 dst_pict = get_xrender_picture(devDst);
2032 wine_tsx11_lock();
2033 src_format = get_xrender_format(WXR_FORMAT_A8R8G8B8);
2034 TRACE("src_format %p\n", src_format);
2035 if(!src_format)
2037 WARN("Unable to find a picture format supporting alpha, make sure X is running at 24-bit\n");
2038 wine_tsx11_unlock();
2039 HeapFree(GetProcessHeap(), 0, data);
2040 return FALSE;
2043 image = XCreateImage(gdi_display, visual, 32, ZPixmap, 0,
2044 (char*) data, widthSrc, heightSrc, 32, widthSrc * 4);
2046 TRACE("src_drawable = %08lx\n", devSrc->drawable);
2047 xpm = XCreatePixmap(gdi_display,
2048 root_window,
2049 widthSrc, heightSrc, 32);
2050 gcv.graphics_exposures = False;
2051 gc = XCreateGC(gdi_display, xpm, GCGraphicsExposures, &gcv);
2052 TRACE("xpm = %08lx\n", xpm);
2053 XPutImage(gdi_display, xpm, gc, image, 0, 0, 0, 0, widthSrc, heightSrc);
2055 pa.subwindow_mode = IncludeInferiors;
2056 pa.repeat = repeat_src ? RepeatNormal : RepeatNone;
2057 src_pict = pXRenderCreatePicture(gdi_display,
2058 xpm, src_format->pict_format,
2059 CPSubwindowMode|CPRepeat, &pa);
2060 TRACE("src_pict %08lx\n", src_pict);
2062 /* Make sure we ALWAYS set the transformation matrix even if we don't need to scale. The reason is
2063 * that later on we want to reuse pictures (it can bring a lot of extra performance) and each time
2064 * a different transformation matrix might have been used. */
2065 if (repeat_src)
2066 set_xrender_transformation(src_pict, 1.0, 1.0, 0, 0);
2067 else
2068 set_xrender_transformation(src_pict, widthSrc/(double)widthDst, heightSrc/(double)heightDst, 0, 0);
2069 pXRenderComposite(gdi_display, PictOpOver, src_pict, 0, dst_pict,
2070 0, 0, 0, 0,
2071 xDst + devDst->dc_rect.left, yDst + devDst->dc_rect.top, widthDst, heightDst);
2074 pXRenderFreePicture(gdi_display, src_pict);
2075 XFreePixmap(gdi_display, xpm);
2076 XFreeGC(gdi_display, gc);
2077 image->data = NULL;
2078 XDestroyImage(image);
2080 wine_tsx11_unlock();
2081 HeapFree(GetProcessHeap(), 0, data);
2082 return TRUE;
2085 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2087 /* 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 */
2088 int depth = physBitmap->pixmap_depth == 1 ? 1 : physDev->depth;
2089 const WineXRenderFormat *src_format = get_xrender_format_from_color_shifts(physBitmap->pixmap_depth, &physBitmap->pixmap_color_shifts);
2090 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDev->depth, physDev->color_shifts);
2092 wine_tsx11_lock();
2093 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, depth);
2095 /* Use XCopyArea when the physBitmap and brush.pixmap have the same format. */
2096 if( (physBitmap->pixmap_depth == 1) || (!X11DRV_XRender_Installed && physDev->depth == physBitmap->pixmap_depth) ||
2097 (src_format->format == dst_format->format) )
2099 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2100 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2102 else /* We need depth conversion */
2104 Picture src_pict, dst_pict;
2105 XRenderPictureAttributes pa;
2106 pa.subwindow_mode = IncludeInferiors;
2107 pa.repeat = RepeatNone;
2109 src_pict = pXRenderCreatePicture(gdi_display, physBitmap->pixmap, src_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2110 dst_pict = pXRenderCreatePicture(gdi_display, physDev->brush.pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2112 xrender_blit(src_pict, 0, dst_pict, 0, 0, 1.0, 1.0, width, height);
2113 pXRenderFreePicture(gdi_display, src_pict);
2114 pXRenderFreePicture(gdi_display, dst_pict);
2116 wine_tsx11_unlock();
2119 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2120 Pixmap pixmap, GC gc,
2121 INT widthSrc, INT heightSrc,
2122 INT widthDst, INT heightDst,
2123 RECT *visRectSrc, RECT *visRectDst )
2125 BOOL stretch = (widthSrc != widthDst) || (heightSrc != heightDst);
2126 int width = visRectDst->right - visRectDst->left;
2127 int height = visRectDst->bottom - visRectDst->top;
2128 int x_src = physDevSrc->dc_rect.left + visRectSrc->left;
2129 int y_src = physDevSrc->dc_rect.top + visRectSrc->top;
2130 struct xrender_info *src_info = get_xrender_info(physDevSrc);
2131 const WineXRenderFormat *dst_format = get_xrender_format_from_color_shifts(physDevDst->depth, physDevDst->color_shifts);
2132 Picture src_pict=0, dst_pict=0, mask_pict=0;
2134 double xscale = widthSrc/(double)widthDst;
2135 double yscale = heightSrc/(double)heightDst;
2137 XRenderPictureAttributes pa;
2138 pa.subwindow_mode = IncludeInferiors;
2139 pa.repeat = RepeatNone;
2141 TRACE("src depth=%d widthSrc=%d heightSrc=%d xSrc=%d ySrc=%d\n", physDevSrc->depth, widthSrc, heightSrc, x_src, y_src);
2142 TRACE("dst depth=%d widthDst=%d heightDst=%d\n", physDevDst->depth, widthDst, heightDst);
2144 if(!X11DRV_XRender_Installed)
2146 TRACE("Not using XRender since it is not available or disabled\n");
2147 return FALSE;
2150 /* XRender can't handle palettes, so abort */
2151 if(X11DRV_PALETTE_XPixelToPalette)
2152 return FALSE;
2154 /* XRender is of no use in this case */
2155 if((physDevDst->depth == 1) && (physDevSrc->depth > 1))
2156 return FALSE;
2158 /* Just use traditional X copy when the formats match and we don't need stretching */
2159 if((src_info->format->format == dst_format->format) && !stretch)
2161 TRACE("Source and destination depth match and no stretching needed falling back to XCopyArea\n");
2162 wine_tsx11_lock();
2163 XCopyArea( gdi_display, physDevSrc->drawable, pixmap, gc, x_src, y_src, width, height, 0, 0);
2164 wine_tsx11_unlock();
2165 return TRUE;
2168 /* mono -> color */
2169 if(physDevSrc->depth == 1)
2171 XRenderColor col;
2172 get_xrender_color(dst_format, physDevDst->textPixel, &col);
2174 /* We use the source drawable as a mask */
2175 mask_pict = get_xrender_picture_source(physDevSrc);
2177 /* Use backgroundPixel as the foreground color */
2178 EnterCriticalSection( &xrender_cs );
2179 src_pict = get_tile_pict(dst_format, physDevDst->backgroundPixel);
2181 /* Create a destination picture and fill it with textPixel color as the background color */
2182 wine_tsx11_lock();
2183 dst_pict = pXRenderCreatePicture(gdi_display, pixmap, dst_format->pict_format, CPSubwindowMode|CPRepeat, &pa);
2184 pXRenderFillRectangle(gdi_display, PictOpSrc, dst_pict, &col, 0, 0, width, height);
2186 xrender_blit(src_pict, mask_pict, dst_pict, x_src, y_src, xscale, yscale, width, height);
2188 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2189 wine_tsx11_unlock();
2190 LeaveCriticalSection( &xrender_cs );
2192 else /* color -> color but with different depths */
2194 src_pict = get_xrender_picture_source(physDevSrc);
2196 wine_tsx11_lock();
2197 dst_pict = pXRenderCreatePicture(gdi_display,
2198 pixmap, dst_format->pict_format,
2199 CPSubwindowMode|CPRepeat, &pa);
2201 xrender_blit(src_pict, 0, dst_pict, x_src, y_src, xscale, yscale, width, height);
2203 if(dst_pict) pXRenderFreePicture(gdi_display, dst_pict);
2204 wine_tsx11_unlock();
2206 return TRUE;
2209 #else /* SONAME_LIBXRENDER */
2211 void X11DRV_XRender_Init(void)
2213 TRACE("XRender support not compiled in.\n");
2214 return;
2217 void X11DRV_XRender_Finalize(void)
2221 BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE *physDev, HFONT hfont)
2223 assert(0);
2224 return FALSE;
2227 void X11DRV_XRender_DeleteDC(X11DRV_PDEVICE *physDev)
2229 assert(0);
2230 return;
2233 void X11DRV_XRender_SetDeviceClipping(X11DRV_PDEVICE *physDev, const RGNDATA *data)
2235 assert(0);
2236 return;
2239 BOOL X11DRV_XRender_ExtTextOut( X11DRV_PDEVICE *physDev, INT x, INT y, UINT flags,
2240 const RECT *lprect, LPCWSTR wstr, UINT count,
2241 const INT *lpDx )
2243 assert(0);
2244 return FALSE;
2247 void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev)
2249 assert(0);
2250 return;
2253 /******************************************************************************
2254 * AlphaBlend (x11drv.@)
2256 BOOL X11DRV_AlphaBlend(X11DRV_PDEVICE *devDst, INT xDst, INT yDst, INT widthDst, INT heightDst,
2257 X11DRV_PDEVICE *devSrc, INT xSrc, INT ySrc, INT widthSrc, INT heightSrc,
2258 BLENDFUNCTION blendfn)
2260 FIXME("not supported - XRENDER headers were missing at compile time\n");
2261 return FALSE;
2264 void X11DRV_XRender_CopyBrush(X11DRV_PDEVICE *physDev, X_PHYSBITMAP *physBitmap, int width, int height)
2266 wine_tsx11_lock();
2267 physDev->brush.pixmap = XCreatePixmap(gdi_display, root_window, width, height, physBitmap->pixmap_depth);
2269 XCopyArea( gdi_display, physBitmap->pixmap, physDev->brush.pixmap,
2270 get_bitmap_gc(physBitmap->pixmap_depth), 0, 0, width, height, 0, 0 );
2271 wine_tsx11_unlock();
2274 BOOL X11DRV_XRender_SetPhysBitmapDepth(X_PHYSBITMAP *physBitmap, const DIBSECTION *dib)
2276 return FALSE;
2279 BOOL X11DRV_XRender_GetSrcAreaStretch(X11DRV_PDEVICE *physDevSrc, X11DRV_PDEVICE *physDevDst,
2280 Pixmap pixmap, GC gc,
2281 INT widthSrc, INT heightSrc,
2282 INT widthDst, INT heightDst,
2283 RECT *visRectSrc, RECT *visRectDst )
2285 return FALSE;
2287 #endif /* SONAME_LIBXRENDER */